mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
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:
33
test/smoke/src/areas/activitybar/activityBar.ts
Normal file
33
test/smoke/src/areas/activitybar/activityBar.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Element } from 'webdriverio';
|
||||
import { SpectronApplication } from '../../spectron/application';
|
||||
|
||||
export enum ActivityBarPosition {
|
||||
LEFT = 0,
|
||||
RIGHT = 1
|
||||
};
|
||||
|
||||
export class ActivityBar {
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public async getActivityBar(position: ActivityBarPosition): Promise<Element> {
|
||||
let positionClass: string;
|
||||
|
||||
if (position === ActivityBarPosition.LEFT) {
|
||||
positionClass = 'left';
|
||||
} else if (position === ActivityBarPosition.RIGHT) {
|
||||
positionClass = 'right';
|
||||
} else {
|
||||
throw new Error('No such position for activity bar defined.');
|
||||
}
|
||||
|
||||
return this.spectron.client.waitForElement(`.part.activitybar.${positionClass}`);
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Util } from '../helpers/utilities';
|
||||
|
||||
/**
|
||||
* Contains methods that are commonly used across test areas.
|
||||
*/
|
||||
export class CommonActions {
|
||||
private util: Util;
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
this.util = new Util();
|
||||
}
|
||||
|
||||
public async getWindowTitle(): Promise<any> {
|
||||
return this.spectron.client.getTitle();
|
||||
}
|
||||
|
||||
public enter(): Promise<any> {
|
||||
return this.spectron.client.keys(['Enter', 'NULL']);
|
||||
}
|
||||
|
||||
public async addSetting(setting: string, value: string): Promise<any> {
|
||||
await this.spectron.command('workbench.action.openGlobalSettings');
|
||||
await this.spectron.wait();
|
||||
await this.spectron.client.keys(['ArrowDown', 'NULL', 'ArrowRight', 'NULL'], false);
|
||||
await this.spectron.client.keys(`"${setting}": "${value}"`);
|
||||
await this.spectron.wait();
|
||||
return this.saveOpenedFile();
|
||||
}
|
||||
|
||||
public async newUntitledFile(): Promise<any> {
|
||||
await this.spectron.command('workbench.action.files.newUntitledFile');
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public closeTab(): Promise<any> {
|
||||
return this.spectron.client.keys(['Control', 'w', 'NULL']);
|
||||
}
|
||||
|
||||
public async getTab(tabName: string, active?: boolean): Promise<any> {
|
||||
await this.closeCurrentNotification(); // close any notification messages that could overlap tabs
|
||||
|
||||
let tabSelector = active ? '.tab.active' : 'div';
|
||||
let el = await this.spectron.client.element(`.tabs-container ${tabSelector}[aria-label="${tabName}, tab"]`);
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async selectTab(tabName: string): Promise<any> {
|
||||
await this.closeCurrentNotification(); // close any notification messages that could overlap tabs
|
||||
return this.spectron.client.click(`.tabs-container div[aria-label="${tabName}, tab"]`);
|
||||
}
|
||||
|
||||
public async openFirstMatchFile(fileName: string): Promise<any> {
|
||||
await this.openQuickOpen();
|
||||
await this.type(fileName);
|
||||
await this.spectron.wait();
|
||||
await this.enter();
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public saveOpenedFile(): Promise<any> {
|
||||
return this.spectron.command('workbench.action.files.save');
|
||||
}
|
||||
|
||||
public type(text: string): Promise<any> {
|
||||
let spectron = this.spectron;
|
||||
|
||||
return new Promise(function (res) {
|
||||
let textSplit = text.split(' ');
|
||||
|
||||
async function type(i: number) {
|
||||
if (!textSplit[i] || textSplit[i].length <= 0) {
|
||||
return res();
|
||||
}
|
||||
|
||||
const toType = textSplit[i + 1] ? `${textSplit[i]} ` : textSplit[i];
|
||||
await spectron.client.keys(toType, false);
|
||||
await spectron.client.keys(['NULL']);
|
||||
await type(i + 1);
|
||||
}
|
||||
|
||||
return type(0);
|
||||
});
|
||||
}
|
||||
|
||||
public showCommands(): Promise<any> {
|
||||
return this.spectron.command('workbench.action.showCommands');
|
||||
}
|
||||
|
||||
public openQuickOpen(): Promise<any> {
|
||||
return this.spectron.command('workbench.action.quickOpen');
|
||||
}
|
||||
|
||||
public closeQuickOpen(): Promise<any> {
|
||||
return this.spectron.command('workbench.action.closeQuickOpen');
|
||||
}
|
||||
|
||||
public selectNextQuickOpenElement(): Promise<any> {
|
||||
return this.spectron.client.keys(['ArrowDown', 'NULL']);
|
||||
}
|
||||
|
||||
public async getQuickOpenElements(): Promise<number> {
|
||||
const elements = await this.spectron.waitFor(this.spectron.client.elements, 'div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties .monaco-tree-row');
|
||||
return elements.value.length;
|
||||
}
|
||||
|
||||
public async openFile(fileName: string, explorer?: boolean): Promise<any> {
|
||||
let selector = `div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.getExtensionSelector(fileName)}`;
|
||||
if (explorer) {
|
||||
selector += ' explorer-item';
|
||||
}
|
||||
selector += '"]';
|
||||
|
||||
try {
|
||||
await this.spectron.waitFor(this.spectron.client.doubleClick, selector);
|
||||
} catch (e) {
|
||||
return Promise.reject(`Cannot fine ${fileName} in a viewlet.`);
|
||||
}
|
||||
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public getExtensionSelector(fileName: string): string {
|
||||
const extension = fileName.split('.')[1];
|
||||
if (extension === 'js') {
|
||||
return 'js-ext-file-icon javascript-lang-file-icon';
|
||||
} else if (extension === 'json') {
|
||||
return 'json-ext-file-icon json-lang-file-icon';
|
||||
} else if (extension === 'md') {
|
||||
return 'md-ext-file-icon markdown-lang-file-icon';
|
||||
}
|
||||
|
||||
throw new Error('No class defined for this file extension');
|
||||
}
|
||||
|
||||
public async getEditorFirstLinePlainText(): Promise<any> {
|
||||
const trials = 3;
|
||||
let retry = 0;
|
||||
let error;
|
||||
|
||||
while (retry < trials) {
|
||||
try {
|
||||
const span = await this.spectron.client.getText('.view-lines span span');
|
||||
if (Array.isArray(span)) {
|
||||
return span[0];
|
||||
}
|
||||
|
||||
return span;
|
||||
} catch (e) {
|
||||
error = e;
|
||||
retry++;
|
||||
|
||||
if (retry < trials) {
|
||||
await this.spectron.wait();
|
||||
} else {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject('Could not obtain text on the first line of an editor: ' + error);
|
||||
}
|
||||
|
||||
public removeFile(filePath: string): void {
|
||||
this.util.removeFile(filePath);
|
||||
}
|
||||
|
||||
public removeDirectory(directory: string): Promise<any> {
|
||||
try {
|
||||
return this.util.rimraf(directory);
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to remove ${directory} with an error: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
private closeCurrentNotification(): Promise<any> {
|
||||
return this.spectron.command('workbench.action.closeMessages');
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export enum ActivityBarPosition {
|
||||
LEFT = 0,
|
||||
RIGHT = 1
|
||||
};
|
||||
|
||||
export class ConfigurationView {
|
||||
// Stores key binding defined for the toggle of activity bar position
|
||||
private keybinding: string[];
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public async getEditorLineNumbers(): Promise<any> {
|
||||
const lineNumbers = await this.spectron.client.elements('.line-numbers');
|
||||
|
||||
return lineNumbers.value.length;
|
||||
}
|
||||
|
||||
public enterKeybindingsView(): any {
|
||||
return this.spectron.command('workbench.action.openGlobalKeybindings');
|
||||
}
|
||||
|
||||
public selectFirstKeybindingsMatch(): any {
|
||||
return this.spectron.waitFor(this.spectron.client.click, 'div[aria-label="Keybindings"] .monaco-list-row.keybinding-item');
|
||||
}
|
||||
|
||||
public changeKeybinding(): any {
|
||||
return this.spectron.command('editor.action.defineKeybinding');
|
||||
}
|
||||
|
||||
public enterBinding(keys: string[]): any {
|
||||
this.keybinding = keys;
|
||||
return this.spectron.client.keys(keys);
|
||||
}
|
||||
|
||||
public toggleActivityBarPosition(): any {
|
||||
return this.spectron.client.keys(this.keybinding);
|
||||
}
|
||||
|
||||
public async getActivityBar(position: ActivityBarPosition) {
|
||||
let positionClass: string;
|
||||
|
||||
if (position === ActivityBarPosition.LEFT) {
|
||||
positionClass = 'left';
|
||||
} else if (position === ActivityBarPosition.RIGHT) {
|
||||
positionClass = 'right';
|
||||
} else {
|
||||
throw new Error('No such position for activity bar defined.');
|
||||
}
|
||||
try {
|
||||
return await this.spectron.waitFor(this.spectron.client.getHTML, `.part.activitybar.${positionClass}`);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export enum CSSProblem {
|
||||
WARNING = 0,
|
||||
ERROR = 1
|
||||
};
|
||||
|
||||
export class CSS {
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public openQuickOutline(): any {
|
||||
return this.spectron.command('workbench.action.gotoSymbol');
|
||||
}
|
||||
|
||||
public toggleProblemsView(): any {
|
||||
return this.spectron.command('workbench.actions.view.problems');
|
||||
}
|
||||
|
||||
public async getEditorProblem(problemType: CSSProblem): Promise<any> {
|
||||
let selector;
|
||||
if (problemType === CSSProblem.WARNING) {
|
||||
selector = 'greensquiggly';
|
||||
} else if (problemType === CSSProblem.ERROR) {
|
||||
selector = 'redsquiggly';
|
||||
} else {
|
||||
throw new Error('No such problem type defined.');
|
||||
}
|
||||
|
||||
let el = await this.spectron.client.element(`.view-overlays .cdr.${selector}`);
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async getProblemsViewsProblem(problemType: CSSProblem): Promise<any> {
|
||||
let selector;
|
||||
if (problemType === CSSProblem.WARNING) {
|
||||
selector = 'warning';
|
||||
} else if (problemType === CSSProblem.ERROR) {
|
||||
selector = 'error';
|
||||
} else {
|
||||
throw new Error('No such problem type defined.');
|
||||
}
|
||||
|
||||
let el = await this.spectron.client.element(`div[aria-label="Problems grouped by files"] .icon.${selector}`);
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
56
test/smoke/src/areas/css/css.test.ts
Normal file
56
test/smoke/src/areas/css/css.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication } from '../../spectron/application';
|
||||
import { ProblemSeverity, Problems } from '../problems/problems';
|
||||
|
||||
describe('CSS', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'CSS';
|
||||
});
|
||||
|
||||
it('verifies quick outline', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.quickopen.openFile('style.css');
|
||||
|
||||
await app.workbench.editor.openOutline();
|
||||
await app.workbench.quickopen.waitForQuickOpenElements(names => names.length === 2);
|
||||
});
|
||||
|
||||
it('verifies warnings for the empty rule', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.quickopen.openFile('style.css');
|
||||
await app.workbench.editor.waitForTypeInEditor('style.css', '.foo{}');
|
||||
|
||||
let warning = await app.client.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.WARNING));
|
||||
await app.screenCapturer.capture('CSS Warning in editor');
|
||||
assert.ok(warning, `Warning squiggle is not shown in 'style.css'.`);
|
||||
|
||||
await app.workbench.problems.showProblemsView();
|
||||
warning = await app.client.waitForElement(Problems.getSelectorInProblemsView(ProblemSeverity.WARNING));
|
||||
await app.screenCapturer.capture('CSS Warning in problems view');
|
||||
assert.ok(warning, 'Warning does not appear in Problems view.');
|
||||
await app.workbench.problems.hideProblemsView();
|
||||
});
|
||||
|
||||
it('verifies that warning becomes an error once setting changed', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.settingsEditor.addUserSetting('css.lint.emptyRules', '"error"');
|
||||
await app.workbench.quickopen.openFile('style.css');
|
||||
await app.workbench.editor.waitForTypeInEditor('style.css', '.foo{}');
|
||||
|
||||
let error = await app.client.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.ERROR));
|
||||
await app.screenCapturer.capture('CSS Error in editor');
|
||||
assert.ok(error, `Warning squiggle is not shown in 'style.css'.`);
|
||||
|
||||
const problems = new Problems(app);
|
||||
await problems.showProblemsView();
|
||||
error = await app.client.waitForElement(Problems.getSelectorInProblemsView(ProblemSeverity.ERROR));
|
||||
await app.screenCapturer.capture('CSS Error in probles view');
|
||||
assert.ok(error, 'Warning does not appear in Problems view.');
|
||||
await problems.hideProblemsView();
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export class DataLoss {
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
}
|
||||
|
||||
public openExplorerViewlet(): Promise<any> {
|
||||
return this.spectron.command('workbench.view.explorer');
|
||||
}
|
||||
|
||||
public async verifyTabIsDirty(tabName: string, active?: boolean): Promise<any> {
|
||||
let activeSelector = active ? '.active' : '';
|
||||
let el = await this.spectron.client.element(`.tabs-container .tab.dirty${activeSelector}[aria-label="${tabName}, tab"]`);
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
154
test/smoke/src/areas/debug/debug.test.ts
Normal file
154
test/smoke/src/areas/debug/debug.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
171
test/smoke/src/areas/debug/debug.ts
Normal file
171
test/smoke/src/areas/debug/debug.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
73
test/smoke/src/areas/editor/editor.test.ts
Normal file
73
test/smoke/src/areas/editor/editor.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
describe('Editor', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'Editor';
|
||||
});
|
||||
|
||||
it('shows correct quick outline', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.quickopen.openFile('www');
|
||||
|
||||
await app.workbench.editor.openOutline();
|
||||
await app.workbench.quickopen.waitForQuickOpenElements(names => names.length >= 6);
|
||||
});
|
||||
|
||||
it(`finds 'All References' to 'app'`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.quickopen.openFile('www');
|
||||
|
||||
const references = await app.workbench.editor.findReferences('app', 7);
|
||||
|
||||
await references.waitForReferencesCountInTitle(3);
|
||||
await references.waitForReferencesCount(3);
|
||||
await references.close();
|
||||
});
|
||||
|
||||
it(`renames local 'app' variable`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.quickopen.openFile('www');
|
||||
await app.workbench.editor.rename('www', 7, 'app', 'newApp');
|
||||
await app.workbench.editor.waitForEditorContents('www', contents => contents.indexOf('newApp') > -1);
|
||||
await app.screenCapturer.capture('Rename result');
|
||||
});
|
||||
|
||||
// it('folds/unfolds the code correctly', async function () {
|
||||
// await app.workbench.quickopen.openFile('www');
|
||||
|
||||
// // Fold
|
||||
// await app.workbench.editor.foldAtLine(3);
|
||||
// await app.workbench.editor.waitUntilShown(3);
|
||||
// await app.workbench.editor.waitUntilHidden(4);
|
||||
// await app.workbench.editor.waitUntilHidden(5);
|
||||
|
||||
// // Unfold
|
||||
// await app.workbench.editor.unfoldAtLine(3);
|
||||
// await app.workbench.editor.waitUntilShown(3);
|
||||
// await app.workbench.editor.waitUntilShown(4);
|
||||
// await app.workbench.editor.waitUntilShown(5);
|
||||
// });
|
||||
|
||||
it(`verifies that 'Go To Definition' works`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.quickopen.openFile('app.js');
|
||||
|
||||
await app.workbench.editor.gotoDefinition('express', 11);
|
||||
|
||||
await app.workbench.waitForActiveTab('index.d.ts');
|
||||
});
|
||||
|
||||
it(`verifies that 'Peek Definition' works`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.quickopen.openFile('app.js');
|
||||
|
||||
const peek = await app.workbench.editor.peekDefinition('express', 11);
|
||||
|
||||
await peek.waitForFile('index.d.ts');
|
||||
});
|
||||
});
|
||||
194
test/smoke/src/areas/editor/editor.ts
Normal file
194
test/smoke/src/areas/editor/editor.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { QuickOutline } from './quickoutline';
|
||||
import { References } from './peek';
|
||||
|
||||
const RENAME_BOX = '.monaco-editor .monaco-editor.rename-box';
|
||||
const RENAME_INPUT = `${RENAME_BOX} .rename-input`;
|
||||
|
||||
export class Editor {
|
||||
|
||||
private static VIEW_LINES = '.monaco-editor .view-lines';
|
||||
private static LINE_NUMBERS = '.monaco-editor .margin .margin-view-overlays .line-numbers';
|
||||
private static FOLDING_EXPANDED = '.monaco-editor .margin .margin-view-overlays>:nth-child(${INDEX}) .folding';
|
||||
private static FOLDING_COLLAPSED = `${Editor.FOLDING_EXPANDED}.collapsed`;
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
}
|
||||
|
||||
async openOutline(): Promise<QuickOutline> {
|
||||
const outline = new QuickOutline(this.spectron);
|
||||
await outline.open();
|
||||
return outline;
|
||||
}
|
||||
|
||||
async findReferences(term: string, line: number): Promise<References> {
|
||||
await this.clickOnTerm(term, line);
|
||||
await this.spectron.workbench.quickopen.runCommand('Find All References');
|
||||
const references = new References(this.spectron);
|
||||
await references.waitUntilOpen();
|
||||
return references;
|
||||
}
|
||||
|
||||
async rename(filename: string, line: number, from: string, to: string): Promise<void> {
|
||||
await this.clickOnTerm(from, line);
|
||||
await this.spectron.workbench.quickopen.runCommand('Rename Symbol');
|
||||
|
||||
await this.spectron.client.waitForActiveElement(RENAME_INPUT);
|
||||
await this.spectron.client.setValue(RENAME_INPUT, to);
|
||||
|
||||
await this.spectron.client.keys(['Enter', 'NULL']);
|
||||
}
|
||||
|
||||
async gotoDefinition(term: string, line: number): Promise<void> {
|
||||
await this.clickOnTerm(term, line);
|
||||
await this.spectron.workbench.quickopen.runCommand('Go to Definition');
|
||||
}
|
||||
|
||||
async peekDefinition(term: string, line: number): Promise<References> {
|
||||
await this.clickOnTerm(term, line);
|
||||
await this.spectron.workbench.quickopen.runCommand('Peek Definition');
|
||||
const peek = new References(this.spectron);
|
||||
await peek.waitUntilOpen();
|
||||
return peek;
|
||||
}
|
||||
|
||||
async waitForHighlightingLine(line: number): Promise<void> {
|
||||
const currentLineIndex = await this.getViewLineIndex(line);
|
||||
if (currentLineIndex) {
|
||||
await this.spectron.client.waitForElement(`.monaco-editor .view-overlays>:nth-child(${currentLineIndex}) .current-line`);
|
||||
return;
|
||||
}
|
||||
throw new Error('Cannot find line ' + line);
|
||||
}
|
||||
|
||||
async getSelector(term: string, line: number): Promise<string> {
|
||||
const lineIndex = await this.getViewLineIndex(line);
|
||||
const classNames = await this.spectron.client.waitFor(() => this.getClassSelectors(term, lineIndex), classNames => classNames && !!classNames.length, 'Getting class names for editor lines');
|
||||
return `${Editor.VIEW_LINES}>:nth-child(${lineIndex}) span span.${classNames[0]}`;
|
||||
}
|
||||
|
||||
async foldAtLine(line: number): Promise<any> {
|
||||
const lineIndex = await this.getViewLineIndex(line);
|
||||
await this.spectron.client.waitAndClick(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex));
|
||||
await this.spectron.client.waitForElement(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex));
|
||||
}
|
||||
|
||||
async unfoldAtLine(line: number): Promise<any> {
|
||||
const lineIndex = await this.getViewLineIndex(line);
|
||||
await this.spectron.client.waitAndClick(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex));
|
||||
await this.spectron.client.waitForElement(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex));
|
||||
}
|
||||
|
||||
async waitUntilHidden(line: number): Promise<void> {
|
||||
await this.spectron.client.waitFor<number>(() => this.getViewLineIndexWithoutWait(line), lineNumber => lineNumber === undefined, 'Waiting until line number is hidden');
|
||||
}
|
||||
|
||||
async waitUntilShown(line: number): Promise<void> {
|
||||
await this.getViewLineIndex(line);
|
||||
}
|
||||
|
||||
async clickOnTerm(term: string, line: number): Promise<void> {
|
||||
const selector = await this.getSelector(term, line);
|
||||
await this.spectron.client.waitAndClick(selector);
|
||||
}
|
||||
|
||||
async waitForTypeInEditor(filename: string, text: string, selectorPrefix = ''): Promise<any> {
|
||||
const editor = [
|
||||
selectorPrefix || '',
|
||||
`.monaco-editor[data-uri$="${filename}"]`
|
||||
].join(' ');
|
||||
|
||||
await this.spectron.client.element(editor);
|
||||
|
||||
const textarea = `${editor} textarea`;
|
||||
await this.spectron.client.waitForActiveElement(textarea);
|
||||
|
||||
// https://github.com/Microsoft/vscode/issues/34203#issuecomment-334441786
|
||||
await this.spectron.client.spectron.client.selectorExecute(textarea, (elements, text) => {
|
||||
const textarea = (Array.isArray(elements) ? elements : [elements])[0] 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);
|
||||
}, text);
|
||||
|
||||
await this.waitForEditorContents(filename, c => c.indexOf(text) > -1, selectorPrefix);
|
||||
}
|
||||
|
||||
async waitForEditorContents(filename: string, accept: (contents: string) => boolean, selectorPrefix = ''): Promise<any> {
|
||||
const selector = [
|
||||
selectorPrefix || '',
|
||||
`.monaco-editor[data-uri$="${filename}"] .view-lines`
|
||||
].join(' ');
|
||||
|
||||
return this.spectron.client.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' ')));
|
||||
}
|
||||
|
||||
async waitForActiveEditor(filename: string): Promise<any> {
|
||||
const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] textarea`;
|
||||
return this.spectron.client.waitForActiveElement(selector);
|
||||
}
|
||||
|
||||
// async waitForActiveEditorFirstLineText(filename: string): Promise<string> {
|
||||
// const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] textarea`;
|
||||
// const result = await this.spectron.client.waitFor(
|
||||
// () => this.spectron.client.spectron.client.execute(s => {
|
||||
// if (!document.activeElement.matches(s)) {
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
// let element: Element | null = document.activeElement;
|
||||
// while (element && !/monaco-editor/.test(element.className) && element !== document.body) {
|
||||
// element = element.parentElement;
|
||||
// }
|
||||
|
||||
// if (element && /monaco-editor/.test(element.className)) {
|
||||
// const firstLine = element.querySelector('.view-lines span span:nth-child(1)');
|
||||
|
||||
// if (firstLine) {
|
||||
// return (firstLine.textContent || '').replace(/\u00a0/g, ' '); // DAMN
|
||||
// }
|
||||
// }
|
||||
|
||||
// return undefined;
|
||||
// }, selector),
|
||||
// r => typeof r.value === 'string',
|
||||
// `wait for active editor first line: ${selector}`
|
||||
// );
|
||||
|
||||
// return result.value;
|
||||
// }
|
||||
|
||||
private async getClassSelectors(term: string, viewline: number): Promise<string[]> {
|
||||
const result: { text: string, className: string }[] = await this.spectron.webclient.selectorExecute(`${Editor.VIEW_LINES}>:nth-child(${viewline}) span span`,
|
||||
elements => (Array.isArray(elements) ? elements : [elements])
|
||||
.map(element => ({ text: element.textContent, className: element.className })));
|
||||
return result.filter(r => r.text === term).map(({ className }) => className);
|
||||
}
|
||||
|
||||
private async getViewLineIndex(line: number): Promise<number> {
|
||||
return await this.spectron.client.waitFor<number>(() => this.getViewLineIndexWithoutWait(line), void 0, 'Getting line index');
|
||||
}
|
||||
|
||||
private async getViewLineIndexWithoutWait(line: number): Promise<number | undefined> {
|
||||
const lineNumbers = await this.spectron.webclient.selectorExecute(Editor.LINE_NUMBERS,
|
||||
elements => (Array.isArray(elements) ? elements : [elements]).map(element => element.textContent));
|
||||
for (let index = 0; index < lineNumbers.length; index++) {
|
||||
if (lineNumbers[index] === `${line}`) {
|
||||
return index + 1;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
41
test/smoke/src/areas/editor/peek.ts
Normal file
41
test/smoke/src/areas/editor/peek.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export class References {
|
||||
|
||||
private static REFERENCES_WIDGET = '.monaco-editor .zone-widget .zone-widget-container.peekview-widget.reference-zone-widget.results-loaded';
|
||||
private static REFERENCES_TITLE_FILE_NAME = `${References.REFERENCES_WIDGET} .head .peekview-title .filename`;
|
||||
private static REFERENCES_TITLE_COUNT = `${References.REFERENCES_WIDGET} .head .peekview-title .meta`;
|
||||
private static REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-tree-row .reference`;
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
}
|
||||
|
||||
public async waitUntilOpen(): Promise<void> {
|
||||
await this.spectron.client.waitForElement(References.REFERENCES_WIDGET);
|
||||
}
|
||||
|
||||
public async waitForReferencesCountInTitle(count: number): Promise<void> {
|
||||
await this.spectron.client.waitForText(References.REFERENCES_TITLE_COUNT, void 0, titleCount => {
|
||||
const matches = titleCount.match(/\d+/);
|
||||
return matches ? parseInt(matches[0]) === count : false;
|
||||
});
|
||||
}
|
||||
|
||||
public async waitForReferencesCount(count: number): Promise<void> {
|
||||
await this.spectron.client.waitForElements(References.REFERENCES, result => result && result.length === count);
|
||||
}
|
||||
|
||||
public async waitForFile(file: string): Promise<void> {
|
||||
await this.spectron.client.waitForText(References.REFERENCES_TITLE_FILE_NAME, file);
|
||||
}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
await this.spectron.client.keys(['Escape', 'NULL']);
|
||||
await this.spectron.client.waitForElement(References.REFERENCES_WIDGET, element => !element);
|
||||
}
|
||||
}
|
||||
28
test/smoke/src/areas/editor/quickoutline.ts
Normal file
28
test/smoke/src/areas/editor/quickoutline.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { QuickOpen } from '../quickopen/quickopen';
|
||||
|
||||
export class QuickOutline extends QuickOpen {
|
||||
|
||||
constructor(spectron: SpectronApplication) {
|
||||
super(spectron);
|
||||
}
|
||||
|
||||
public async open(): Promise<void> {
|
||||
await this.spectron.client.waitFor(async () => {
|
||||
await this.spectron.runCommand('workbench.action.gotoSymbol');
|
||||
const entry = await this.spectron.client.element('div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row .quick-open-entry');
|
||||
if (entry) {
|
||||
const text = await this.spectron.client.getText('div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row .quick-open-entry .monaco-icon-label .label-name .monaco-highlighted-label span');
|
||||
if (text !== 'No symbol information for the file') {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
await this.closeQuickOpen();
|
||||
}, undefined, 'Opening Outline');
|
||||
}
|
||||
}
|
||||
42
test/smoke/src/areas/explorer/explorer.test.ts
Normal file
42
test/smoke/src/areas/explorer/explorer.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
describe('Explorer', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'Explorer';
|
||||
});
|
||||
|
||||
it('quick open search produces correct result', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
const expectedNames = [
|
||||
'.eslintrc.json',
|
||||
'tasks.json',
|
||||
'app.js',
|
||||
'index.js',
|
||||
'users.js',
|
||||
'package.json',
|
||||
'jsconfig.json'
|
||||
];
|
||||
|
||||
await app.workbench.quickopen.openQuickOpen('.js');
|
||||
await app.workbench.quickopen.waitForQuickOpenElements(names => expectedNames.every(n => names.some(m => n === m)));
|
||||
await app.client.keys(['Escape', 'NULL']);
|
||||
});
|
||||
|
||||
it('quick open respects fuzzy matching', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
const expectedNames = [
|
||||
'tasks.json',
|
||||
'app.js',
|
||||
'package.json'
|
||||
];
|
||||
|
||||
await app.workbench.quickopen.openQuickOpen('a.s');
|
||||
await app.workbench.quickopen.waitForQuickOpenElements(names => expectedNames.every(n => names.some(m => n === m)));
|
||||
await app.client.keys(['Escape', 'NULL']);
|
||||
});
|
||||
});
|
||||
44
test/smoke/src/areas/explorer/explorer.ts
Normal file
44
test/smoke/src/areas/explorer/explorer.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
|
||||
export class Explorer extends Viewlet {
|
||||
|
||||
private static EXPLORER_VIEWLET = 'div[id="workbench.view.explorer"]';
|
||||
private static OPEN_EDITORS_VIEW = `${Explorer.EXPLORER_VIEWLET} .split-view-view:nth-child(1) .title`;
|
||||
|
||||
constructor(spectron: SpectronApplication) {
|
||||
super(spectron);
|
||||
}
|
||||
|
||||
public openExplorerView(): Promise<any> {
|
||||
return this.spectron.runCommand('workbench.view.explorer');
|
||||
}
|
||||
|
||||
public getOpenEditorsViewTitle(): Promise<string> {
|
||||
return this.spectron.client.waitForText(Explorer.OPEN_EDITORS_VIEW);
|
||||
}
|
||||
|
||||
public async openFile(fileName: string): Promise<any> {
|
||||
await this.spectron.client.doubleClickAndWait(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.getExtensionSelector(fileName)} explorer-item"]`);
|
||||
await this.spectron.workbench.waitForEditorFocus(fileName);
|
||||
}
|
||||
|
||||
public getExtensionSelector(fileName: string): string {
|
||||
const extension = fileName.split('.')[1];
|
||||
if (extension === 'js') {
|
||||
return 'js-ext-file-icon ext-file-icon javascript-lang-file-icon';
|
||||
} else if (extension === 'json') {
|
||||
return 'json-ext-file-icon ext-file-icon json-lang-file-icon';
|
||||
} else if (extension === 'md') {
|
||||
return 'md-ext-file-icon ext-file-icon markdown-lang-file-icon';
|
||||
}
|
||||
throw new Error('No class defined for this file extension');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { CommonActions } from './common';
|
||||
|
||||
var htmlparser = require('htmlparser2');
|
||||
|
||||
export class Extensions {
|
||||
|
||||
private readonly extensionsViewletSelector = 'div[id="workbench.view.extensions"]';
|
||||
private viewletExtensionIndex: number;
|
||||
|
||||
constructor(private spectron: SpectronApplication, private common: CommonActions) {
|
||||
}
|
||||
|
||||
public async openExtensionsViewlet(): Promise<any> {
|
||||
await this.spectron.command('workbench.view.extensions');
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public async searchForExtension(name: string): Promise<any> {
|
||||
const searchBoxSelector = `${this.extensionsViewletSelector} .search-box`;
|
||||
|
||||
await this.spectron.client.clearElement(searchBoxSelector);
|
||||
try {
|
||||
await this.spectron.client.click(searchBoxSelector, false);
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to click on search box in extensions viewlet.');
|
||||
}
|
||||
await this.spectron.client.keys(name);
|
||||
|
||||
return this.spectron.client.keys(['NULL', 'Enter', 'NULL']);
|
||||
}
|
||||
|
||||
public async installExtension(name: string): Promise<any> {
|
||||
const extensionListSelector = `${this.extensionsViewletSelector} .monaco-list-rows`;
|
||||
this.viewletExtensionIndex = await this.getExtensionIndex(name, extensionListSelector);
|
||||
|
||||
try {
|
||||
return this.spectron.client.click(`${extensionListSelector}>:nth-child(${this.viewletExtensionIndex}) .extension .extension-action.install`);
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to click on install button for selected extension.');
|
||||
}
|
||||
}
|
||||
|
||||
public getExtensionReloadText(): Promise<any> {
|
||||
try {
|
||||
return this.spectron.waitFor(this.spectron.client.getText, `${this.extensionsViewletSelector} .monaco-list-rows>:nth-child(${this.viewletExtensionIndex}) .extension .extension-action.reload`);
|
||||
} catch (e) {
|
||||
return Promise.reject('Reload was not prompted for an installed extension.');
|
||||
}
|
||||
}
|
||||
|
||||
public async activateExtension(): Promise<any> {
|
||||
await this.common.showCommands();
|
||||
await this.common.type('Smoke Test Check');
|
||||
await this.spectron.wait();
|
||||
return this.common.enter();
|
||||
}
|
||||
|
||||
public verifyStatusbarItem(): Promise<any> {
|
||||
try {
|
||||
return this.spectron.waitFor(this.spectron.client.getText, '.statusbar-item.statusbar-entry span[title="smoke test"]');
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to validate extension contribution.');
|
||||
}
|
||||
}
|
||||
|
||||
private getExtensionIndex(name: string, extensionListSelector: string): Promise<number> {
|
||||
return this.spectron.waitFor(this.spectron.client.getHTML, extensionListSelector).then(html => {
|
||||
return new Promise<number>((res, rej) => {
|
||||
let extensionIndex: number = 0;
|
||||
let extension: boolean;
|
||||
let tags: string[] = [];
|
||||
let parser = new htmlparser.Parser({
|
||||
onopentag: function (name, attribs) {
|
||||
if (name === 'div' && attribs.class === 'extension') {
|
||||
extensionIndex++;
|
||||
extension = true;
|
||||
}
|
||||
if (extension) {
|
||||
tags.push(name);
|
||||
}
|
||||
},
|
||||
ontext: function (text) {
|
||||
if (extension && text === name) {
|
||||
parser.end();
|
||||
}
|
||||
},
|
||||
onclosetag: function (name) {
|
||||
if (extension) {
|
||||
tags.pop();
|
||||
}
|
||||
if (extension && tags.length === 0) {
|
||||
extension = false;
|
||||
}
|
||||
},
|
||||
onend: function () {
|
||||
if (extensionIndex === 0) {
|
||||
return rej(`${name} extension was not found.`);
|
||||
}
|
||||
return res(extensionIndex);
|
||||
}
|
||||
});
|
||||
parser.write(html);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
36
test/smoke/src/areas/extensions/extensions.test.ts
Normal file
36
test/smoke/src/areas/extensions/extensions.test.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, Quality } from '../../spectron/application';
|
||||
|
||||
describe('Extensions', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'Extensions';
|
||||
});
|
||||
|
||||
it(`install and activate vscode-smoketest-check extension`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
if (app.quality === Quality.Dev) {
|
||||
this.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const extensionName = 'vscode-smoketest-check';
|
||||
await app.workbench.extensions.openExtensionsViewlet();
|
||||
|
||||
const installed = await app.workbench.extensions.installExtension(extensionName);
|
||||
assert.ok(installed);
|
||||
|
||||
await app.reload();
|
||||
await app.workbench.extensions.waitForExtensionsViewlet();
|
||||
await app.workbench.quickopen.runCommand('Smoke Test Check');
|
||||
|
||||
const statusbarText = await app.workbench.statusbar.getStatusbarTextByTitle('smoke test');
|
||||
await app.screenCapturer.capture('Statusbar');
|
||||
assert.equal(statusbarText, 'VS Code Smoke Test Check');
|
||||
});
|
||||
});
|
||||
42
test/smoke/src/areas/extensions/extensions.ts
Normal file
42
test/smoke/src/areas/extensions/extensions.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 SEARCH_BOX = 'div.extensions-viewlet[id="workbench.view.extensions"] input.search-box';
|
||||
|
||||
export class Extensions extends Viewlet {
|
||||
|
||||
constructor(spectron: SpectronApplication) {
|
||||
super(spectron);
|
||||
}
|
||||
|
||||
async openExtensionsViewlet(): Promise<any> {
|
||||
await this.spectron.runCommand('workbench.view.extensions');
|
||||
await this.waitForExtensionsViewlet();
|
||||
}
|
||||
|
||||
async waitForExtensionsViewlet(): Promise<any> {
|
||||
await this.spectron.client.waitForActiveElement(SEARCH_BOX);
|
||||
}
|
||||
|
||||
async searchForExtension(name: string): Promise<any> {
|
||||
await this.spectron.client.click(SEARCH_BOX);
|
||||
await this.spectron.client.waitForActiveElement(SEARCH_BOX);
|
||||
await this.spectron.client.setValue(SEARCH_BOX, name);
|
||||
}
|
||||
|
||||
async installExtension(name: string): Promise<boolean> {
|
||||
await this.searchForExtension(name);
|
||||
|
||||
// we might want to wait for a while longer since the Marketplace can be slow
|
||||
// a minute should do
|
||||
await this.spectron.client.waitFor(() => this.spectron.client.click(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${name}"] .extension li[class='action-item'] .extension-action.install`), void 0, 'waiting for install button', 600);
|
||||
|
||||
await this.spectron.client.waitForElement(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${name}"] .extension li[class='action-item'] .extension-action.reload`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { CommonActions } from './common';
|
||||
|
||||
var htmlparser = require('htmlparser2');
|
||||
|
||||
export class Git {
|
||||
private editorChangeIndex: number;
|
||||
|
||||
constructor(private spectron: SpectronApplication, private commonActions: CommonActions) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public openGitViewlet(): Promise<any> {
|
||||
return this.spectron.command('workbench.view.scm');
|
||||
}
|
||||
|
||||
public getScmIconChanges(): Promise<any> {
|
||||
return this.spectron.waitFor(this.spectron.client.getText, 'div[id="workbench.parts.activitybar"] .badge.scm-viewlet-label .badge-content');
|
||||
}
|
||||
|
||||
public async verifyScmChange(fileName: string): Promise<any> {
|
||||
let el;
|
||||
try {
|
||||
el = await this.spectron.client.element(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"]`);
|
||||
} catch (e) {
|
||||
return Promise.reject(`${fileName} change is not present in SCM viewlet.`);
|
||||
}
|
||||
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async getOriginalAppJsBodyVarName(): Promise<any> {
|
||||
this.editorChangeIndex = await this.getFirstChangeIndex('cdr line-delete', '.editor.original .view-overlays');
|
||||
return this.spectron.waitFor(this.spectron.client.getText, `.editor.original .view-lines>:nth-child(${this.editorChangeIndex}) .mtk11`);
|
||||
}
|
||||
|
||||
public getModifiedAppJsBodyVarName(): Promise<any> {
|
||||
return this.spectron.waitFor(this.spectron.client.getText, `.editor.modified .view-lines>:nth-child(${this.editorChangeIndex}) .mtk11`);
|
||||
}
|
||||
|
||||
public async stageFile(fileName: string): Promise<any> {
|
||||
try {
|
||||
await this.spectron.client.moveToObject(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"`);
|
||||
} catch (e) {
|
||||
return Promise.reject(`${fileName} was not found in SCM viewlet`);
|
||||
}
|
||||
|
||||
await this.spectron.wait();
|
||||
|
||||
try {
|
||||
await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-4');
|
||||
} catch (e) {
|
||||
return Promise.reject('Stage button was not found');
|
||||
}
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public async unstageFile(fileName: string): Promise<any> {
|
||||
try {
|
||||
await this.spectron.client.moveToObject(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"`);
|
||||
} catch (e) {
|
||||
return Promise.reject(`${fileName} was not found in SCM viewlet`);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-6');
|
||||
} catch (e) {
|
||||
return Promise.reject('Unstage button was not found.');
|
||||
}
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public async getStagedCount(): Promise<any> {
|
||||
let scmHeaders: Array<string>;
|
||||
try {
|
||||
scmHeaders = await this.spectron.waitFor(this.spectron.client.getText, '.scm-status.show-file-icons .monaco-list-rows .name'); // get all headers
|
||||
}
|
||||
catch (e) {
|
||||
return Promise.reject('No row names in SCM viewlet were found.');
|
||||
}
|
||||
|
||||
const stagedTitle = scmHeaders.find((val) => {
|
||||
return val.match(/staged/i) ? true : false;
|
||||
});
|
||||
|
||||
if (!stagedTitle) {
|
||||
return Promise.reject(`No 'Staged' header title found in SCM viewlet`);
|
||||
}
|
||||
|
||||
const monacoRowIndex = scmHeaders.indexOf(stagedTitle);
|
||||
try {
|
||||
return this.spectron.waitFor(this.spectron.client.getText, `.scm-status.show-file-icons .monaco-list-rows>:nth-child(${monacoRowIndex + 1}) .monaco-count-badge`);
|
||||
} catch (e) {
|
||||
return Promise.reject('Stage count badge cannot be found');
|
||||
}
|
||||
}
|
||||
|
||||
public focusOnCommitBox(): Promise<any> {
|
||||
try {
|
||||
return this.spectron.client.click('div[id="workbench.view.scm"] textarea');
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to focus on commit box: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public async pressCommit(): Promise<any> {
|
||||
try {
|
||||
await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-10');
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to press commit: ' + e);
|
||||
}
|
||||
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public getOutgoingChanges(): Promise<string> {
|
||||
try {
|
||||
return this.spectron.client.getText('a[title="Synchronize Changes"]');
|
||||
} catch (e) {
|
||||
return Promise.reject(`Failed to obtain 'synchronize changes' title value from the status bar.`);
|
||||
}
|
||||
}
|
||||
|
||||
private getFirstChangeIndex(changeClass: string, selector: string): Promise<number> {
|
||||
return this.spectron.waitFor(this.spectron.client.getHTML, selector).then(html => {
|
||||
return new Promise<number>((res, rej) => {
|
||||
let lineIndex: number = 0;
|
||||
let changeFound: boolean;
|
||||
let tags: string[] = [];
|
||||
let parser = new htmlparser.Parser({
|
||||
onopentag: function (name: string, attribs: any) {
|
||||
tags.push(name);
|
||||
if (name === 'div' && !attribs.class) {
|
||||
lineIndex++;
|
||||
} else if (name === 'div' && attribs.class === changeClass) {
|
||||
changeFound = true;
|
||||
parser.end();
|
||||
}
|
||||
},
|
||||
onclosetag: function (name) {
|
||||
// Terminate once last tag is closed
|
||||
tags.pop();
|
||||
if (!changeFound && tags.length === 0) {
|
||||
parser.end();
|
||||
}
|
||||
},
|
||||
onend: function () {
|
||||
if (!changeFound) {
|
||||
return rej(`No changes in the diff found.`);
|
||||
}
|
||||
return res(lineIndex);
|
||||
}
|
||||
});
|
||||
parser.write(html);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
99
test/smoke/src/areas/git/git.test.ts
Normal file
99
test/smoke/src/areas/git/git.test.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 cp from 'child_process';
|
||||
import { SpectronApplication } from '../../spectron/application';
|
||||
|
||||
const DIFF_EDITOR_LINE_INSERT = '.monaco-diff-editor .editor.modified .line-insert';
|
||||
const SYNC_STATUSBAR = 'div[id="workbench.parts.statusbar"] .statusbar-entry a[title$="Synchronize Changes"]';
|
||||
|
||||
describe('Git', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'Git';
|
||||
});
|
||||
|
||||
it('reflects working tree changes', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.scm.openSCMViewlet();
|
||||
|
||||
await app.workbench.quickopen.openFile('app.js');
|
||||
await app.workbench.editor.waitForTypeInEditor('app.js', '.foo{}');
|
||||
await app.workbench.saveOpenedFile();
|
||||
|
||||
await app.workbench.quickopen.openFile('index.jade');
|
||||
await app.workbench.editor.waitForTypeInEditor('index.jade', 'hello world');
|
||||
await app.workbench.saveOpenedFile();
|
||||
|
||||
await app.workbench.scm.refreshSCMViewlet();
|
||||
const appJs = await app.workbench.scm.waitForChange(c => c.name === 'app.js');
|
||||
const indexJade = await app.workbench.scm.waitForChange(c => c.name === 'index.jade');
|
||||
await app.screenCapturer.capture('changes');
|
||||
|
||||
assert.equal(appJs.name, 'app.js');
|
||||
assert.equal(appJs.type, 'Modified');
|
||||
|
||||
assert.equal(indexJade.name, 'index.jade');
|
||||
assert.equal(indexJade.type, 'Modified');
|
||||
});
|
||||
|
||||
it('opens diff editor', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.scm.openSCMViewlet();
|
||||
const appJs = await app.workbench.scm.waitForChange(c => c.name === 'app.js');
|
||||
await app.workbench.scm.openChange(appJs);
|
||||
await app.client.waitForElement(DIFF_EDITOR_LINE_INSERT);
|
||||
});
|
||||
|
||||
it('stages correctly', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
// TODO@joao get these working once joh fixes scm viewlet
|
||||
if (!false) {
|
||||
this.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
await app.workbench.scm.openSCMViewlet();
|
||||
|
||||
const appJs = await app.workbench.scm.waitForChange(c => c.name === 'app.js' && c.type === 'Modified');
|
||||
await app.workbench.scm.stage(appJs);
|
||||
|
||||
const indexAppJs = await app.workbench.scm.waitForChange(c => c.name === 'app.js' && c.type === 'Index Modified');
|
||||
await app.workbench.scm.unstage(indexAppJs);
|
||||
|
||||
await app.workbench.scm.waitForChange(c => c.name === 'app.js' && c.type === 'Modified');
|
||||
});
|
||||
|
||||
it(`stages, commits changes and verifies outgoing change`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
// TODO@joao get these working once joh fixes scm viewlet
|
||||
if (!false) {
|
||||
cp.execSync('git reset --hard origin/master', { cwd: app.workspacePath });
|
||||
this.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
await app.workbench.scm.openSCMViewlet();
|
||||
|
||||
const appJs = await app.workbench.scm.waitForChange(c => c.name === 'app.js' && c.type === 'Modified');
|
||||
await app.workbench.scm.stage(appJs);
|
||||
await app.workbench.scm.waitForChange(c => c.name === 'app.js' && c.type === 'Index Modified');
|
||||
|
||||
await app.workbench.scm.commit('first commit');
|
||||
await app.client.waitForText(SYNC_STATUSBAR, ' 0↓ 1↑');
|
||||
|
||||
await app.workbench.quickopen.runCommand('Git: Stage All Changes');
|
||||
await app.workbench.scm.waitForChange(c => c.name === 'index.jade' && c.type === 'Index Modified');
|
||||
|
||||
await app.workbench.scm.commit('second commit');
|
||||
await app.client.waitForText(SYNC_STATUSBAR, ' 0↓ 2↑');
|
||||
|
||||
cp.execSync('git reset --hard origin/master', { cwd: app.workspacePath });
|
||||
});
|
||||
});
|
||||
103
test/smoke/src/areas/git/scm.ts
Normal file
103
test/smoke/src/areas/git/scm.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication } from '../../spectron/application';
|
||||
import { Viewlet } from '../workbench/viewlet';
|
||||
|
||||
const VIEWLET = 'div[id="workbench.view.scm"]';
|
||||
const SCM_INPUT = `${VIEWLET} .scm-editor textarea`;
|
||||
const SCM_RESOURCE = `${VIEWLET} .monaco-list-row > .resource`;
|
||||
const SCM_RESOURCE_GROUP = `${VIEWLET} .monaco-list-row > .resource-group`;
|
||||
const REFRESH_COMMAND = `div[id="workbench.parts.sidebar"] .actions-container a.action-label[title="Refresh"]`;
|
||||
const COMMIT_COMMAND = `div[id="workbench.parts.sidebar"] .actions-container a.action-label[title="Commit"]`;
|
||||
const SCM_RESOURCE_CLICK = name => `${SCM_RESOURCE} .monaco-icon-label[title$="${name}"]`;
|
||||
const SCM_RESOURCE_GROUP_COMMAND_CLICK = name => `${SCM_RESOURCE_GROUP} .actions .action-label[title="${name}"]`;
|
||||
|
||||
export interface Change {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
actions: { id: string, title: string; }[];
|
||||
}
|
||||
|
||||
export class SCM extends Viewlet {
|
||||
|
||||
constructor(spectron: SpectronApplication) {
|
||||
super(spectron);
|
||||
}
|
||||
|
||||
async openSCMViewlet(): Promise<any> {
|
||||
await this.spectron.runCommand('workbench.view.scm');
|
||||
await this.spectron.client.waitForElement(SCM_INPUT);
|
||||
}
|
||||
|
||||
async waitForChange(func: (change: Change) => boolean): Promise<Change> {
|
||||
return await this.spectron.client.waitFor(async () => {
|
||||
const changes = await this.getChanges();
|
||||
return changes.filter(func)[0];
|
||||
}, void 0, 'Getting changes');
|
||||
}
|
||||
|
||||
async refreshSCMViewlet(): Promise<any> {
|
||||
await this.spectron.client.click(REFRESH_COMMAND);
|
||||
}
|
||||
|
||||
async getChanges(): Promise<Change[]> {
|
||||
const result = await this.spectron.webclient.selectorExecute(SCM_RESOURCE,
|
||||
div => (Array.isArray(div) ? div : [div]).map(element => {
|
||||
const name = element.querySelector('.label-name') as HTMLElement;
|
||||
const icon = element.querySelector('.monaco-icon-label') as HTMLElement;
|
||||
const actionElementList = element.querySelectorAll('.actions .action-label');
|
||||
const actionElements: any[] = [];
|
||||
|
||||
for (let i = 0; i < actionElementList.length; i++) {
|
||||
const element = actionElementList.item(i) as HTMLElement;
|
||||
actionElements.push({ element, title: element.title });
|
||||
}
|
||||
|
||||
return {
|
||||
name: name.textContent,
|
||||
type: (icon.title || '').replace(/^([^,]+),.*$/, '$1'),
|
||||
element,
|
||||
actionElements
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return result.map(({ name, type, element, actionElements }) => {
|
||||
// const actions = actionElements.reduce((r, { element, title }) => r[title] = element.ELEMENT, {});
|
||||
const actions = actionElements.map(({ element, title }) => ({ id: element.ELEMENT, title }));
|
||||
return { name, type, id: element.ELEMENT, actions };
|
||||
});
|
||||
}
|
||||
|
||||
async openChange(change: Change): Promise<void> {
|
||||
await this.spectron.client.waitAndClick(SCM_RESOURCE_CLICK(change.name));
|
||||
}
|
||||
|
||||
async stage(change: Change): Promise<void> {
|
||||
const action = change.actions.filter(a => a.title === 'Stage Changes')[0];
|
||||
assert(action);
|
||||
await this.spectron.client.spectron.client.elementIdClick(action.id);
|
||||
}
|
||||
|
||||
async stageAll(): Promise<void> {
|
||||
await this.spectron.client.waitAndClick(SCM_RESOURCE_GROUP_COMMAND_CLICK('Stage All Changes'));
|
||||
}
|
||||
|
||||
async unstage(change: Change): Promise<void> {
|
||||
const action = change.actions.filter(a => a.title === 'Unstage Changes')[0];
|
||||
assert(action);
|
||||
await this.spectron.client.spectron.client.elementIdClick(action.id);
|
||||
}
|
||||
|
||||
async commit(message: string): Promise<void> {
|
||||
await this.spectron.client.waitAndClick(SCM_INPUT);
|
||||
await this.spectron.client.waitForActiveElement(SCM_INPUT);
|
||||
await this.spectron.client.setValue(SCM_INPUT, message);
|
||||
await this.spectron.client.waitAndClick(COMMIT_COMMAND);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { CommonActions } from './common';
|
||||
|
||||
export class IntegratedTerminal {
|
||||
|
||||
public static terminalSelector = 'div[id="workbench.panel.terminal"]';
|
||||
public static terminalRowsSelector = 'div[id="workbench.panel.terminal"] .xterm-rows';
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public async openTerminal(commonActions: CommonActions): Promise<any> {
|
||||
// Backquote dispatching does not work in OS X
|
||||
if (process.platform === 'darwin') {
|
||||
await commonActions.showCommands();
|
||||
await commonActions.type('Toggle Integrated Terminal');
|
||||
return commonActions.enter();
|
||||
}
|
||||
|
||||
await this.spectron.command('workbench.action.terminal.toggleTerminal');
|
||||
|
||||
// If no terminal panel was opened, try triggering terminal from quick open
|
||||
try {
|
||||
await this.spectron.client.getHTML(IntegratedTerminal.terminalSelector);
|
||||
} catch (e) {
|
||||
await commonActions.openQuickOpen();
|
||||
await this.spectron.client.keys('>Toggle Integrated Terminal');
|
||||
await this.spectron.client.keys(['Enter', 'NULL']);
|
||||
}
|
||||
}
|
||||
|
||||
public async commandOutputHas(result: string): Promise<boolean> {
|
||||
const rows = await this.spectron.client.elements(`${IntegratedTerminal.terminalRowsSelector} div`);
|
||||
for (let i = 0; i < rows.value.length; i++) {
|
||||
let rowText;
|
||||
try {
|
||||
rowText = await this.spectron.client.getText(`${IntegratedTerminal.terminalRowsSelector}>:nth-child(${i + 1})`);
|
||||
} catch (e) {
|
||||
return Promise.reject(`Failed to obtain text from line ${i + 1} from the terminal.`);
|
||||
}
|
||||
if (rowText.trim() === result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
var stripJsonComments = require('strip-json-comments');
|
||||
|
||||
export class JavaScriptDebug {
|
||||
private readonly sidebarSelector = '.margin-view-overlays';
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public openDebugViewlet(): Promise<any> {
|
||||
return this.spectron.command('workbench.view.debug');
|
||||
}
|
||||
|
||||
public async pressConfigureLaunchJson(): Promise<any> {
|
||||
try {
|
||||
await this.spectron.waitFor(this.spectron.client.click, 'ul[aria-label="Debug actions"] .action-label.icon.debug-action.configure');
|
||||
} catch (e) {
|
||||
return Promise.reject('Clicking on debug configuration gear failed.');
|
||||
}
|
||||
await this.spectron.wait();
|
||||
await this.spectron.client.keys(['ArrowDown', 'NULL', 'Enter']);
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public async getProgramConfigValue(): Promise<any> {
|
||||
const lines = stripJsonComments(await this.spectron.client.getText('.view-lines'));
|
||||
const json = JSON.parse(lines);
|
||||
return json.configurations[0].program;
|
||||
}
|
||||
|
||||
public setBreakpointOnLine(lineNumber: number): Promise<any> {
|
||||
try {
|
||||
return this.spectron.client.leftClick(`${this.sidebarSelector}>:nth-child(${lineNumber})`, 5, 5);
|
||||
} catch (e) {
|
||||
return Promise.reject('Setting breakpoint failed: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public async verifyBreakpointOnLine(lineNumber: number): Promise<any> {
|
||||
let el = await this.spectron.client.element(`${this.sidebarSelector}>:nth-child(${lineNumber}) .cgmr.debug-breakpoint-glyph`);
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
var htmlparser = require('htmlparser2');
|
||||
|
||||
export class JavaScript {
|
||||
private appVarSelector: string;
|
||||
private expressVarSelector: string;
|
||||
|
||||
private foldSelector: string;
|
||||
private foldLine: number;
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public openQuickOutline(): Promise<any> {
|
||||
return this.spectron.command('workbench.action.gotoSymbol');
|
||||
}
|
||||
|
||||
public async findAppReferences(): Promise<any> {
|
||||
await this.setAppVarSelector();
|
||||
try {
|
||||
await this.spectron.client.click(this.appVarSelector, false);
|
||||
} catch (e) {
|
||||
return Promise.reject(`Failed to select 'app' variable.`);
|
||||
}
|
||||
|
||||
return this.spectron.command('editor.action.referenceSearch.trigger');
|
||||
}
|
||||
|
||||
public async getTitleReferencesCount(): Promise<any> {
|
||||
const meta = await this.spectron.client.getText('.reference-zone-widget.results-loaded .peekview-title .meta');
|
||||
|
||||
return meta.match(/\d+/)[0];
|
||||
}
|
||||
|
||||
public async getTreeReferencesCount(): Promise<any> {
|
||||
const treeElems = await this.spectron.client.elements('.reference-zone-widget.results-loaded .ref-tree.inline .show-twisties .monaco-tree-row');
|
||||
|
||||
return treeElems.value.length;
|
||||
}
|
||||
|
||||
public async renameApp(newValue: string): Promise<any> {
|
||||
await this.setAppVarSelector();
|
||||
|
||||
try {
|
||||
await this.spectron.client.click(this.appVarSelector);
|
||||
} catch (e) {
|
||||
return Promise.reject(`Failed to select 'app' variable.`);
|
||||
}
|
||||
await this.spectron.command('editor.action.rename');
|
||||
await this.spectron.wait();
|
||||
return this.spectron.client.keys(newValue, false);
|
||||
}
|
||||
|
||||
public async getNewAppName(): Promise<any> {
|
||||
return this.spectron.client.getText(this.appVarSelector);
|
||||
}
|
||||
|
||||
public async toggleFirstCommentFold(): Promise<any> {
|
||||
this.foldLine = await this.getLineIndexOfFirstFoldableElement(`.margin-view-overlays`);
|
||||
this.foldSelector = `.margin-view-overlays>:nth-child(${this.foldLine})`;
|
||||
|
||||
try {
|
||||
return this.spectron.client.click(`${this.foldSelector} .cldr.folding`);
|
||||
} catch (e) {
|
||||
return Promise.reject('Clicking on fold element failed ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public async getFirstCommentFoldedIcon(): Promise<any> {
|
||||
if (!this.foldSelector) {
|
||||
return Promise.reject('No code folding happened to be able to check for a folded icon.');
|
||||
}
|
||||
|
||||
return this.spectron.client.getHTML(`${this.foldSelector} .cldr.folding.collapsed`);
|
||||
}
|
||||
|
||||
public async getNextLineNumberAfterFold(): Promise<any> {
|
||||
if (!this.foldLine) {
|
||||
return Promise.reject('Folded line was not set, most likely because fold was not toggled initially.');
|
||||
}
|
||||
|
||||
return this.spectron.client.getText(`.margin-view-overlays>:nth-child(${this.foldLine + 1}) .line-numbers`);
|
||||
}
|
||||
|
||||
public async goToExpressDefinition(): Promise<any> {
|
||||
await this.setExpressVarSelector();
|
||||
try {
|
||||
await this.spectron.client.click(this.expressVarSelector);
|
||||
} catch (e) {
|
||||
return Promise.reject(`Clicking on express variable failed: ` + e);
|
||||
}
|
||||
|
||||
return this.spectron.command('editor.action.goToDeclaration');
|
||||
}
|
||||
|
||||
public async peekExpressDefinition(): Promise<any> {
|
||||
await this.setExpressVarSelector();
|
||||
try {
|
||||
await this.spectron.client.click(this.expressVarSelector);
|
||||
} catch (e) {
|
||||
return Promise.reject('Clicking on express variable failed: ' + e);
|
||||
}
|
||||
|
||||
return this.spectron.command('editor.action.previewDeclaration');
|
||||
}
|
||||
|
||||
public async getPeekExpressResultName(): Promise<any> {
|
||||
return this.spectron.client.getText('.reference-zone-widget.results-loaded .filename');
|
||||
}
|
||||
|
||||
private async setAppVarSelector(): Promise<any> {
|
||||
if (!this.appVarSelector) {
|
||||
const lineIndex = await this.getLineIndexOfFirst('app', '.view-lines');
|
||||
this.appVarSelector = `.view-lines>:nth-child(${lineIndex}) .mtk11`;
|
||||
}
|
||||
}
|
||||
|
||||
private async setExpressVarSelector(): Promise<any> {
|
||||
if (!this.expressVarSelector) {
|
||||
const lineIndex = await this.getLineIndexOfFirst('express', '.view-lines');
|
||||
this.expressVarSelector = `.view-lines>:nth-child(${lineIndex}) .mtk10`;
|
||||
}
|
||||
}
|
||||
|
||||
private getLineIndexOfFirst(string: string, selector: string): Promise<number> {
|
||||
return this.spectron.waitFor(this.spectron.client.getHTML, selector).then(html => {
|
||||
return new Promise<number>((res, rej) => {
|
||||
let lineIndex: number = 0;
|
||||
let stringFound: boolean;
|
||||
let parser = new htmlparser.Parser({
|
||||
onopentag: function (name: string, attribs: any) {
|
||||
if (name === 'div' && attribs.class === 'view-line') {
|
||||
lineIndex++;
|
||||
}
|
||||
},
|
||||
ontext: function (text) {
|
||||
if (!stringFound && text === string) {
|
||||
stringFound = true;
|
||||
parser.end();
|
||||
}
|
||||
},
|
||||
onend: function () {
|
||||
if (!stringFound) {
|
||||
return rej(`No ${string} in editor found.`);
|
||||
}
|
||||
return res(lineIndex);
|
||||
}
|
||||
});
|
||||
parser.write(html);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getLineIndexOfFirstFoldableElement(selector: string): Promise<number> {
|
||||
return this.spectron.waitFor(this.spectron.client.getHTML, selector).then(html => {
|
||||
return new Promise<number>((res, rej) => {
|
||||
let lineIndex: number = 0;
|
||||
let foldFound: boolean;
|
||||
let parser = new htmlparser.Parser({
|
||||
onopentag: function (name: string, attribs: any) {
|
||||
if (name === 'div' && !attribs.class) {
|
||||
lineIndex++;
|
||||
} else if (name === 'div' && attribs.class.indexOf('cldr folding') !== -1) {
|
||||
foldFound = true;
|
||||
parser.end();
|
||||
}
|
||||
},
|
||||
onend: function () {
|
||||
if (!foldFound) {
|
||||
return rej(`No foldable elements found.`);
|
||||
}
|
||||
return res(lineIndex);
|
||||
}
|
||||
});
|
||||
parser.write(html);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export enum ViewletType {
|
||||
SEARCH = 0,
|
||||
SCM = 1,
|
||||
DEBUG = 2,
|
||||
EXTENSIONS = 3
|
||||
}
|
||||
|
||||
export class Localization {
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public async getOpenEditorsText(): Promise<string> {
|
||||
let explorerTitles;
|
||||
try {
|
||||
explorerTitles = await this.spectron.client.getText('div[id="workbench.view.explorer"] .title span');
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to get span of title in explorer viewlet.');
|
||||
}
|
||||
|
||||
return explorerTitles[0];
|
||||
}
|
||||
|
||||
public async openViewlet(type: ViewletType): Promise<any> {
|
||||
let command;
|
||||
|
||||
switch (type) {
|
||||
case ViewletType.SEARCH:
|
||||
command = 'workbench.view.search';
|
||||
break;
|
||||
case ViewletType.SCM:
|
||||
command = 'workbench.view.scm';
|
||||
break;
|
||||
case ViewletType.DEBUG:
|
||||
command = 'workbench.view.debug';
|
||||
break;
|
||||
case ViewletType.EXTENSIONS:
|
||||
command = 'workbench.view.extensions';
|
||||
break;
|
||||
}
|
||||
|
||||
await this.spectron.command(command, false);
|
||||
return this.spectron.wait();
|
||||
}
|
||||
|
||||
public getOpenedViewletTitle(): Promise<string> {
|
||||
try {
|
||||
return this.spectron.client.getText('div[id="workbench.parts.sidebar"] .title-label span');
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to get span of title label in explorer viewlet.');
|
||||
}
|
||||
}
|
||||
|
||||
public getExtensionsSearchPlaceholder(): Promise<string> {
|
||||
try {
|
||||
return this.spectron.client.getAttribute('div[id="workbench.view.extensions"] .search-box', 'placeholder');
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to get extension viewlet search box placeholder.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
37
test/smoke/src/areas/multiroot/multiroot.test.ts
Normal file
37
test/smoke/src/areas/multiroot/multiroot.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication } from '../../spectron/application';
|
||||
|
||||
describe('Multiroot', () => {
|
||||
|
||||
before(async function () {
|
||||
this.app.suiteName = 'Multiroot';
|
||||
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.restart([app.workspaceFilePath]);
|
||||
|
||||
// for some reason Code opens 2 windows at this point
|
||||
// so let's select the last one
|
||||
await app.client.windowByIndex(2);
|
||||
});
|
||||
|
||||
it('shows results from all folders', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.quickopen.openQuickOpen('*.*');
|
||||
|
||||
await app.workbench.quickopen.waitForQuickOpenElements(names => names.length >= 6);
|
||||
await app.workbench.quickopen.closeQuickOpen();
|
||||
});
|
||||
|
||||
it('shows workspace name in title', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
const title = await app.client.getTitle();
|
||||
await app.screenCapturer.capture('window title');
|
||||
assert.ok(title.indexOf('smoketest (Workspace)') >= 0);
|
||||
});
|
||||
});
|
||||
28
test/smoke/src/areas/preferences/keybindings.ts
Normal file
28
test/smoke/src/areas/preferences/keybindings.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
const SEARCH_INPUT = '.settings-search-input input';
|
||||
|
||||
export class KeybindingsEditor {
|
||||
|
||||
constructor(private spectron: SpectronApplication) { }
|
||||
|
||||
async updateKeybinding(command: string, keys: string[], ariaLabel: string): Promise<any> {
|
||||
await this.spectron.runCommand('workbench.action.openGlobalKeybindings');
|
||||
await this.spectron.client.waitForActiveElement(SEARCH_INPUT);
|
||||
await this.spectron.client.setValue(SEARCH_INPUT, command);
|
||||
|
||||
await this.spectron.client.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item');
|
||||
await this.spectron.client.waitForElement('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item.focused.selected');
|
||||
|
||||
await this.spectron.client.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item .action-item .icon.add');
|
||||
await this.spectron.client.waitForElement('.defineKeybindingWidget .monaco-inputbox.synthetic-focus');
|
||||
|
||||
await this.spectron.client.keys([...keys, 'NULL', 'Enter', 'NULL']);
|
||||
await this.spectron.client.waitForElement(`div[aria-label="Keybindings"] div[aria-label="Keybinding is ${ariaLabel}."]`);
|
||||
}
|
||||
}
|
||||
46
test/smoke/src/areas/preferences/preferences.test.ts
Normal file
46
test/smoke/src/areas/preferences/preferences.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication } from '../../spectron/application';
|
||||
import { ActivityBarPosition } from '../activitybar/activityBar';
|
||||
|
||||
describe('Preferences', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'Preferences';
|
||||
});
|
||||
|
||||
it('turns off editor line numbers and verifies the live change', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.explorer.openFile('app.js');
|
||||
let lineNumbers = await app.client.waitForElements('.line-numbers');
|
||||
await app.screenCapturer.capture('app.js has line numbers');
|
||||
assert.ok(!!lineNumbers.length, 'Line numbers are not present in the editor before disabling them.');
|
||||
|
||||
await app.workbench.settingsEditor.addUserSetting('editor.lineNumbers', '"off"');
|
||||
await app.workbench.selectTab('app.js');
|
||||
lineNumbers = await app.client.waitForElements('.line-numbers', result => !result || result.length === 0);
|
||||
|
||||
await app.screenCapturer.capture('line numbers hidden');
|
||||
assert.ok(!lineNumbers.length, 'Line numbers are still present in the editor after disabling them.');
|
||||
});
|
||||
|
||||
it(`changes 'workbench.action.toggleSidebarPosition' command key binding and verifies it`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
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.client.keys(['Control', 'u', 'NULL']);
|
||||
assert.ok(await app.workbench.activitybar.getActivityBar(ActivityBarPosition.RIGHT), 'Activity bar was not moved to right after toggling its position.');
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.settingsEditor.clearUserSettings();
|
||||
});
|
||||
});
|
||||
45
test/smoke/src/areas/preferences/settings.ts
Normal file
45
test/smoke/src/areas/preferences/settings.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { SpectronApplication } from '../../spectron/application';
|
||||
|
||||
export enum ActivityBarPosition {
|
||||
LEFT = 0,
|
||||
RIGHT = 1
|
||||
};
|
||||
|
||||
const SEARCH_INPUT = '.settings-search-input input';
|
||||
const EDITOR = '.editable-preferences-editor-container .monaco-editor textarea';
|
||||
|
||||
export class SettingsEditor {
|
||||
|
||||
constructor(private spectron: SpectronApplication) { }
|
||||
|
||||
async addUserSetting(setting: string, value: string): Promise<void> {
|
||||
await this.spectron.runCommand('workbench.action.openGlobalSettings');
|
||||
await this.spectron.client.waitAndClick(SEARCH_INPUT);
|
||||
await this.spectron.client.waitForActiveElement(SEARCH_INPUT);
|
||||
|
||||
await this.spectron.client.keys(['ArrowDown', 'NULL']);
|
||||
await this.spectron.client.waitForActiveElement(EDITOR);
|
||||
|
||||
await this.spectron.client.keys(['ArrowRight', 'NULL']);
|
||||
await this.spectron.screenCapturer.capture('user settings is open and focused');
|
||||
|
||||
await this.spectron.workbench.editor.waitForTypeInEditor('settings.json', `"${setting}": ${value}`, '.editable-preferences-editor-container');
|
||||
await this.spectron.workbench.saveOpenedFile();
|
||||
|
||||
await this.spectron.screenCapturer.capture('user settings has changed');
|
||||
}
|
||||
|
||||
async clearUserSettings(): Promise<void> {
|
||||
const settingsPath = path.join(this.spectron.userDataPath, 'User', 'settings.json');
|
||||
await new Promise((c, e) => fs.writeFile(settingsPath, '{}', 'utf8', err => err ? e(err) : c()));
|
||||
|
||||
await this.spectron.workbench.editor.waitForEditorContents('settings.json', c => c.length === 0, '.editable-preferences-editor-container');
|
||||
}
|
||||
}
|
||||
53
test/smoke/src/areas/problems/problems.ts
Normal file
53
test/smoke/src/areas/problems/problems.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export enum ProblemSeverity {
|
||||
WARNING = 0,
|
||||
ERROR = 1
|
||||
};
|
||||
|
||||
export class Problems {
|
||||
|
||||
static PROBLEMS_VIEW_SELECTOR = '.panel.markers-panel';
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public async showProblemsView(): Promise<any> {
|
||||
if (!await this.isVisible()) {
|
||||
await this.spectron.runCommand('workbench.actions.view.problems');
|
||||
await this.waitForProblemsView();
|
||||
}
|
||||
}
|
||||
|
||||
public async hideProblemsView(): Promise<any> {
|
||||
if (await this.isVisible()) {
|
||||
await this.spectron.runCommand('workbench.actions.view.problems');
|
||||
await this.spectron.client.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR, el => !el);
|
||||
}
|
||||
}
|
||||
|
||||
public async isVisible(): Promise<boolean> {
|
||||
const element = await this.spectron.client.element(Problems.PROBLEMS_VIEW_SELECTOR);
|
||||
return !!element;
|
||||
}
|
||||
|
||||
public async waitForProblemsView(): Promise<void> {
|
||||
await this.spectron.client.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR);
|
||||
}
|
||||
|
||||
public static getSelectorInProblemsView(problemType: ProblemSeverity): string {
|
||||
let selector = problemType === ProblemSeverity.WARNING ? 'warning' : 'error';
|
||||
return `div[aria-label="Problems grouped by files"] .icon.${selector}`;
|
||||
}
|
||||
|
||||
public static getSelectorInEditor(problemType: ProblemSeverity): string {
|
||||
let selector = problemType === ProblemSeverity.WARNING ? 'warningsquiggly' : 'errorsquiggly';
|
||||
return `.view-overlays .cdr.${selector}`;
|
||||
}
|
||||
}
|
||||
91
test/smoke/src/areas/quickopen/quickopen.ts
Normal file
91
test/smoke/src/areas/quickopen/quickopen.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export class QuickOpen {
|
||||
|
||||
static QUICK_OPEN_HIDDEN = 'div.quick-open-widget[aria-hidden="true"]';
|
||||
static QUICK_OPEN = 'div.quick-open-widget[aria-hidden="false"]';
|
||||
static QUICK_OPEN_INPUT = `${QuickOpen.QUICK_OPEN} .quick-open-input input`;
|
||||
static QUICK_OPEN_FOCUSED_ELEMENT = `${QuickOpen.QUICK_OPEN} .quick-open-tree .monaco-tree-row.focused .monaco-highlighted-label`;
|
||||
static QUICK_OPEN_ENTRY_SELECTOR = 'div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties .monaco-tree-row .quick-open-entry';
|
||||
|
||||
constructor(readonly spectron: SpectronApplication) { }
|
||||
|
||||
async openQuickOpen(value: string): Promise<void> {
|
||||
await this.spectron.runCommand('workbench.action.quickOpen');
|
||||
await this.waitForQuickOpenOpened();
|
||||
|
||||
if (value) {
|
||||
await this.spectron.client.setValue(QuickOpen.QUICK_OPEN_INPUT, value);
|
||||
}
|
||||
}
|
||||
|
||||
async closeQuickOpen(): Promise<void> {
|
||||
await this.spectron.runCommand('workbench.action.closeQuickOpen');
|
||||
await this.waitForQuickOpenClosed();
|
||||
}
|
||||
|
||||
async openFile(fileName: string): Promise<void> {
|
||||
await this.openQuickOpen(fileName);
|
||||
|
||||
await this.waitForQuickOpenElements(names => names.some(n => n === fileName));
|
||||
await this.spectron.client.keys(['Enter', 'NULL']);
|
||||
await this.spectron.workbench.waitForActiveTab(fileName);
|
||||
await this.spectron.workbench.waitForEditorFocus(fileName);
|
||||
}
|
||||
|
||||
async runCommand(commandText: string): Promise<void> {
|
||||
await this.openQuickOpen(`> ${commandText}`);
|
||||
|
||||
// wait for best choice to be focused
|
||||
await this.spectron.client.waitForTextContent(QuickOpen.QUICK_OPEN_FOCUSED_ELEMENT, commandText);
|
||||
|
||||
// wait and click on best choice
|
||||
await this.spectron.client.waitAndClick(QuickOpen.QUICK_OPEN_FOCUSED_ELEMENT);
|
||||
}
|
||||
|
||||
async waitForQuickOpenOpened(): Promise<void> {
|
||||
await this.spectron.client.waitForActiveElement(QuickOpen.QUICK_OPEN_INPUT);
|
||||
|
||||
// we gotta wait 50 milliseconds due to https://github.com/Microsoft/vscode/blob/master/src/vs/platform/list/browser/listService.ts#L59
|
||||
await new Promise(c => setTimeout(c, 50));
|
||||
}
|
||||
|
||||
private async waitForQuickOpenClosed(): Promise<void> {
|
||||
await this.spectron.client.waitForElement(QuickOpen.QUICK_OPEN_HIDDEN);
|
||||
}
|
||||
|
||||
async submit(text: string): Promise<void> {
|
||||
await this.spectron.client.setValue(QuickOpen.QUICK_OPEN_INPUT, text);
|
||||
await this.spectron.client.keys(['Enter', 'NULL']);
|
||||
await this.waitForQuickOpenClosed();
|
||||
}
|
||||
|
||||
async selectQuickOpenElement(index: number): Promise<void> {
|
||||
await this.waitForQuickOpenOpened();
|
||||
for (let from = 0; from < index; from++) {
|
||||
await this.spectron.client.keys(['ArrowDown', 'NULL']);
|
||||
}
|
||||
await this.spectron.client.keys(['Enter', 'NULL']);
|
||||
await this.waitForQuickOpenClosed();
|
||||
}
|
||||
|
||||
async waitForQuickOpenElements(accept: (names: string[]) => boolean): Promise<void> {
|
||||
await this.spectron.client.waitFor(() => this.getQuickOpenElements(), accept);
|
||||
}
|
||||
|
||||
private async getQuickOpenElements(): Promise<string[]> {
|
||||
const result = await this.spectron.webclient.selectorExecute(QuickOpen.QUICK_OPEN_ENTRY_SELECTOR,
|
||||
div => (Array.isArray(div) ? div : [div]).map(element => {
|
||||
const name = element.querySelector('.label-name') as HTMLElement;
|
||||
return name.textContent;
|
||||
})
|
||||
);
|
||||
|
||||
return Array.isArray(result) ? result : [];
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export class Search {
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public openSearchViewlet(): Promise<any> {
|
||||
return this.spectron.command('workbench.view.search');
|
||||
}
|
||||
|
||||
public async searchFor(text: string): Promise<any> {
|
||||
await this.spectron.client.keys(text);
|
||||
return this.spectron.client.keys(['NULL', 'Enter', 'NULL'], false);
|
||||
}
|
||||
|
||||
public setReplaceText(text: string): any {
|
||||
try {
|
||||
return this.spectron.client.setValue('.viewlet .input[title="Replace"]', text);
|
||||
} catch (e) {
|
||||
return Promise.reject('Cannot set replace input in the viewlet: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public replaceFirstMatch(): any {
|
||||
try {
|
||||
return this.spectron.client.click('.monaco-tree-rows.show-twisties .action-label.icon.action-replace-all');
|
||||
} catch (e) {
|
||||
return Promise.reject('Cannot replace the search first match: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public getResultText(): any {
|
||||
return this.spectron.waitFor(this.spectron.client.getText, '.search-viewlet .message>p');
|
||||
}
|
||||
|
||||
public toggleSearchDetails(): any {
|
||||
try {
|
||||
return this.spectron.client.click('.query-details .more');
|
||||
} catch (e) {
|
||||
return Promise.reject('Toggling search details failed: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public toggleReplace(): any {
|
||||
try {
|
||||
return this.spectron.client.click('.monaco-button.toggle-replace-button.collapse');
|
||||
} catch (e) {
|
||||
return Promise.reject('Toggling replace failed: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public hoverOverResultCount(): any {
|
||||
try {
|
||||
return this.spectron.waitFor(this.spectron.client.moveToObject, '.monaco-count-badge');
|
||||
} catch (e) {
|
||||
return Promise.reject('Hovering over result count failed: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public dismissResult(): any {
|
||||
try {
|
||||
return this.spectron.client.click('.action-label.icon.action-remove');
|
||||
} catch (e) {
|
||||
return Promise.reject('Clicking on dismissing result failed: ' + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
test/smoke/src/areas/search/search.test.ts
Normal file
56
test/smoke/src/areas/search/search.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
describe('Search', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'Search';
|
||||
});
|
||||
|
||||
it('searches for body & checks for correct result number', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.search.openSearchViewlet();
|
||||
await app.workbench.search.searchFor('body');
|
||||
|
||||
await app.workbench.search.waitForResultText('7 results in 4 files');
|
||||
});
|
||||
|
||||
it('searches only for *.js files & checks for correct result number', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.search.searchFor('body');
|
||||
await app.workbench.search.showQueryDetails();
|
||||
await app.workbench.search.setFilesToIncludeText('*.js');
|
||||
await app.workbench.search.submitSearch();
|
||||
|
||||
await app.workbench.search.waitForResultText('4 results in 1 file');
|
||||
await app.workbench.search.setFilesToIncludeText('');
|
||||
await app.workbench.search.hideQueryDetails();
|
||||
});
|
||||
|
||||
it('dismisses result & checks for correct result number', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.search.searchFor('body');
|
||||
await app.workbench.search.removeFileMatch(1);
|
||||
await app.workbench.search.waitForResultText('3 results in 3 files');
|
||||
});
|
||||
|
||||
it('replaces first search result with a replace term', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.search.searchFor('body');
|
||||
await app.workbench.search.expandReplace();
|
||||
await app.workbench.search.setReplaceText('ydob');
|
||||
await app.workbench.search.replaceFileMatch(1);
|
||||
await app.workbench.saveOpenedFile();
|
||||
|
||||
await app.workbench.search.waitForResultText('3 results in 3 files');
|
||||
|
||||
await app.workbench.search.searchFor('ydob');
|
||||
await app.workbench.search.setReplaceText('body');
|
||||
await app.workbench.search.replaceFileMatch(1);
|
||||
await app.workbench.saveOpenedFile();
|
||||
});
|
||||
});
|
||||
87
test/smoke/src/areas/search/search.ts
Normal file
87
test/smoke/src/areas/search/search.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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.search"] .search-viewlet';
|
||||
const INPUT = `${VIEWLET} .search-widget .search-container .monaco-inputbox input`;
|
||||
const INCLUDE_INPUT = `${VIEWLET} .query-details .monaco-inputbox input[aria-label="Search Include Patterns"]`;
|
||||
|
||||
export class Search extends Viewlet {
|
||||
|
||||
constructor(spectron: SpectronApplication) {
|
||||
super(spectron);
|
||||
}
|
||||
|
||||
async openSearchViewlet(): Promise<any> {
|
||||
await this.spectron.runCommand('workbench.view.search');
|
||||
await this.spectron.client.waitForActiveElement(INPUT);
|
||||
}
|
||||
|
||||
async searchFor(text: string): Promise<void> {
|
||||
await this.spectron.client.click(INPUT);
|
||||
await this.spectron.client.waitForActiveElement(INPUT);
|
||||
await this.spectron.client.setValue(INPUT, text);
|
||||
await this.submitSearch();
|
||||
}
|
||||
|
||||
async submitSearch(): Promise<void> {
|
||||
await this.spectron.client.click(INPUT);
|
||||
await this.spectron.client.waitForActiveElement(INPUT);
|
||||
|
||||
await this.spectron.client.keys(['Enter', 'NULL']);
|
||||
await this.spectron.client.element(`${VIEWLET} .messages[aria-hidden="false"]`);
|
||||
}
|
||||
|
||||
async setFilesToIncludeText(text: string): Promise<void> {
|
||||
await this.spectron.client.click(INCLUDE_INPUT);
|
||||
await this.spectron.client.waitForActiveElement(INCLUDE_INPUT);
|
||||
await this.spectron.client.setValue(INCLUDE_INPUT, text || '');
|
||||
}
|
||||
|
||||
async showQueryDetails(): Promise<void> {
|
||||
if (!await this.areDetailsVisible()) {
|
||||
await this.spectron.client.waitAndClick(`${VIEWLET} .query-details .more`);
|
||||
}
|
||||
}
|
||||
|
||||
async hideQueryDetails(): Promise<void> {
|
||||
if (await this.areDetailsVisible()) {
|
||||
await this.spectron.client.waitAndClick(`${VIEWLET} .query-details.more .more`);
|
||||
}
|
||||
}
|
||||
|
||||
async areDetailsVisible(): Promise<boolean> {
|
||||
const element = await this.spectron.client.element(`${VIEWLET} .query-details.more`);
|
||||
return !!element;
|
||||
}
|
||||
|
||||
async removeFileMatch(index: number): Promise<void> {
|
||||
await this.spectron.client.waitAndMoveToObject(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch`);
|
||||
const file = await this.spectron.client.waitForText(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`);
|
||||
await this.spectron.client.waitAndClick(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-remove`);
|
||||
await this.spectron.client.waitForText(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`, void 0, result => result !== file);
|
||||
}
|
||||
|
||||
async expandReplace(): Promise<void> {
|
||||
await this.spectron.client.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.collapse`);
|
||||
}
|
||||
|
||||
async setReplaceText(text: string): Promise<void> {
|
||||
await this.spectron.client.waitAndClick(`${VIEWLET} .search-widget .replace-container .monaco-inputbox input[title="Replace"]`);
|
||||
await this.spectron.client.element(`${VIEWLET} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`);
|
||||
await this.spectron.client.setValue(`${VIEWLET} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`, text);
|
||||
}
|
||||
|
||||
async replaceFileMatch(index: number): Promise<void> {
|
||||
await this.spectron.client.waitAndMoveToObject(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch`);
|
||||
await this.spectron.client.click(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-replace-all`);
|
||||
}
|
||||
|
||||
async waitForResultText(text: string): Promise<void> {
|
||||
await this.spectron.client.waitForText(`${VIEWLET} .messages[aria-hidden="false"] .message>p`, text);
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export enum StatusBarElement {
|
||||
BRANCH_STATUS = 0,
|
||||
SYNC_STATUS = 1,
|
||||
PROBLEMS_STATUS = 2,
|
||||
SELECTION_STATUS = 3,
|
||||
INDENTATION_STATUS = 4,
|
||||
ENCODING_STATUS = 5,
|
||||
EOL_STATUS = 6,
|
||||
LANGUAGE_STATUS = 7,
|
||||
FEEDBACK_ICON = 8
|
||||
}
|
||||
|
||||
export class StatusBar {
|
||||
|
||||
private selectorsMap: Map<StatusBarElement, string>;
|
||||
private readonly mainSelector = 'div[id="workbench.parts.statusbar"]';
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
this.populateSelectorsMap();
|
||||
}
|
||||
|
||||
public async isVisible(element: StatusBarElement): Promise<boolean> {
|
||||
const selector = this.selectorsMap.get(element);
|
||||
if (!selector) {
|
||||
throw new Error('No such element in the status bar defined.');
|
||||
}
|
||||
|
||||
return this.spectron.client.isVisible(selector);
|
||||
}
|
||||
|
||||
public async clickOn(element: StatusBarElement): Promise<any> {
|
||||
const selector = this.selectorsMap.get(element);
|
||||
if (!selector) {
|
||||
throw new Error('No such element in the status bar defined.');
|
||||
}
|
||||
|
||||
try {
|
||||
return this.spectron.client.click(selector);
|
||||
} catch (e) {
|
||||
return Promise.reject(`Clicking on status bar element ${selector} failed.`);
|
||||
}
|
||||
}
|
||||
|
||||
public async getProblemsView(): Promise<any> {
|
||||
let el = await this.spectron.client.element('div[id="workbench.panel.markers"]');
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async getFeedbackView(): Promise<any> {
|
||||
let el = await this.spectron.client.element('.feedback-form');
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public isQuickOpenWidgetVisible(): Promise<any> {
|
||||
return this.spectron.client.isVisible('.quick-open-widget');
|
||||
}
|
||||
|
||||
public async getEditorHighlightedLine(lineNumber: number): Promise<any> {
|
||||
let el = await this.spectron.client.element(`.monaco-editor .view-overlays>:nth-child(${lineNumber}) .current-line`);
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async getEOLMode(): Promise<any> {
|
||||
const selector = this.selectorsMap.get(StatusBarElement.EOL_STATUS);
|
||||
if (!selector) {
|
||||
throw new Error('No such element in the status bar defined.');
|
||||
}
|
||||
|
||||
return this.spectron.client.getText(selector);
|
||||
}
|
||||
|
||||
private populateSelectorsMap(): void {
|
||||
this.selectorsMap = new Map<StatusBarElement, string>();
|
||||
this.selectorsMap.set(StatusBarElement.BRANCH_STATUS, `${this.mainSelector} .octicon.octicon-git-branch`);
|
||||
this.selectorsMap.set(StatusBarElement.SYNC_STATUS, `${this.mainSelector} .octicon.octicon-sync`);
|
||||
this.selectorsMap.set(StatusBarElement.PROBLEMS_STATUS, `${this.mainSelector} .task-statusbar-item[title="Problems"]`);
|
||||
this.selectorsMap.set(StatusBarElement.SELECTION_STATUS, `${this.mainSelector} .editor-status-selection`);
|
||||
this.selectorsMap.set(StatusBarElement.INDENTATION_STATUS, `${this.mainSelector} .editor-status-indentation`);
|
||||
this.selectorsMap.set(StatusBarElement.ENCODING_STATUS, `${this.mainSelector} .editor-status-encoding`);
|
||||
this.selectorsMap.set(StatusBarElement.EOL_STATUS, `${this.mainSelector} .editor-status-eol`);
|
||||
this.selectorsMap.set(StatusBarElement.LANGUAGE_STATUS, `${this.mainSelector} .editor-status-mode`);
|
||||
this.selectorsMap.set(StatusBarElement.FEEDBACK_ICON, `${this.mainSelector} .dropdown.send-feedback`);
|
||||
}
|
||||
}
|
||||
97
test/smoke/src/areas/statusbar/statusbar.test.ts
Normal file
97
test/smoke/src/areas/statusbar/statusbar.test.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, Quality } from '../../spectron/application';
|
||||
import { StatusBarElement } from './statusbar';
|
||||
|
||||
describe('Statusbar', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'Statusbar';
|
||||
});
|
||||
|
||||
it('verifies presence of all default status bar elements', async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.BRANCH_STATUS);
|
||||
if (app.quality !== Quality.Dev) {
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.FEEDBACK_ICON);
|
||||
}
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.SYNC_STATUS);
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.PROBLEMS_STATUS);
|
||||
|
||||
await app.workbench.quickopen.openFile('app.js');
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.ENCODING_STATUS);
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.EOL_STATUS);
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.INDENTATION_STATUS);
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.LANGUAGE_STATUS);
|
||||
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.SELECTION_STATUS);
|
||||
});
|
||||
|
||||
it(`verifies that 'quick open' opens when clicking on status bar elements`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.BRANCH_STATUS);
|
||||
await app.workbench.quickopen.waitForQuickOpenOpened();
|
||||
await app.workbench.quickopen.closeQuickOpen();
|
||||
|
||||
await app.workbench.quickopen.openFile('app.js');
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.INDENTATION_STATUS);
|
||||
await app.workbench.quickopen.waitForQuickOpenOpened();
|
||||
await app.workbench.quickopen.closeQuickOpen();
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.ENCODING_STATUS);
|
||||
await app.workbench.quickopen.waitForQuickOpenOpened();
|
||||
await app.workbench.quickopen.closeQuickOpen();
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS);
|
||||
await app.workbench.quickopen.waitForQuickOpenOpened();
|
||||
await app.workbench.quickopen.closeQuickOpen();
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.LANGUAGE_STATUS);
|
||||
await app.workbench.quickopen.waitForQuickOpenOpened();
|
||||
await app.workbench.quickopen.closeQuickOpen();
|
||||
});
|
||||
|
||||
it(`verifies that 'Problems View' appears when clicking on 'Problems' status element`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.PROBLEMS_STATUS);
|
||||
await app.workbench.problems.waitForProblemsView();
|
||||
});
|
||||
|
||||
it(`verifies that 'Tweet us feedback' pop-up appears when clicking on 'Feedback' icon`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
if (app.quality === Quality.Dev) {
|
||||
return this.skip();
|
||||
}
|
||||
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.FEEDBACK_ICON);
|
||||
assert.ok(!!await app.client.waitForElement('.feedback-form'));
|
||||
});
|
||||
|
||||
it(`checks if 'Go to Line' works if called from the status bar`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.quickopen.openFile('app.js');
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.SELECTION_STATUS);
|
||||
|
||||
await app.workbench.quickopen.waitForQuickOpenOpened();
|
||||
|
||||
await app.workbench.quickopen.submit(':15');
|
||||
await app.workbench.editor.waitForHighlightingLine(15);
|
||||
});
|
||||
|
||||
it(`verifies if changing EOL is reflected in the status bar`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
await app.workbench.quickopen.openFile('app.js');
|
||||
await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS);
|
||||
|
||||
await app.workbench.quickopen.waitForQuickOpenOpened();
|
||||
await app.workbench.quickopen.selectQuickOpenElement(1);
|
||||
|
||||
await app.workbench.statusbar.waitForEOL('CRLF');
|
||||
});
|
||||
});
|
||||
69
test/smoke/src/areas/statusbar/statusbar.ts
Normal file
69
test/smoke/src/areas/statusbar/statusbar.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export enum StatusBarElement {
|
||||
BRANCH_STATUS = 0,
|
||||
SYNC_STATUS = 1,
|
||||
PROBLEMS_STATUS = 2,
|
||||
SELECTION_STATUS = 3,
|
||||
INDENTATION_STATUS = 4,
|
||||
ENCODING_STATUS = 5,
|
||||
EOL_STATUS = 6,
|
||||
LANGUAGE_STATUS = 7,
|
||||
FEEDBACK_ICON = 8
|
||||
}
|
||||
|
||||
export class StatusBar {
|
||||
|
||||
private readonly mainSelector = 'div[id="workbench.parts.statusbar"]';
|
||||
private readonly leftSelector = '.statusbar-item.left';
|
||||
private readonly rightSelector = '.statusbar-item.right';
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
}
|
||||
|
||||
public async waitForStatusbarElement(element: StatusBarElement): Promise<void> {
|
||||
await this.spectron.client.waitForElement(this.getSelector(element));
|
||||
}
|
||||
|
||||
public async clickOn(element: StatusBarElement): Promise<void> {
|
||||
await this.spectron.client.waitAndClick(this.getSelector(element));
|
||||
}
|
||||
|
||||
public async waitForEOL(eol: string): Promise<string> {
|
||||
return this.spectron.client.waitForText(this.getSelector(StatusBarElement.EOL_STATUS), eol);
|
||||
}
|
||||
|
||||
public async getStatusbarTextByTitle(title: string): Promise<string> {
|
||||
return await this.spectron.client.waitForText(`${this.mainSelector} span[title="smoke test"]`);
|
||||
}
|
||||
|
||||
private getSelector(element: StatusBarElement): string {
|
||||
switch (element) {
|
||||
case StatusBarElement.BRANCH_STATUS:
|
||||
return `${this.mainSelector} ${this.leftSelector} .octicon.octicon-git-branch`;
|
||||
case StatusBarElement.SYNC_STATUS:
|
||||
return `${this.mainSelector} ${this.leftSelector} .octicon.octicon-sync`;
|
||||
case StatusBarElement.PROBLEMS_STATUS:
|
||||
return `${this.mainSelector} ${this.leftSelector} .task-statusbar-item[title="Problems"]`;
|
||||
case StatusBarElement.SELECTION_STATUS:
|
||||
return `${this.mainSelector} ${this.rightSelector} .editor-status-selection`;
|
||||
case StatusBarElement.INDENTATION_STATUS:
|
||||
return `${this.mainSelector} ${this.rightSelector} .editor-status-indentation`;
|
||||
case StatusBarElement.ENCODING_STATUS:
|
||||
return `${this.mainSelector} ${this.rightSelector} .editor-status-encoding`;
|
||||
case StatusBarElement.EOL_STATUS:
|
||||
return `${this.mainSelector} ${this.rightSelector} .editor-status-eol`;
|
||||
case StatusBarElement.LANGUAGE_STATUS:
|
||||
return `${this.mainSelector} ${this.rightSelector} .editor-status-mode`;
|
||||
case StatusBarElement.FEEDBACK_ICON:
|
||||
return `${this.mainSelector} ${this.rightSelector} .dropdown.send-feedback`;
|
||||
default:
|
||||
throw new Error(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IntegratedTerminal } from './integrated-terminal';
|
||||
|
||||
export class Tasks {
|
||||
|
||||
private readonly outputViewSelector = IntegratedTerminal.terminalRowsSelector;
|
||||
private readonly workbenchPanelSelector = 'div[id="workbench.parts.panel"]';
|
||||
private readonly problemsViewSelector = 'div[id="workbench.panel.markers"] .monaco-tree-row.expanded';
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public async build(): Promise<any> {
|
||||
await this.spectron.command('workbench.action.tasks.build');
|
||||
await this.spectron.wait(); // wait for build to finish
|
||||
|
||||
// Validate that it has finished
|
||||
let trial = 0;
|
||||
while (trial < 3) {
|
||||
// Determine build status based on the statusbar indicator, don't continue until task has been terminated
|
||||
try {
|
||||
return await this.spectron.client.getValue('.task-statusbar-item-progress.builder-hidden');
|
||||
} catch (e) {
|
||||
await this.spectron.wait();
|
||||
trial++;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject('Could not determine if the task was terminated based on status bar progress spinner.');
|
||||
}
|
||||
|
||||
public openProblemsView(): Promise<any> {
|
||||
return this.spectron.command('workbench.actions.view.problems');
|
||||
}
|
||||
|
||||
public async outputContains(string: string): Promise<boolean> {
|
||||
const output: string = await this.spectron.waitFor(this.spectron.client.getText, this.outputViewSelector);
|
||||
|
||||
if (output.indexOf(string) !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async selectOutputViewType(type: string): Promise<any> {
|
||||
await this.openOutputView();
|
||||
|
||||
try {
|
||||
return this.spectron.client.selectByValue(`${this.workbenchPanelSelector} .select-box`, type);
|
||||
} catch (e) {
|
||||
return Promise.reject(`Failed to select ${type} as workbench panel output.`);
|
||||
}
|
||||
}
|
||||
|
||||
public getOutputViewType(): Promise<any> {
|
||||
return this.spectron.client.getValue(`${this.workbenchPanelSelector} .select-box`);
|
||||
}
|
||||
|
||||
public getProblemsViewFirstElementName(): Promise<any> {
|
||||
try {
|
||||
return this.spectron.waitFor(this.spectron.client.getText, `${this.problemsViewSelector} .label-name`);
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to get problem label from Problems view: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
public getProblemsViewFirstElementCount(): Promise<any> {
|
||||
try {
|
||||
return this.spectron.waitFor(this.spectron.client.getText, `${this.problemsViewSelector} .monaco-count-badge`);
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to get problem count from Problems view: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
private openOutputView(): Promise<any> {
|
||||
try {
|
||||
return this.spectron.command('workbench.action.output.toggleOutput');
|
||||
} catch (e) {
|
||||
return Promise.reject('Failed to toggle output view');
|
||||
}
|
||||
}
|
||||
}
|
||||
29
test/smoke/src/areas/terminal/terminal.test.ts
Normal file
29
test/smoke/src/areas/terminal/terminal.test.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
describe('Terminal', () => {
|
||||
// let app: SpectronApplication;
|
||||
// before(() => { app = new SpectronApplication(); return app.start('Terminal'); });
|
||||
// after(() => app.stop());
|
||||
|
||||
// it(`opens terminal, runs 'echo' and verifies the output`, async function () {
|
||||
// const expected = new Date().getTime().toString();
|
||||
// await app.workbench.terminal.showTerminal();
|
||||
|
||||
// await app.workbench.terminal.runCommand(`echo ${expected}`);
|
||||
|
||||
// await app.workbench.terminal.waitForTerminalText(terminalText => {
|
||||
// // Last line will not contain the output
|
||||
// for (let index = terminalText.length - 2; index >= 0; index--) {
|
||||
// if (!!terminalText[index] && terminalText[index].trim() === expected) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// });
|
||||
// });
|
||||
});
|
||||
61
test/smoke/src/areas/terminal/terminal.ts
Normal file
61
test/smoke/src/areas/terminal/terminal.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
const PANEL_SELECTOR = 'div[id="workbench.panel.terminal"]';
|
||||
const XTERM_SELECTOR = `${PANEL_SELECTOR} .terminal-wrapper`;
|
||||
|
||||
export class Terminal {
|
||||
|
||||
constructor(private spectron: SpectronApplication) { }
|
||||
|
||||
async showTerminal(): Promise<void> {
|
||||
if (!await this.isVisible()) {
|
||||
await this.spectron.workbench.quickopen.runCommand('View: Toggle Integrated Terminal');
|
||||
await this.spectron.client.waitForElement(XTERM_SELECTOR);
|
||||
await this.waitForTerminalText(text => text.length > 0, 'Waiting for Terminal to be ready');
|
||||
}
|
||||
}
|
||||
|
||||
async isVisible(): Promise<boolean> {
|
||||
const element = await this.spectron.client.element(PANEL_SELECTOR);
|
||||
return !!element;
|
||||
}
|
||||
|
||||
async runCommand(commandText: string): Promise<void> {
|
||||
// TODO@Tyriar fix this. we should not use type but setValue
|
||||
// await this.spectron.client.type(commandText);
|
||||
await this.spectron.client.keys(['Enter', 'NULL']);
|
||||
}
|
||||
|
||||
async waitForTerminalText(fn: (text: string[]) => boolean, timeOutDescription: string = 'Getting Terminal Text'): Promise<string[]> {
|
||||
return this.spectron.client.waitFor(async () => {
|
||||
const terminalText = await this.getTerminalText();
|
||||
if (fn(terminalText)) {
|
||||
return terminalText;
|
||||
}
|
||||
return undefined;
|
||||
}, void 0, timeOutDescription);
|
||||
}
|
||||
|
||||
getCurrentLineNumber(): Promise<number> {
|
||||
return this.getTerminalText().then(text => text.length);
|
||||
}
|
||||
|
||||
private async getTerminalText(): Promise<string[]> {
|
||||
return await this.spectron.webclient.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;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
40
test/smoke/src/areas/workbench/data-loss.test.ts
Normal file
40
test/smoke/src/areas/workbench/data-loss.test.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
describe('Dataloss', () => {
|
||||
before(function () {
|
||||
this.app.suiteName = 'Dataloss';
|
||||
});
|
||||
|
||||
it(`verifies that 'hot exit' works for dirty files`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
await app.workbench.newUntitledFile();
|
||||
|
||||
const untitled = 'Untitled-1';
|
||||
const textToTypeInUntitled = 'Hello, Unitled Code';
|
||||
await app.workbench.editor.waitForTypeInEditor(untitled, textToTypeInUntitled);
|
||||
await app.screenCapturer.capture('Untitled file before reload');
|
||||
|
||||
const readmeMd = 'readme.md';
|
||||
const textToType = 'Hello, Code';
|
||||
await app.workbench.explorer.openFile(readmeMd);
|
||||
await app.workbench.editor.waitForTypeInEditor(readmeMd, textToType);
|
||||
await app.screenCapturer.capture(`${readmeMd} before reload`);
|
||||
|
||||
await app.reload();
|
||||
await app.screenCapturer.capture('After reload');
|
||||
|
||||
await app.workbench.waitForActiveTab(readmeMd, true);
|
||||
await app.screenCapturer.capture(`${readmeMd} after reload`);
|
||||
await app.workbench.editor.waitForEditorContents(readmeMd, c => c.indexOf(textToType) > -1);
|
||||
|
||||
await app.workbench.waitForTab(untitled, true);
|
||||
await app.workbench.selectTab(untitled, true);
|
||||
await app.screenCapturer.capture('Untitled file after reload');
|
||||
await app.workbench.editor.waitForEditorContents(untitled, c => c.indexOf(textToTypeInUntitled) > -1);
|
||||
});
|
||||
});
|
||||
87
test/smoke/src/areas/workbench/data-migration.test.ts
Normal file
87
test/smoke/src/areas/workbench/data-migration.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, STABLE_PATH, LATEST_PATH } from '../../spectron/application';
|
||||
// import { Util } from '../../helpers/utilities';
|
||||
|
||||
// describe('Data Migration', () => {
|
||||
|
||||
// if (!STABLE_PATH) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// let app: SpectronApplication;
|
||||
// afterEach(() => app.stop());
|
||||
|
||||
// it('checks if the Untitled file is restored migrating from stable to latest', async function () {
|
||||
// const textToType = 'Very dirty file';
|
||||
|
||||
// // Setting up stable version
|
||||
// let app = new SpectronApplication(STABLE_PATH);
|
||||
// await app.start('Data Migration');
|
||||
|
||||
// await app.workbench.newUntitledFile();
|
||||
// await app.workbench.editor.waitForTypeInEditor('Untitled-1', textToType);
|
||||
|
||||
// await app.stop();
|
||||
// await new Promise(c => setTimeout(c, 500)); // wait until all resources are released (e.g. locked local storage)
|
||||
// // Checking latest version for the restored state
|
||||
|
||||
// app = new SpectronApplication(LATEST_PATH);
|
||||
// await app.start('Data Migration');
|
||||
|
||||
// assert.ok(await app.workbench.waitForActiveTab('Untitled-1', true), `Untitled-1 tab is not present after migration.`);
|
||||
|
||||
// await app.workbench.editor.waitForEditorContents('Untitled-1', c => c.indexOf(textToType) > -1);
|
||||
// await app.screenCapturer.capture('Untitled file text');
|
||||
// });
|
||||
|
||||
// it('checks if the newly created dirty file is restored migrating from stable to latest', async function () {
|
||||
// const fileName = 'test_data/plainFile',
|
||||
// firstTextPart = 'This is going to be an unsaved file', secondTextPart = '_that is dirty.';
|
||||
|
||||
// // Setting up stable version
|
||||
// let app = new SpectronApplication(STABLE_PATH, fileName);
|
||||
// await Util.removeFile(`${fileName}`);
|
||||
// await app.start('Data Migration');
|
||||
|
||||
// await app.workbench.editor.waitForTypeInEditor('plainFile', firstTextPart);
|
||||
// await app.workbench.saveOpenedFile();
|
||||
// await app.workbench.editor.waitForTypeInEditor('plainFile', secondTextPart);
|
||||
|
||||
// await app.stop();
|
||||
// await new Promise(c => setTimeout(c, 1000)); // wait until all resources are released (e.g. locked local storage)
|
||||
|
||||
// // Checking latest version for the restored state
|
||||
// app = new SpectronApplication(LATEST_PATH);
|
||||
// await app.start('Data Migration');
|
||||
|
||||
// const filename = fileName.split('/')[1];
|
||||
// assert.ok(await app.workbench.waitForActiveTab(filename), `Untitled-1 tab is not present after migration.`);
|
||||
// await app.workbench.editor.waitForEditorContents(filename, c => c.indexOf(firstTextPart + secondTextPart) > -1);
|
||||
|
||||
// await Util.removeFile(`${fileName}`);
|
||||
// });
|
||||
|
||||
// it('cheks if opened tabs are restored migrating from stable to latest', async function () {
|
||||
// const fileName1 = 'app.js', fileName2 = 'jsconfig.json', fileName3 = 'readme.md';
|
||||
// let app = new SpectronApplication(STABLE_PATH);
|
||||
// await app.start('Data Migration');
|
||||
|
||||
// await app.workbench.quickopen.openFile(fileName1);
|
||||
// await app.workbench.quickopen.openFile(fileName2);
|
||||
// await app.workbench.quickopen.openFile(fileName3);
|
||||
// await app.stop();
|
||||
|
||||
// app = new SpectronApplication(LATEST_PATH);
|
||||
// await app.start('Data Migration');
|
||||
|
||||
// assert.ok(await app.workbench.waitForTab(fileName1), `${fileName1} tab was not restored after migration.`);
|
||||
// assert.ok(await app.workbench.waitForTab(fileName2), `${fileName2} tab was not restored after migration.`);
|
||||
// assert.ok(await app.workbench.waitForTab(fileName3), `${fileName3} tab was not restored after migration.`);
|
||||
// });
|
||||
// });
|
||||
54
test/smoke/src/areas/workbench/localization.test.ts
Normal file
54
test/smoke/src/areas/workbench/localization.test.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, Quality } from '../../spectron/application';
|
||||
|
||||
describe('Localization', () => {
|
||||
before(async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
this.app.suiteName = 'Localization';
|
||||
|
||||
if (app.quality === Quality.Dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
await app.restart(['--locale=DE']);
|
||||
});
|
||||
|
||||
it(`starts with 'DE' locale and verifies title and viewlets text is in German`, async function () {
|
||||
const app = this.app as SpectronApplication;
|
||||
|
||||
if (app.quality === Quality.Dev) {
|
||||
this.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
let text = await app.workbench.explorer.getOpenEditorsViewTitle();
|
||||
await app.screenCapturer.capture('Open editors title');
|
||||
assert(/geöffnete editoren/i.test(text));
|
||||
|
||||
await app.workbench.search.openSearchViewlet();
|
||||
text = await app.workbench.search.getTitle();
|
||||
await app.screenCapturer.capture('Search title');
|
||||
assert(/suchen/i.test(text));
|
||||
|
||||
await app.workbench.scm.openSCMViewlet();
|
||||
text = await app.workbench.scm.getTitle();
|
||||
await app.screenCapturer.capture('Scm title');
|
||||
assert(/quellcodeverwaltung/i.test(text));
|
||||
|
||||
await app.workbench.debug.openDebugViewlet();
|
||||
text = await app.workbench.debug.getTitle();
|
||||
await app.screenCapturer.capture('Debug title');
|
||||
assert(/debuggen/i.test(text));
|
||||
|
||||
await app.workbench.extensions.openExtensionsViewlet();
|
||||
text = await app.workbench.extensions.getTitle();
|
||||
await app.screenCapturer.capture('Extensions title');
|
||||
assert(/erweiterungen/i.test(text));
|
||||
});
|
||||
});
|
||||
@@ -3,19 +3,16 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SpectronApplication } from '../spectron/application';
|
||||
import { SpectronApplication } from '../../spectron/application';
|
||||
|
||||
export class FirstExperience {
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
export abstract class Viewlet {
|
||||
|
||||
constructor(protected spectron: SpectronApplication) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public async getWelcomeTab(): Promise<any> {
|
||||
let el = await this.spectron.client.element('.vs_code_welcome_page-name-file-icon');
|
||||
if (el.status === 0) {
|
||||
return el;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
public async getTitle(): Promise<string> {
|
||||
return this.spectron.client.waitForText('.monaco-workbench-container .part.sidebar > .title > .title-label > span');
|
||||
}
|
||||
|
||||
}
|
||||
86
test/smoke/src/areas/workbench/workbench.ts
Normal file
86
test/smoke/src/areas/workbench/workbench.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Explorer } from '../explorer/explorer';
|
||||
import { ActivityBar } from '../activitybar/activityBar';
|
||||
import { QuickOpen } from '../quickopen/quickopen';
|
||||
import { Extensions } from '../extensions/extensions';
|
||||
import { Search } from '../search/search';
|
||||
import { Editor } from '../editor/editor';
|
||||
import { SCM } from '../git/scm';
|
||||
import { Debug } from '../debug/debug';
|
||||
import { StatusBar } from '../statusbar/statusbar';
|
||||
import { Problems } from '../problems/problems';
|
||||
import { SettingsEditor } from '../preferences/settings';
|
||||
import { KeybindingsEditor } from '../preferences/keybindings';
|
||||
import { Terminal } from '../terminal/terminal';
|
||||
|
||||
export class Workbench {
|
||||
|
||||
readonly explorer: Explorer;
|
||||
readonly activitybar: ActivityBar;
|
||||
readonly quickopen: QuickOpen;
|
||||
readonly search: Search;
|
||||
readonly extensions: Extensions;
|
||||
readonly editor: Editor;
|
||||
readonly scm: SCM;
|
||||
readonly debug: Debug;
|
||||
readonly statusbar: StatusBar;
|
||||
readonly problems: Problems;
|
||||
readonly settingsEditor: SettingsEditor;
|
||||
readonly keybindingsEditor: KeybindingsEditor;
|
||||
readonly terminal: Terminal;
|
||||
|
||||
constructor(private spectron: SpectronApplication) {
|
||||
this.explorer = new Explorer(spectron);
|
||||
this.activitybar = new ActivityBar(spectron);
|
||||
this.quickopen = new QuickOpen(spectron);
|
||||
this.search = new Search(spectron);
|
||||
this.extensions = new Extensions(spectron);
|
||||
this.editor = new Editor(spectron);
|
||||
this.scm = new SCM(spectron);
|
||||
this.debug = new Debug(spectron);
|
||||
this.statusbar = new StatusBar(spectron);
|
||||
this.problems = new Problems(spectron);
|
||||
this.settingsEditor = new SettingsEditor(spectron);
|
||||
this.keybindingsEditor = new KeybindingsEditor(spectron);
|
||||
this.terminal = new Terminal(spectron);
|
||||
}
|
||||
|
||||
public async saveOpenedFile(): Promise<any> {
|
||||
try {
|
||||
await this.spectron.client.waitForElement('.tabs-container div.tab.active.dirty');
|
||||
} catch (e) {
|
||||
// ignore if there is no dirty file
|
||||
return Promise.resolve();
|
||||
}
|
||||
await this.spectron.runCommand('workbench.action.files.save');
|
||||
return this.spectron.client.waitForElement('.tabs-container div.tab.active.dirty', element => !element);
|
||||
}
|
||||
|
||||
public async selectTab(tabName: string, untitled: boolean = false): Promise<void> {
|
||||
await this.spectron.client.waitAndClick(`.tabs-container div.tab[aria-label="${tabName}, tab"]`);
|
||||
await this.waitForEditorFocus(tabName, untitled);
|
||||
}
|
||||
|
||||
public async waitForEditorFocus(fileName: string, untitled: boolean = false): Promise<void> {
|
||||
await this.waitForActiveTab(fileName);
|
||||
await this.editor.waitForActiveEditor(fileName);
|
||||
}
|
||||
|
||||
public async waitForActiveTab(fileName: string, isDirty: boolean = false): Promise<any> {
|
||||
return this.spectron.client.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][aria-label="${fileName}, tab"]`);
|
||||
}
|
||||
|
||||
public async waitForTab(fileName: string, isDirty: boolean = false): Promise<boolean> {
|
||||
return this.spectron.client.waitForElement(`.tabs-container div.tab${isDirty ? '.dirty' : ''}[aria-label="${fileName}, tab"]`).then(() => true);
|
||||
}
|
||||
|
||||
public async newUntitledFile(): Promise<void> {
|
||||
await this.spectron.runCommand('workbench.action.files.newUntitledFile');
|
||||
await this.waitForEditorFocus('Untitled-1', true);
|
||||
}
|
||||
}
|
||||
@@ -3,38 +3,35 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SpectronApplication } from '../spectron/application';
|
||||
var fs = require('fs');
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
import { Application } from 'spectron';
|
||||
import { sanitize } from './utilities';
|
||||
|
||||
const __testTime = new Date().toISOString();
|
||||
export class ScreenCapturer {
|
||||
|
||||
export class Screenshot {
|
||||
private index: number = 0;
|
||||
private testPath: string;
|
||||
private static counter = 0;
|
||||
|
||||
constructor(private spectron: SpectronApplication, testName: string, testRetry: number) {
|
||||
const testTime = this.sanitizeFolderName(__testTime);
|
||||
testName = this.sanitizeFolderName(testName);
|
||||
constructor(
|
||||
private application: Application,
|
||||
public suiteName: string,
|
||||
private screenshotsDirPath: string | undefined,
|
||||
) { }
|
||||
|
||||
this.testPath = `test_data/screenshots/${testTime}/${testName}/${testRetry}`;
|
||||
this.createFolder(this.testPath);
|
||||
async capture(name: string): Promise<void> {
|
||||
if (!this.screenshotsDirPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const screenshotPath = path.join(
|
||||
this.screenshotsDirPath,
|
||||
sanitize(this.suiteName),
|
||||
`${ScreenCapturer.counter++}-${sanitize(name)}.png`
|
||||
);
|
||||
|
||||
const image = await this.application.browserWindow.capturePage();
|
||||
await new Promise((c, e) => mkdirp(path.dirname(screenshotPath), err => err ? e(err) : c()));
|
||||
await new Promise((c, e) => fs.writeFile(screenshotPath, image, err => err ? e(err) : c()));
|
||||
}
|
||||
|
||||
public async capture(): Promise<any> {
|
||||
const image = await this.spectron.app.browserWindow.capturePage();
|
||||
await new Promise((c, e) => fs.writeFile(`${this.testPath}/${this.index++}.png`, image, err => err ? e(err) : c()));
|
||||
}
|
||||
|
||||
private createFolder(name: string): void {
|
||||
name.split('/').forEach((folderName, i, fullPath) => {
|
||||
const folder = fullPath.slice(0, i + 1).join('/');
|
||||
if (!fs.existsSync(folder)) {
|
||||
fs.mkdirSync(folder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private sanitizeFolderName(name: string): string {
|
||||
return name.replace(/[&*:\/]/g, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
var fs = require('fs');
|
||||
var rimraf = require('rimraf');
|
||||
import * as fs from 'fs';
|
||||
import { dirname } from 'path';
|
||||
|
||||
/**
|
||||
* Contains methods that are commonly used across test areas.
|
||||
@@ -24,14 +24,59 @@ export class Util {
|
||||
}
|
||||
}
|
||||
|
||||
public rimraf(directory: string): Promise<any> {
|
||||
return new Promise((res, rej) => {
|
||||
rimraf(directory, (err) => {
|
||||
if (err) {
|
||||
rej(err);
|
||||
}
|
||||
res();
|
||||
});
|
||||
});
|
||||
public static removeFile(filePath: string): void {
|
||||
try {
|
||||
fs.unlinkSync(`${filePath}`);
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function nfcall<R>(fn: Function, ...args): Promise<R> {
|
||||
return new Promise<R>((c, e) => fn(...args, (err, r) => err ? e(err) : c(r)));
|
||||
}
|
||||
|
||||
export async function mkdirp(path: string, mode?: number): Promise<boolean> {
|
||||
const mkdir = async () => {
|
||||
try {
|
||||
await nfcall(fs.mkdir, path, mode);
|
||||
} catch (err) {
|
||||
if (err.code === 'EEXIST') {
|
||||
const stat = await nfcall<fs.Stats>(fs.stat, path);
|
||||
|
||||
if (stat.isDirectory) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(`'${path}' exists and is not a directory.`);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
// is root?
|
||||
if (path === dirname(path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
await mkdir();
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
await mkdirp(dirname(path), mode);
|
||||
await mkdir();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function sanitize(name: string): string {
|
||||
return name.replace(/[&*:\/]/g, '');
|
||||
}
|
||||
@@ -3,220 +3,242 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const program = require('commander');
|
||||
const git = require('simple-git')();
|
||||
const child_process = require('child_process');
|
||||
const path = require('path');
|
||||
const mkdirp = require('mkdirp');
|
||||
import * as fs from 'fs';
|
||||
import * as https from 'https';
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as minimist from 'minimist';
|
||||
import * as tmp from 'tmp';
|
||||
import * as rimraf from 'rimraf';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
import { SpectronApplication, Quality } from './spectron/application';
|
||||
|
||||
const testDataPath = path.join(process.cwd(), 'test_data');
|
||||
const codeWorkspacePath = path.join(testDataPath, 'smoketest.code-workspace');
|
||||
const testRepoUrl = 'https://github.com/Microsoft/vscode-smoketest-express';
|
||||
const testRepoLocalDir = path.join(testDataPath, 'vscode-smoketest-express');
|
||||
const keybindingsUrl = 'https://raw.githubusercontent.com/Microsoft/vscode-docs/master/scripts/keybindings';
|
||||
const tmpDir = tmp.dirSync({ prefix: 't' }) as { name: string; removeCallback: Function; };
|
||||
const testDataPath = tmpDir.name;
|
||||
process.once('exit', () => rimraf.sync(testDataPath));
|
||||
|
||||
mkdirp.sync(testDataPath);
|
||||
|
||||
program
|
||||
.option('-l, --latest <file path>', 'path to the latest VS Code to test')
|
||||
.option('-s, --stable [file path]', 'path to the stable VS Code to be used in data migration tests');
|
||||
|
||||
program.on('--help', () => {
|
||||
console.log(' Examples:');
|
||||
console.log('');
|
||||
console.log(' $ npm test -- --latest path/to/binary');
|
||||
console.log(' $ npm test -- -l path/to/binary');
|
||||
console.log('');
|
||||
console.log(' $ npm test -- --latest path/to/latest/binary --stable path/to/stable/binary');
|
||||
console.log(' $ npm test -- -l path/to/latest/binary -s path/to/stable/binary');
|
||||
console.log('');
|
||||
const [, , ...args] = process.argv;
|
||||
const opts = minimist(args, {
|
||||
string: [
|
||||
'build',
|
||||
'stable-build',
|
||||
'log',
|
||||
'wait-time'
|
||||
]
|
||||
});
|
||||
program.parse(process.argv);
|
||||
|
||||
if (!program.latest) {
|
||||
fail('You must specify the binary to run the smoke test against');
|
||||
}
|
||||
if (!binaryExists(program.latest) || (program.stable && !binaryExists(program.stable))) {
|
||||
fail('The file path to electron binary does not exist or permissions do not allow to execute it. Please check the path provided.');
|
||||
}
|
||||
if (parseInt(process.version.substr(1)) < 6) {
|
||||
fail('Please update your Node version to greater than 6 to run the smoke test.');
|
||||
}
|
||||
const artifactsPath = opts.log || '';
|
||||
|
||||
// Setting up environment variables
|
||||
process.env.VSCODE_LATEST_PATH = program.latest;
|
||||
if (program.stable) {
|
||||
process.env.VSCODE_STABLE_PATH = program.stable;
|
||||
}
|
||||
process.env.SMOKETEST_REPO = testRepoLocalDir;
|
||||
if (program.latest && (program.latest.indexOf('Code - Insiders') /* macOS/Windows */ || program.latest.indexOf('code-insiders') /* Linux */) >= 0) {
|
||||
process.env.VSCODE_EDITION = 'insiders';
|
||||
}
|
||||
process.env.VSCODE_WORKSPACE_PATH = codeWorkspacePath;
|
||||
|
||||
// Setting up 'vscode-smoketest-express' project
|
||||
let os = process.platform.toString();
|
||||
if (os === 'darwin') {
|
||||
os = 'osx';
|
||||
}
|
||||
else if (os === 'win32') {
|
||||
os = 'win';
|
||||
}
|
||||
|
||||
main().catch(err => console.error(err));
|
||||
|
||||
async function main(): Promise<void> {
|
||||
await getKeybindings(`${keybindingsUrl}/doc.keybindings.${os}.json`, path.join(testDataPath, 'keybindings.json'));
|
||||
|
||||
const workspace = {
|
||||
id: (Date.now() + Math.round(Math.random() * 1000)).toString(),
|
||||
folders: [
|
||||
toUri(path.join(testRepoLocalDir, 'public')),
|
||||
toUri(path.join(testRepoLocalDir, 'routes')),
|
||||
toUri(path.join(testRepoLocalDir, 'views'))
|
||||
]
|
||||
};
|
||||
|
||||
await createWorkspaceFile(codeWorkspacePath, workspace);
|
||||
await cleanOrClone(testRepoUrl, testRepoLocalDir);
|
||||
await execute('npm install', testRepoLocalDir);
|
||||
await runTests();
|
||||
}
|
||||
const workspaceFilePath = path.join(testDataPath, 'smoketest.code-workspace');
|
||||
const testRepoUrl = 'https://github.com/Microsoft/vscode-smoketest-express';
|
||||
const workspacePath = path.join(testDataPath, 'vscode-smoketest-express');
|
||||
const keybindingsPath = path.join(testDataPath, 'keybindings.json');
|
||||
const extensionsPath = path.join(testDataPath, 'extensions-dir');
|
||||
mkdirp.sync(extensionsPath);
|
||||
|
||||
function fail(errorMessage): void {
|
||||
console.error(errorMessage);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (parseInt(process.version.substr(1)) < 6) {
|
||||
fail('Please update your Node version to greater than 6 to run the smoke test.');
|
||||
}
|
||||
|
||||
const repoPath = path.join(__dirname, '..', '..', '..');
|
||||
|
||||
function getDevElectronPath(): string {
|
||||
const buildPath = path.join(repoPath, '.build');
|
||||
const product = require(path.join(repoPath, 'product.json'));
|
||||
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return path.join(buildPath, 'electron', `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron');
|
||||
case 'linux':
|
||||
return path.join(buildPath, 'electron', `${product.applicationName}`);
|
||||
case 'win32':
|
||||
return path.join(buildPath, 'electron', `${product.nameShort}.exe`);
|
||||
default:
|
||||
throw new Error('Unsupported platform.');
|
||||
}
|
||||
}
|
||||
|
||||
function getBuildElectronPath(root: string): string {
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return path.join(root, 'Contents', 'MacOS', 'Electron');
|
||||
case 'linux': {
|
||||
const product = require(path.join(root, 'resources', 'app', 'product.json'));
|
||||
return path.join(root, product.applicationName);
|
||||
}
|
||||
case 'win32': {
|
||||
const product = require(path.join(root, 'resources', 'app', 'product.json'));
|
||||
return path.join(root, `${product.nameShort}.exe`);
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported platform.');
|
||||
}
|
||||
}
|
||||
|
||||
let testCodePath = opts.build;
|
||||
let stableCodePath = opts['stable-build'];
|
||||
let electronPath: string;
|
||||
|
||||
if (testCodePath) {
|
||||
electronPath = getBuildElectronPath(testCodePath);
|
||||
|
||||
if (stableCodePath) {
|
||||
process.env.VSCODE_STABLE_PATH = getBuildElectronPath(stableCodePath);
|
||||
}
|
||||
} else {
|
||||
testCodePath = getDevElectronPath();
|
||||
electronPath = testCodePath;
|
||||
process.env.VSCODE_REPOSITORY = repoPath;
|
||||
process.env.VSCODE_DEV = '1';
|
||||
process.env.VSCODE_CLI = '1';
|
||||
}
|
||||
|
||||
if (!fs.existsSync(electronPath || '')) {
|
||||
fail(`Can't find Code at ${electronPath}.`);
|
||||
}
|
||||
|
||||
const userDataDir = path.join(testDataPath, 'd');
|
||||
// process.env.VSCODE_WORKSPACE_PATH = workspaceFilePath;
|
||||
process.env.VSCODE_KEYBINDINGS_PATH = keybindingsPath;
|
||||
|
||||
let quality: Quality;
|
||||
if (process.env.VSCODE_DEV === '1') {
|
||||
quality = Quality.Dev;
|
||||
} else if ((testCodePath.indexOf('Code - Insiders') /* macOS/Windows */ || testCodePath.indexOf('code-insiders') /* Linux */) >= 0) {
|
||||
quality = Quality.Insiders;
|
||||
} else {
|
||||
quality = Quality.Stable;
|
||||
}
|
||||
|
||||
function getKeybindingPlatform(): string {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return 'osx';
|
||||
case 'win32': return 'win';
|
||||
default: return process.platform;
|
||||
}
|
||||
}
|
||||
|
||||
function toUri(path: string): string {
|
||||
if (os === 'win') {
|
||||
return `file:///${path.replace(/\\/g, '/')}`;
|
||||
if (process.platform === 'win32') {
|
||||
return `${path.replace(/\\/g, '/')}`;
|
||||
}
|
||||
|
||||
return `file://${path}`;
|
||||
return `${path}`;
|
||||
}
|
||||
|
||||
function runTests(): void {
|
||||
console.log('Running tests...');
|
||||
var proc = child_process.spawn(process.execPath, [
|
||||
'out/mocha-runner.js'
|
||||
]);
|
||||
proc.stdout.on('data', data => {
|
||||
console.log(data.toString());
|
||||
async function setup(): Promise<void> {
|
||||
console.log('*** Test data:', testDataPath);
|
||||
console.log('*** Preparing smoketest setup...');
|
||||
|
||||
const keybindingsUrl = `https://raw.githubusercontent.com/Microsoft/vscode-docs/master/scripts/keybindings/doc.keybindings.${getKeybindingPlatform()}.json`;
|
||||
console.log('*** Fetching keybindings...');
|
||||
|
||||
await new Promise((c, e) => {
|
||||
https.get(keybindingsUrl, res => {
|
||||
const output = fs.createWriteStream(keybindingsPath);
|
||||
res.on('error', e);
|
||||
output.on('error', e);
|
||||
output.on('close', c);
|
||||
res.pipe(output);
|
||||
}).on('error', e);
|
||||
});
|
||||
proc.stderr.on('data', data => {
|
||||
var date = new Date().toLocaleString();
|
||||
fs.appendFile(path.join(testDataPath, 'errors.log'), `${date}: ${data.toString()}`, (err) => {
|
||||
if (err) {
|
||||
throw new Error(`Could not write stderr to errors.log with the following error: ${err}`);
|
||||
};
|
||||
});
|
||||
});
|
||||
proc.on('exit', (code) => {
|
||||
process.exit(code);
|
||||
});
|
||||
}
|
||||
|
||||
async function cleanOrClone(repo: string, dir: string): Promise<any> {
|
||||
console.log('Cleaning or cloning test project repository...');
|
||||
|
||||
if (!folderExists(dir)) {
|
||||
await gitClone(repo, dir);
|
||||
} else {
|
||||
git.cwd(dir);
|
||||
await new Promise((c, e) => git.fetch(err => err ? e(err) : c()));
|
||||
await gitResetAndClean();
|
||||
}
|
||||
}
|
||||
|
||||
function gitClone(repo: string, dir: string): Promise<any> {
|
||||
return new Promise((res, rej) => {
|
||||
git.clone(repo, dir, () => {
|
||||
console.log('Test repository successfully cloned.');
|
||||
res();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function gitResetAndClean(): Promise<any> {
|
||||
await new Promise((c, e) => git.reset(['FETCH_HEAD', '--hard'], err => err ? e(err) : c()));
|
||||
await new Promise((c, e) => git.clean('f', ['-d'], err => err ? e(err) : c()));
|
||||
console.log('Test project was successfully reset to initial state.');
|
||||
}
|
||||
|
||||
function execute(cmd: string, dir: string): Promise<any> {
|
||||
return new Promise((res, rej) => {
|
||||
console.log(`Running ${cmd}...`);
|
||||
child_process.exec(cmd, { cwd: dir, stdio: [0, 1, 2] }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
rej(error);
|
||||
}
|
||||
if (stderr) {
|
||||
console.error(stderr);
|
||||
}
|
||||
console.log(stdout);
|
||||
res();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getKeybindings(url: string, location: string): Promise<any> {
|
||||
console.log(`Fetching keybindings from ${url}...`);
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(url, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
reject(`Failed to obtain key bindings with response code: ${res.statusCode}`);
|
||||
}
|
||||
|
||||
var buffer: Buffer[] = [];
|
||||
res.on('data', (chunk) => buffer.push(chunk));
|
||||
res.on('end', () => {
|
||||
fs.writeFile(location, Buffer.concat(buffer), 'utf8', () => {
|
||||
console.log('Keybindings were successfully fetched.');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}).on('error', (e) => {
|
||||
reject(`Failed to obtain key bindings with an error: ${e}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createWorkspaceFile(path: string, workspace: any): Promise<any> {
|
||||
console.log(`Creating workspace file at ${path}...`);
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.exists(path, exists => {
|
||||
if (exists) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
fs.writeFile(path, JSON.stringify(workspace, null, '\t'), error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
if (!fs.existsSync(workspaceFilePath)) {
|
||||
console.log('*** Creating workspace file...');
|
||||
const workspace = {
|
||||
folders: [
|
||||
{
|
||||
path: toUri(path.join(workspacePath, 'public'))
|
||||
},
|
||||
{
|
||||
path: toUri(path.join(workspacePath, 'routes'))
|
||||
},
|
||||
{
|
||||
path: toUri(path.join(workspacePath, 'views'))
|
||||
}
|
||||
});
|
||||
});
|
||||
]
|
||||
};
|
||||
|
||||
fs.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, '\t'));
|
||||
}
|
||||
|
||||
if (!fs.existsSync(workspacePath)) {
|
||||
console.log('*** Cloning test project repository...');
|
||||
cp.spawnSync('git', ['clone', testRepoUrl, workspacePath]);
|
||||
} else {
|
||||
console.log('*** Cleaning test project repository...');
|
||||
cp.spawnSync('git', ['fetch'], { cwd: workspacePath });
|
||||
cp.spawnSync('git', ['reset', '--hard', 'FETCH_HEAD'], { cwd: workspacePath });
|
||||
cp.spawnSync('git', ['clean', '-xdf'], { cwd: workspacePath });
|
||||
}
|
||||
|
||||
console.log('*** Running npm install...');
|
||||
cp.execSync('npm install', { cwd: workspacePath, stdio: 'inherit' });
|
||||
|
||||
console.log('*** Smoketest setup done!\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* WebDriverIO 4.8.0 outputs all kinds of "deprecation" warnings
|
||||
* for common commands like `keys` and `moveToObject`.
|
||||
* According to https://github.com/Codeception/CodeceptJS/issues/531,
|
||||
* these deprecation warnings are for Firefox, and have no alternative replacements.
|
||||
* Since we can't downgrade WDIO as suggested (it's Spectron's dep, not ours),
|
||||
* we must suppress the warning with a classic monkey-patch.
|
||||
*
|
||||
* @see webdriverio/lib/helpers/depcrecationWarning.js
|
||||
* @see https://github.com/webdriverio/webdriverio/issues/2076
|
||||
*/
|
||||
// Filter out the following messages:
|
||||
const wdioDeprecationWarning = /^WARNING: the "\w+" command will be depcrecated soon./; // [sic]
|
||||
// Monkey patch:
|
||||
const warn = console.warn;
|
||||
console.warn = function suppressWebdriverWarnings(message) {
|
||||
if (wdioDeprecationWarning.test(message)) { return; }
|
||||
warn.apply(console, arguments);
|
||||
};
|
||||
|
||||
before(async function () {
|
||||
// allow two minutes for setup
|
||||
this.timeout(2 * 60 * 1000);
|
||||
await setup();
|
||||
|
||||
const app = new SpectronApplication({
|
||||
quality,
|
||||
electronPath,
|
||||
workspacePath,
|
||||
userDataDir,
|
||||
extensionsPath,
|
||||
artifactsPath,
|
||||
workspaceFilePath,
|
||||
waitTime: parseInt(opts['wait-time'] || '0') || 20
|
||||
});
|
||||
}
|
||||
|
||||
function folderExists(folder: string): boolean {
|
||||
try {
|
||||
fs.accessSync(folder, 'rw');
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
await app.start();
|
||||
this.app = app;
|
||||
});
|
||||
|
||||
function binaryExists(filePath: string): boolean {
|
||||
try {
|
||||
fs.accessSync(filePath, 'x');
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
after(async function () {
|
||||
await this.app.stop();
|
||||
await new Promise((c, e) => rimraf(testDataPath, { maxBusyTries: 10 }, err => err ? e(err) : c()));
|
||||
});
|
||||
|
||||
// import './areas/workbench/data-migration.test';
|
||||
import './areas/workbench/data-loss.test';
|
||||
import './areas/explorer/explorer.test';
|
||||
import './areas/preferences/preferences.test';
|
||||
import './areas/search/search.test';
|
||||
import './areas/css/css.test';
|
||||
import './areas/editor/editor.test';
|
||||
import './areas/debug/debug.test';
|
||||
import './areas/git/git.test';
|
||||
// import './areas/terminal/terminal.test';
|
||||
import './areas/statusbar/statusbar.test';
|
||||
import './areas/extensions/extensions.test';
|
||||
import './areas/multiroot/multiroot.test';
|
||||
import './areas/workbench/localization.test';
|
||||
@@ -1,16 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const MochaTest = require('mocha');
|
||||
|
||||
const mochaTest = new MochaTest({
|
||||
timeout: 60000,
|
||||
slow: 10000,
|
||||
useColors: true
|
||||
});
|
||||
mochaTest.addFile(require('path').join(process.cwd(), 'out/test.js'));
|
||||
mochaTest.run((failures) => {
|
||||
process.exit(failures);
|
||||
});
|
||||
@@ -3,142 +3,308 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Application } from 'spectron';
|
||||
import { Application, SpectronClient as WebClient } from 'spectron';
|
||||
import { test as testPort } from 'portastic';
|
||||
import { SpectronClient } from './client';
|
||||
import { Screenshot } from '../helpers/screenshot';
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
import { ScreenCapturer } from '../helpers/screenshot';
|
||||
import { Workbench } from '../areas/workbench/workbench';
|
||||
import * as fs from 'fs';
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
import { sanitize } from '../helpers/utilities';
|
||||
|
||||
export const LATEST_PATH = process.env.VSCODE_LATEST_PATH;
|
||||
export const STABLE_PATH = process.env.VSCODE_STABLE_PATH;
|
||||
export const WORKSPACE_PATH = process.env.SMOKETEST_REPO;
|
||||
export const CODE_WORKSPACE_PATH = process.env.VSCODE_WORKSPACE_PATH;
|
||||
export const USER_DIR = 'test_data/temp_user_dir';
|
||||
export const EXTENSIONS_DIR = 'test_data/temp_extensions_dir';
|
||||
// Just hope random helps us here, cross your fingers!
|
||||
export async function findFreePort(): Promise<number> {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const port = 10000 + Math.round(Math.random() * 10000);
|
||||
|
||||
if (await testPort(port)) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Could not find free port!');
|
||||
}
|
||||
|
||||
export enum Quality {
|
||||
Dev,
|
||||
Insiders,
|
||||
Stable
|
||||
}
|
||||
|
||||
export interface SpectronApplicationOptions {
|
||||
quality: Quality;
|
||||
electronPath: string;
|
||||
workspacePath: string;
|
||||
userDataDir: string;
|
||||
extensionsPath: string;
|
||||
artifactsPath: string;
|
||||
workspaceFilePath: string;
|
||||
waitTime: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps Spectron's Application instance with its used methods.
|
||||
*/
|
||||
export class SpectronApplication {
|
||||
public client: SpectronClient;
|
||||
|
||||
private spectron: Application;
|
||||
private keybindings: any[];
|
||||
private screenshot: Screenshot;
|
||||
private static count = 0;
|
||||
|
||||
private readonly sampleExtensionsDir: string = 'test_data/sample_extensions_dir';
|
||||
private readonly pollTrials = 50;
|
||||
private readonly pollTimeout = 1; // in secs
|
||||
private _client: SpectronClient;
|
||||
private _workbench: Workbench;
|
||||
private _screenCapturer: ScreenCapturer;
|
||||
private spectron: Application | undefined;
|
||||
private keybindings: any[]; private stopLogCollection: (() => Promise<void>) | undefined;
|
||||
|
||||
constructor(electronPath: string, testName: string, private testRetry: number, args?: string[], chromeDriverArgs?: string[]) {
|
||||
if (!args) {
|
||||
args = [];
|
||||
constructor(
|
||||
private options: SpectronApplicationOptions
|
||||
) { }
|
||||
|
||||
get quality(): Quality {
|
||||
return this.options.quality;
|
||||
}
|
||||
|
||||
get client(): SpectronClient {
|
||||
return this._client;
|
||||
}
|
||||
|
||||
get webclient(): WebClient {
|
||||
if (!this.spectron) {
|
||||
throw new Error('Application not started');
|
||||
}
|
||||
|
||||
return this.spectron.client;
|
||||
}
|
||||
|
||||
get screenCapturer(): ScreenCapturer {
|
||||
return this._screenCapturer;
|
||||
}
|
||||
|
||||
get workbench(): Workbench {
|
||||
return this._workbench;
|
||||
}
|
||||
|
||||
get workspacePath(): string {
|
||||
return this.options.workspacePath;
|
||||
}
|
||||
|
||||
get extensionsPath(): string {
|
||||
return this.options.extensionsPath;
|
||||
}
|
||||
|
||||
get userDataPath(): string {
|
||||
return this.options.userDataDir;
|
||||
}
|
||||
|
||||
get workspaceFilePath(): string {
|
||||
return this.options.workspaceFilePath;
|
||||
}
|
||||
|
||||
private _suiteName: string = 'Init';
|
||||
|
||||
set suiteName(suiteName: string) {
|
||||
this._suiteName = suiteName;
|
||||
this._screenCapturer.suiteName = suiteName;
|
||||
}
|
||||
|
||||
async start(): Promise<any> {
|
||||
await this._start();
|
||||
await this.waitForWelcome();
|
||||
}
|
||||
|
||||
async restart(codeArgs: string[] = []): Promise<any> {
|
||||
await this.stop();
|
||||
await new Promise(c => setTimeout(c, 1000));
|
||||
await this._start(codeArgs);
|
||||
}
|
||||
|
||||
private async _start(codeArgs: string[] = []): Promise<any> {
|
||||
await this.retrieveKeybindings();
|
||||
cp.execSync('git checkout .', { cwd: this.options.workspacePath });
|
||||
await this.startApplication(codeArgs);
|
||||
await this.checkWindowReady();
|
||||
}
|
||||
|
||||
async reload(): Promise<any> {
|
||||
await this.workbench.quickopen.runCommand('Reload Window');
|
||||
// TODO @sandy: Find a proper condition to wait for reload
|
||||
await new Promise(c => setTimeout(c, 500));
|
||||
await this.checkWindowReady();
|
||||
}
|
||||
|
||||
async stop(): Promise<any> {
|
||||
if (this.stopLogCollection) {
|
||||
await this.stopLogCollection();
|
||||
this.stopLogCollection = undefined;
|
||||
}
|
||||
|
||||
if (this.spectron && this.spectron.isRunning()) {
|
||||
await this.spectron.stop();
|
||||
this.spectron = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async startApplication(codeArgs: string[] = []): Promise<any> {
|
||||
|
||||
let args: string[] = [];
|
||||
let chromeDriverArgs: string[] = [];
|
||||
|
||||
if (process.env.VSCODE_REPOSITORY) {
|
||||
args.push(process.env.VSCODE_REPOSITORY as string);
|
||||
}
|
||||
|
||||
args.push(this.options.workspacePath);
|
||||
|
||||
// Prevent 'Getting Started' web page from opening on clean user-data-dir
|
||||
args.push('--skip-getting-started');
|
||||
|
||||
// Ensure that running over custom extensions directory, rather than picking up the one that was used by a tester previously
|
||||
let extensionDirIsSet = false;
|
||||
for (let arg of args) {
|
||||
if (arg.startsWith('--extensions-dir')) {
|
||||
extensionDirIsSet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!extensionDirIsSet) {
|
||||
args.push(`--extensions-dir=${this.sampleExtensionsDir}`);
|
||||
}
|
||||
// Prevent Quick Open from closing when focus is stolen, this allows concurrent smoketest suite running
|
||||
args.push('--sticky-quickopen');
|
||||
|
||||
this.spectron = new Application({
|
||||
path: electronPath,
|
||||
args: args,
|
||||
chromeDriverArgs: chromeDriverArgs,
|
||||
// Disable telemetry
|
||||
args.push('--disable-telemetry');
|
||||
|
||||
// Disable updates
|
||||
args.push('--disable-updates');
|
||||
|
||||
// Disable crash reporter
|
||||
// This seems to be the fix for the strange hangups in which Code stays unresponsive
|
||||
// and tests finish badly with timeouts, leaving Code running in the background forever
|
||||
args.push('--disable-crash-reporter');
|
||||
|
||||
// Ensure that running over custom extensions directory, rather than picking up the one that was used by a tester previously
|
||||
args.push(`--extensions-dir=${this.options.extensionsPath}`);
|
||||
|
||||
args.push(...codeArgs);
|
||||
|
||||
chromeDriverArgs.push(`--user-data-dir=${this.options.userDataDir}`);
|
||||
|
||||
// Spectron always uses the same port number for the chrome driver
|
||||
// and it handles gracefully when two instances use the same port number
|
||||
// This works, but when one of the instances quits, it takes down
|
||||
// chrome driver with it, leaving the other instance in DISPAIR!!! :(
|
||||
const port = await findFreePort();
|
||||
|
||||
// We must get a different port for debugging the smoketest express app
|
||||
// otherwise concurrent test runs will clash on those ports
|
||||
const env = { PORT: String(await findFreePort()), ...process.env };
|
||||
|
||||
const opts: any = {
|
||||
path: this.options.electronPath,
|
||||
port,
|
||||
args,
|
||||
env,
|
||||
chromeDriverArgs,
|
||||
startTimeout: 10000,
|
||||
requireName: 'nodeRequire'
|
||||
});
|
||||
this.testRetry += 1; // avoid multiplication by 0 for wait times
|
||||
this.screenshot = new Screenshot(this, testName, testRetry);
|
||||
this.client = new SpectronClient(this.spectron, this.screenshot);
|
||||
this.retrieveKeybindings();
|
||||
}
|
||||
};
|
||||
|
||||
public get app(): Application {
|
||||
return this.spectron;
|
||||
}
|
||||
const runName = String(SpectronApplication.count++);
|
||||
let testsuiteRootPath: string | undefined = undefined;
|
||||
let screenshotsDirPath: string | undefined = undefined;
|
||||
|
||||
public async start(): Promise<any> {
|
||||
await this.spectron.start();
|
||||
await this.focusOnWindow(1); // focuses on main renderer window
|
||||
await this.checkWindowReady();
|
||||
}
|
||||
if (this.options.artifactsPath) {
|
||||
testsuiteRootPath = path.join(this.options.artifactsPath, sanitize(runName));
|
||||
mkdirp.sync(testsuiteRootPath);
|
||||
|
||||
public async stop(): Promise<any> {
|
||||
if (this.spectron && this.spectron.isRunning()) {
|
||||
return await this.spectron.stop();
|
||||
// Collect screenshots
|
||||
screenshotsDirPath = path.join(testsuiteRootPath, 'screenshots');
|
||||
mkdirp.sync(screenshotsDirPath);
|
||||
|
||||
// Collect chromedriver logs
|
||||
const chromedriverLogPath = path.join(testsuiteRootPath, 'chromedriver.log');
|
||||
opts.chromeDriverLogPath = chromedriverLogPath;
|
||||
|
||||
// Collect webdriver logs
|
||||
const webdriverLogsPath = path.join(testsuiteRootPath, 'webdriver');
|
||||
mkdirp.sync(webdriverLogsPath);
|
||||
opts.webdriverLogPath = webdriverLogsPath;
|
||||
}
|
||||
}
|
||||
|
||||
public waitFor(func: (...args: any[]) => any, args: any): Promise<any> {
|
||||
return this.callClientAPI(func, args);
|
||||
}
|
||||
this.spectron = new Application(opts);
|
||||
await this.spectron.start();
|
||||
|
||||
public wait(): Promise<any> {
|
||||
return new Promise(resolve => setTimeout(resolve, this.testRetry * this.pollTimeout * 1000));
|
||||
}
|
||||
if (testsuiteRootPath) {
|
||||
// Collect logs
|
||||
const mainProcessLogPath = path.join(testsuiteRootPath, 'main.log');
|
||||
const rendererProcessLogPath = path.join(testsuiteRootPath, 'renderer.log');
|
||||
|
||||
public focusOnWindow(index: number): Promise<any> {
|
||||
return this.client.windowByIndex(index);
|
||||
const flush = async () => {
|
||||
if (!this.spectron) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mainLogs = await this.spectron.client.getMainProcessLogs();
|
||||
await new Promise((c, e) => fs.appendFile(mainProcessLogPath, mainLogs.join('\n'), { encoding: 'utf8' }, err => err ? e(err) : c()));
|
||||
|
||||
const rendererLogs = (await this.spectron.client.getRenderProcessLogs()).map(m => `${m.timestamp} - ${m.level} - ${m.message}`);
|
||||
await new Promise((c, e) => fs.appendFile(rendererProcessLogPath, rendererLogs.join('\n'), { encoding: 'utf8' }, err => err ? e(err) : c()));
|
||||
};
|
||||
|
||||
let running = true;
|
||||
const loopFlush = async () => {
|
||||
while (true) {
|
||||
await flush();
|
||||
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
await new Promise(c => setTimeout(c, 1000));
|
||||
}
|
||||
};
|
||||
|
||||
const loopPromise = loopFlush();
|
||||
this.stopLogCollection = () => {
|
||||
running = false;
|
||||
return loopPromise;
|
||||
};
|
||||
}
|
||||
|
||||
this._screenCapturer = new ScreenCapturer(this.spectron, this._suiteName, screenshotsDirPath);
|
||||
this._client = new SpectronClient(this.spectron, this, this.options.waitTime);
|
||||
this._workbench = new Workbench(this);
|
||||
}
|
||||
|
||||
private async checkWindowReady(): Promise<any> {
|
||||
await this.waitFor(this.spectron.client.getHTML, '[id="workbench.main.container"]');
|
||||
await this.webclient.waitUntilWindowLoaded();
|
||||
// Spectron opens multiple terminals in Windows platform
|
||||
// Workaround to focus the right window - https://github.com/electron/spectron/issues/60
|
||||
await this.client.windowByIndex(1);
|
||||
// await this.app.browserWindow.focus();
|
||||
await this.client.waitForHTML('[id="workbench.main.container"]');
|
||||
}
|
||||
|
||||
private retrieveKeybindings() {
|
||||
fs.readFile(path.join(process.cwd(), `test_data/keybindings.json`), 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
try {
|
||||
this.keybindings = JSON.parse(data);
|
||||
} catch (e) {
|
||||
throw new Error(`Error parsing keybindings JSON: ${e}`);
|
||||
}
|
||||
private async waitForWelcome(): Promise<any> {
|
||||
await this.client.waitForElement('.explorer-folders-view');
|
||||
await this.client.waitForElement(`.editor-container[id="workbench.editor.walkThroughPart"] .welcomePage`);
|
||||
}
|
||||
|
||||
private retrieveKeybindings(): Promise<void> {
|
||||
return new Promise((c, e) => {
|
||||
fs.readFile(process.env.VSCODE_KEYBINDINGS_PATH as string, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
try {
|
||||
this.keybindings = JSON.parse(data);
|
||||
c();
|
||||
} catch (e) {
|
||||
throw new Error(`Error parsing keybindings JSON: ${e}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async callClientAPI(func: (...args: any[]) => Promise<any>, args: any): Promise<any> {
|
||||
let trial = 1;
|
||||
|
||||
while (true) {
|
||||
if (trial > this.pollTrials) {
|
||||
throw new Error(`Could not retrieve the element in ${this.testRetry * this.pollTrials * this.pollTimeout} seconds.`);
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await func.call(this.client, args, false);
|
||||
} catch (e) { }
|
||||
|
||||
if (result && result !== '') {
|
||||
await this.screenshot.capture();
|
||||
return result;
|
||||
}
|
||||
|
||||
await this.wait();
|
||||
trial++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the command from keybindings file and executes it with WebdriverIO client API
|
||||
* @param command command (e.g. 'workbench.action.files.newUntitledFile')
|
||||
*/
|
||||
public command(command: string, capture?: boolean): Promise<any> {
|
||||
runCommand(command: string): Promise<any> {
|
||||
const binding = this.keybindings.find(x => x['command'] === command);
|
||||
if (!binding) {
|
||||
return Promise.reject(`Key binding for ${command} was not found.`);
|
||||
return this.workbench.quickopen.runCommand(command);
|
||||
}
|
||||
|
||||
const keys: string = binding.key;
|
||||
@@ -151,7 +317,7 @@ export class SpectronApplication {
|
||||
keysToPress.push('NULL');
|
||||
});
|
||||
|
||||
return this.client.keys(keysToPress, capture);
|
||||
return this.client.keys(keysToPress);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,124 +4,209 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Application } from 'spectron';
|
||||
import { Screenshot } from '../helpers/screenshot';
|
||||
import { RawResult, Element } from 'webdriverio';
|
||||
import { SpectronApplication } from './application';
|
||||
|
||||
/**
|
||||
* Abstracts the Spectron's WebdriverIO managed client property on the created Application instances.
|
||||
*/
|
||||
export class SpectronClient {
|
||||
|
||||
constructor(private spectron: Application, private shot: Screenshot) {
|
||||
// noop
|
||||
// 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
|
||||
private retryCount: number;
|
||||
private readonly retryDuration = 100; // in milliseconds
|
||||
|
||||
constructor(
|
||||
readonly spectron: Application,
|
||||
private application: SpectronApplication,
|
||||
waitTime: number
|
||||
) {
|
||||
this.retryCount = (waitTime * 1000) / this.retryDuration;
|
||||
}
|
||||
|
||||
public windowByIndex(index: number): Promise<any> {
|
||||
windowByIndex(index: number): Promise<any> {
|
||||
return this.spectron.client.windowByIndex(index);
|
||||
}
|
||||
|
||||
public async keys(keys: string[] | string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
return this.spectron.client.keys(keys);
|
||||
keys(keys: string[]): Promise<void> {
|
||||
this.spectron.client.keys(keys);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public async getText(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async getText(selector: string, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.getText(selector);
|
||||
}
|
||||
|
||||
public async getHTML(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
return this.spectron.client.getHTML(selector);
|
||||
async waitForText(selector: string, text?: string, accept?: (result: string) => boolean): Promise<string> {
|
||||
accept = accept ? accept : result => text !== void 0 ? text === result : !!result;
|
||||
return this.waitFor(() => this.spectron.client.getText(selector), accept, `getText with selector ${selector}`);
|
||||
}
|
||||
|
||||
public async click(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean): Promise<string> {
|
||||
accept = accept ? accept : (result => textContent !== void 0 ? textContent === result : !!result);
|
||||
const fn = async () => await this.spectron.client.selectorExecute(selector, div => Array.isArray(div) ? div[0].textContent : div.textContent);
|
||||
return this.waitFor(fn, s => accept!(typeof s === 'string' ? s : ''), `getTextContent with selector ${selector}`);
|
||||
}
|
||||
|
||||
async waitForValue(selector: string, value?: string, accept?: (result: string) => boolean): Promise<any> {
|
||||
accept = accept ? accept : result => value !== void 0 ? value === result : !!result;
|
||||
return this.waitFor(() => this.spectron.client.getValue(selector), accept, `getValue with selector ${selector}`);
|
||||
}
|
||||
|
||||
async waitForHTML(selector: string, accept: (result: string) => boolean = (result: string) => !!result): Promise<any> {
|
||||
return this.waitFor(() => this.spectron.client.getHTML(selector), accept, `getHTML with selector ${selector}`);
|
||||
}
|
||||
|
||||
async waitAndClick(selector: string): Promise<any> {
|
||||
return this.waitFor(() => this.spectron.client.click(selector), void 0, `click with selector ${selector}`);
|
||||
}
|
||||
|
||||
async click(selector: string): Promise<any> {
|
||||
return this.spectron.client.click(selector);
|
||||
}
|
||||
|
||||
public async doubleClick(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
return this.spectron.client.doubleClick(selector);
|
||||
async doubleClickAndWait(selector: string, capture: boolean = true): Promise<any> {
|
||||
return this.waitFor(() => this.spectron.client.doubleClick(selector), void 0, `doubleClick with selector ${selector}`);
|
||||
}
|
||||
|
||||
public async leftClick(selector: string, xoffset: number, yoffset: number, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async leftClick(selector: string, xoffset: number, yoffset: number, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.leftClick(selector, xoffset, yoffset);
|
||||
}
|
||||
|
||||
public async rightClick(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async rightClick(selector: string, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.rightClick(selector);
|
||||
}
|
||||
|
||||
public async moveToObject(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async moveToObject(selector: string, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.moveToObject(selector);
|
||||
}
|
||||
|
||||
public async setValue(selector: string, text: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async waitAndMoveToObject(selector: string): Promise<any> {
|
||||
return this.waitFor(() => this.spectron.client.moveToObject(selector), void 0, `move to object with selector ${selector}`);
|
||||
}
|
||||
|
||||
async setValue(selector: string, text: string, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.setValue(selector, text);
|
||||
}
|
||||
|
||||
public async elements(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
return this.spectron.client.elements(selector);
|
||||
async waitForElements(selector: string, accept: (result: Element[]) => boolean = result => result.length > 0): Promise<Element[]> {
|
||||
return this.waitFor<RawResult<Element[]>>(() => this.spectron.client.elements(selector), result => accept(result.value), `elements with selector ${selector}`)
|
||||
.then(result => result.value);
|
||||
}
|
||||
|
||||
public async element(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
return this.spectron.client.element(selector);
|
||||
async waitForElement(selector: string, accept: (result: Element | undefined) => boolean = result => !!result): Promise<Element> {
|
||||
return this.waitFor<RawResult<Element>>(() => this.spectron.client.element(selector), result => accept(result ? result.value : void 0), `element with selector ${selector}`)
|
||||
.then(result => result.value);
|
||||
}
|
||||
|
||||
public async dragAndDrop(sourceElem: string, destinationElem: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async waitForVisibility(selector: string, accept: (result: boolean) => boolean = result => result): Promise<any> {
|
||||
return this.waitFor(() => this.spectron.client.isVisible(selector), accept, `isVisible with selector ${selector}`);
|
||||
}
|
||||
|
||||
async element(selector: string): Promise<Element> {
|
||||
return this.spectron.client.element(selector)
|
||||
.then(result => result.value);
|
||||
}
|
||||
|
||||
async waitForActiveElement(selector: string): Promise<any> {
|
||||
return this.waitFor(
|
||||
() => this.spectron.client.execute(s => document.activeElement.matches(s), selector),
|
||||
r => r.value,
|
||||
`wait for active element: ${selector}`
|
||||
);
|
||||
}
|
||||
|
||||
async waitForAttribute(selector: string, attribute: string, accept: (result: string) => boolean = result => !!result): Promise<string> {
|
||||
return this.waitFor<string>(() => this.spectron.client.getAttribute(selector), accept, `attribute with selector ${selector}`);
|
||||
}
|
||||
|
||||
async dragAndDrop(sourceElem: string, destinationElem: string, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.dragAndDrop(sourceElem, destinationElem);
|
||||
}
|
||||
|
||||
public async selectByValue(selector: string, value: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async selectByValue(selector: string, value: string, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.selectByValue(selector, value);
|
||||
}
|
||||
|
||||
public async getValue(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async getValue(selector: string, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.getValue(selector);
|
||||
}
|
||||
|
||||
public async getAttribute(selector: string, attribute: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async getAttribute(selector: string, attribute: string, capture: boolean = true): Promise<any> {
|
||||
return Promise.resolve(this.spectron.client.getAttribute(selector, attribute));
|
||||
}
|
||||
|
||||
public clearElement(selector: string): any {
|
||||
return this.spectron.client.clearElement(selector);
|
||||
}
|
||||
|
||||
public buttonDown(): any {
|
||||
buttonDown(): any {
|
||||
return this.spectron.client.buttonDown();
|
||||
}
|
||||
|
||||
public buttonUp(): any {
|
||||
buttonUp(): any {
|
||||
return this.spectron.client.buttonUp();
|
||||
}
|
||||
|
||||
public async isVisible(selector: string, capture: boolean = true): Promise<any> {
|
||||
await this.screenshot(capture);
|
||||
async isVisible(selector: string, capture: boolean = true): Promise<any> {
|
||||
return this.spectron.client.isVisible(selector);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
async getTitle(): Promise<string> {
|
||||
return this.spectron.client.getTitle();
|
||||
}
|
||||
|
||||
private async screenshot(capture: boolean): Promise<any> {
|
||||
if (capture) {
|
||||
try {
|
||||
await this.shot.capture();
|
||||
} catch (e) {
|
||||
throw new Error(`Screenshot could not be captured: ${e}`);
|
||||
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> {
|
||||
if (this.running) {
|
||||
throw new Error('Not allowed to run nested waitFor calls!');
|
||||
}
|
||||
|
||||
this.running = true;
|
||||
|
||||
try {
|
||||
let trial = 1;
|
||||
retryCount = typeof retryCount === 'number' ? retryCount : this.retryCount;
|
||||
|
||||
while (true) {
|
||||
if (trial > retryCount) {
|
||||
await this.application.screenCapturer.capture('timeout');
|
||||
throw new Error(`${timeoutMessage}: Timed out after ${(retryCount * this.retryDuration) / 1000} seconds.`);
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await func();
|
||||
} catch (e) {
|
||||
// console.log(e);
|
||||
}
|
||||
|
||||
if (accept(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, this.retryDuration));
|
||||
trial++;
|
||||
}
|
||||
} finally {
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
// type(text: string): Promise<any> {
|
||||
// return new Promise((res) => {
|
||||
// let textSplit = text.split(' ');
|
||||
|
||||
// const type = async (i: number) => {
|
||||
// if (!textSplit[i] || textSplit[i].length <= 0) {
|
||||
// return res();
|
||||
// }
|
||||
|
||||
// const toType = textSplit[i + 1] ? `${textSplit[i]} ` : textSplit[i];
|
||||
// await this.keys(toType);
|
||||
// await this.keys(['NULL']);
|
||||
// await type(i + 1);
|
||||
// };
|
||||
|
||||
// return type(0);
|
||||
// });
|
||||
// }
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { testDataMigration } from './tests/data-migration';
|
||||
import { testDataLoss } from './tests/data-loss';
|
||||
import { testExplorer } from './tests/explorer';
|
||||
import { testConfigViews } from './tests/configuration-views';
|
||||
import { testSearch } from './tests/search';
|
||||
import { testCSS } from './tests/css';
|
||||
import { testJavaScript } from './tests/javascript';
|
||||
import { testJavaScriptDebug } from './tests/javascript-debug';
|
||||
import { testGit } from './tests/git';
|
||||
import { testIntegratedTerminal } from './tests/integrated-terminal';
|
||||
import { testStatusbar } from './tests/statusbar';
|
||||
import { testTasks } from './tests/tasks';
|
||||
import { testExtensions } from './tests/extensions';
|
||||
import { testLocalization } from './tests/localization';
|
||||
import { testMultiRoot } from './tests/multiroot';
|
||||
|
||||
describe('Smoke:', () => {
|
||||
testDataMigration();
|
||||
testDataLoss();
|
||||
testExplorer();
|
||||
testConfigViews();
|
||||
testSearch();
|
||||
testCSS();
|
||||
testJavaScript();
|
||||
testJavaScriptDebug();
|
||||
testGit();
|
||||
testIntegratedTerminal();
|
||||
testStatusbar();
|
||||
testTasks();
|
||||
testExtensions();
|
||||
testLocalization();
|
||||
if (process.env.VSCODE_EDITION === 'insiders') {
|
||||
testMultiRoot(); // only enabled in insiders
|
||||
}
|
||||
});
|
||||
@@ -1,57 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { ConfigurationView, ActivityBarPosition } from '../areas/configuration-views';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testConfigViews() {
|
||||
describe('Configuration and views', () => {
|
||||
let configView: ConfigurationView;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
configView = new ConfigurationView(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('turns off editor line numbers and verifies the live change', async function () {
|
||||
await common.newUntitledFile();
|
||||
await app.wait();
|
||||
let elementsCount = await configView.getEditorLineNumbers();
|
||||
assert.equal(elementsCount, 1, 'Line numbers are not present in the editor before disabling them.');
|
||||
await common.addSetting('editor.lineNumbers', 'off');
|
||||
await app.wait();
|
||||
elementsCount = await configView.getEditorLineNumbers();
|
||||
assert.equal(elementsCount, 0, 'Line numbers are still present in the editor after disabling them.');
|
||||
});
|
||||
|
||||
it(`changes 'workbench.action.toggleSidebarPosition' command key binding and verifies it`, async function () {
|
||||
await configView.enterKeybindingsView();
|
||||
await common.type('workbench.action.toggleSidebarPosition');
|
||||
await app.wait();
|
||||
await configView.selectFirstKeybindingsMatch();
|
||||
await configView.changeKeybinding();
|
||||
await configView.enterBinding(['Control', 'u', 'NULL']);
|
||||
await common.enter();
|
||||
let html = await configView.getActivityBar(ActivityBarPosition.RIGHT);
|
||||
assert.equal(html, undefined, 'Activity bar is positioned on the right, whereas should not be.');
|
||||
await app.wait();
|
||||
await configView.toggleActivityBarPosition();
|
||||
html = await configView.getActivityBar(ActivityBarPosition.RIGHT);
|
||||
assert.ok(html, 'Activity bar was not moved to right after toggling its position.');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { CSS, CSSProblem } from '../areas/css';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testCSS() {
|
||||
describe('CSS', () => {
|
||||
let css: CSS;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
css = new CSS(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('verifies quick outline', async function () {
|
||||
await common.openFirstMatchFile('style.css');
|
||||
await css.openQuickOutline();
|
||||
await app.wait();
|
||||
const count = await common.getQuickOpenElements();
|
||||
assert.equal(count, 2, 'Quick outline symbol count is wrong.');
|
||||
});
|
||||
|
||||
it('verifies warnings for the empty rule', async function () {
|
||||
await common.openFirstMatchFile('style.css');
|
||||
await common.type('.foo{}');
|
||||
await app.wait();
|
||||
let warning = await css.getEditorProblem(CSSProblem.WARNING);
|
||||
assert.ok(warning, `Warning squiggle is not shown in 'style.css'.`);
|
||||
await css.toggleProblemsView();
|
||||
warning = await css.getProblemsViewsProblem(CSSProblem.WARNING);
|
||||
assert.ok(warning, 'Warning does not appear in Problems view.');
|
||||
});
|
||||
|
||||
it('verifies that warning becomes an error once setting changed', async function () {
|
||||
await common.addSetting('css.lint.emptyRules', 'error');
|
||||
await common.openFirstMatchFile('style.css');
|
||||
await common.type('.foo{}');
|
||||
await app.wait();
|
||||
let error = await css.getEditorProblem(CSSProblem.ERROR);
|
||||
assert.ok(error, `Error squiggle is not shown in 'style.css'.`);
|
||||
await css.toggleProblemsView();
|
||||
error = await css.getProblemsViewsProblem(CSSProblem.ERROR);
|
||||
assert.ok(error, `Error does not appear in Problems view`);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, USER_DIR, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { DataLoss } from '../areas/data-loss';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
let dl: DataLoss;
|
||||
|
||||
export function testDataLoss() {
|
||||
describe('Data Loss', () => {
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH], [`--user-data-dir=${USER_DIR}`]);
|
||||
common = new CommonActions(app);
|
||||
dl = new DataLoss(app);
|
||||
await common.removeDirectory(USER_DIR);
|
||||
|
||||
await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it(`verifies that 'hot exit' works for dirty files`, async function () {
|
||||
const textToType = 'Hello, Code', fileName = 'readme.md', untitled = 'Untitled-1';
|
||||
await common.newUntitledFile();
|
||||
await common.type(textToType);
|
||||
await dl.openExplorerViewlet();
|
||||
await common.openFile(fileName, true);
|
||||
await common.type(textToType);
|
||||
|
||||
await app.stop();
|
||||
await app.start();
|
||||
|
||||
// check tab presence
|
||||
assert.ok(await common.getTab(untitled), `${untitled} tab is not present after reopening.`);
|
||||
assert.ok(await common.getTab(fileName, true), `${fileName} tab is not present or is not active after reopening.`);
|
||||
// check if they marked as dirty (icon) and active tab is the last opened
|
||||
assert.ok(await dl.verifyTabIsDirty(untitled), `${untitled} tab is not dirty after reopening.`);
|
||||
assert.ok(await dl.verifyTabIsDirty(fileName, true), `${fileName} tab is not dirty after reopening.`);
|
||||
});
|
||||
|
||||
it(`verifies that contents of the dirty files are restored after 'hot exit'`, async function () {
|
||||
// make one dirty file,
|
||||
// create one untitled file
|
||||
const textToType = 'Hello, Code';
|
||||
|
||||
// create one untitled file
|
||||
await common.newUntitledFile();
|
||||
await common.type(textToType);
|
||||
|
||||
// make one dirty file,
|
||||
await common.openFile('readme.md', true);
|
||||
await common.type(textToType);
|
||||
|
||||
await app.stop();
|
||||
await app.start();
|
||||
|
||||
// check their contents
|
||||
let fileDirt = await common.getEditorFirstLinePlainText();
|
||||
assert.equal(fileDirt, textToType, 'Active file contents are different after restore.');
|
||||
await common.selectTab('Untitled-1');
|
||||
fileDirt = await common.getEditorFirstLinePlainText();
|
||||
assert.equal(fileDirt, textToType, 'Untitled file edit are different after restore.');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, USER_DIR, STABLE_PATH, LATEST_PATH, WORKSPACE_PATH, EXTENSIONS_DIR } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testDataMigration() {
|
||||
if (!STABLE_PATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
describe('Data Migration', () => {
|
||||
|
||||
afterEach(async function () {
|
||||
await app.stop();
|
||||
await common.removeDirectory(USER_DIR);
|
||||
return await common.removeDirectory(EXTENSIONS_DIR);
|
||||
});
|
||||
|
||||
function setupSpectron(context: Mocha.ITestCallbackContext, appPath: string, args?: string[]): void {
|
||||
if (!args) {
|
||||
args = [];
|
||||
}
|
||||
args.push(`--extensions-dir=${EXTENSIONS_DIR}`);
|
||||
|
||||
app = new SpectronApplication(appPath, context.test.fullTitle(), context.test.currentRetry(), args, [`--user-data-dir=${USER_DIR}`]);
|
||||
common = new CommonActions(app);
|
||||
}
|
||||
|
||||
it('checks if the Untitled file is restored migrating from stable to latest', async function () {
|
||||
const textToType = 'Very dirty file';
|
||||
|
||||
// Setting up stable version
|
||||
setupSpectron(this, STABLE_PATH);
|
||||
await app.start();
|
||||
|
||||
await common.newUntitledFile();
|
||||
await common.type(textToType);
|
||||
await app.stop();
|
||||
|
||||
await app.wait(); // wait until all resources are released (e.g. locked local storage)
|
||||
|
||||
// Checking latest version for the restored state
|
||||
setupSpectron(this, LATEST_PATH);
|
||||
await app.start();
|
||||
|
||||
assert.ok(await common.getTab('Untitled-1'), 'Untitled-1 tab was not restored after migration.');
|
||||
await common.selectTab('Untitled-1');
|
||||
const editorText = await common.getEditorFirstLinePlainText();
|
||||
assert.equal(editorText, textToType, 'Typed editor text does not match to the one after migration.');
|
||||
});
|
||||
|
||||
it('checks if the newly created dirty file is restored migrating from stable to latest', async function () {
|
||||
const fileName = 'test_data/plainFile',
|
||||
firstTextPart = 'This is going to be an unsaved file', secondTextPart = '_that is dirty.';
|
||||
|
||||
// Setting up stable version
|
||||
setupSpectron(this, STABLE_PATH, [fileName]);
|
||||
await common.removeFile(`${fileName}`);
|
||||
await app.start();
|
||||
|
||||
await common.type(firstTextPart);
|
||||
await common.saveOpenedFile();
|
||||
await app.wait();
|
||||
await common.type(secondTextPart);
|
||||
|
||||
await app.stop();
|
||||
await app.wait(); // wait until all resources are released (e.g. locked local storage)
|
||||
|
||||
// Checking latest version for the restored state
|
||||
setupSpectron(this, LATEST_PATH);
|
||||
await app.start();
|
||||
assert.ok(await common.getTab(fileName.split('/')[1]), `${fileName} was not restored after migration.`);
|
||||
await common.selectTab(fileName.split('/')[1]);
|
||||
const editorText = await common.getEditorFirstLinePlainText();
|
||||
assert.equal(editorText, firstTextPart.concat(secondTextPart), 'Entered text was not correctly restored after migration.');
|
||||
|
||||
// Cleanup
|
||||
await common.removeFile(`${fileName}`);
|
||||
});
|
||||
|
||||
it('cheks if opened tabs are restored migrating from stable to latest', async function () {
|
||||
const fileName1 = 'app.js', fileName2 = 'jsconfig.json', fileName3 = 'readme.md';
|
||||
setupSpectron(this, STABLE_PATH, [WORKSPACE_PATH]);
|
||||
await app.start();
|
||||
await common.openFile(fileName1, true);
|
||||
await common.openFile(fileName2, true);
|
||||
await common.openFile(fileName3, true);
|
||||
await app.stop();
|
||||
|
||||
setupSpectron(this, LATEST_PATH, [WORKSPACE_PATH]);
|
||||
await app.start();
|
||||
assert.ok(await common.getTab(fileName1), `${fileName1} tab was not restored after migration.`);
|
||||
assert.ok(await common.getTab(fileName2), `${fileName2} tab was not restored after migration.`);
|
||||
assert.ok(await common.getTab(fileName3), `${fileName3} tab was not restored after migration.`);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testExplorer() {
|
||||
describe('Explorer', () => {
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('quick open search produces correct result', async function () {
|
||||
await common.openQuickOpen();
|
||||
await common.type('.js');
|
||||
await app.wait();
|
||||
const elCount = await common.getQuickOpenElements();
|
||||
assert.equal(elCount, 7);
|
||||
});
|
||||
|
||||
it('quick open respects fuzzy matching', async function () {
|
||||
await common.openQuickOpen();
|
||||
await common.type('a.s');
|
||||
await app.wait();
|
||||
const elCount = await common.getQuickOpenElements();
|
||||
assert.equal(elCount, 3);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH, EXTENSIONS_DIR } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { Extensions } from '../areas/extensions';
|
||||
|
||||
var dns = require('dns');
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testExtensions() {
|
||||
|
||||
describe('Extensions', () => {
|
||||
let extensions: Extensions;
|
||||
const extensionName = 'vscode-smoketest-check';
|
||||
|
||||
beforeEach(async function () {
|
||||
const network = await networkAttached();
|
||||
if (!network) {
|
||||
return Promise.reject('There is no network connection for testing extensions.');
|
||||
}
|
||||
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH, `--extensions-dir=${EXTENSIONS_DIR}`]);
|
||||
common = new CommonActions(app);
|
||||
extensions = new Extensions(app, common);
|
||||
await common.removeDirectory(EXTENSIONS_DIR);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
await app.stop();
|
||||
return await common.removeDirectory(EXTENSIONS_DIR);
|
||||
});
|
||||
|
||||
it(`installs 'vscode-smoketest-check' extension and verifies reload is prompted`, async function () {
|
||||
await extensions.openExtensionsViewlet();
|
||||
await extensions.searchForExtension(extensionName);
|
||||
await app.wait();
|
||||
await extensions.installExtension(extensionName);
|
||||
await app.wait();
|
||||
assert.ok(await extensions.getExtensionReloadText(), 'Reload was not prompted after extension installation.');
|
||||
});
|
||||
|
||||
it(`installs an extension and checks if it works on restart`, async function () {
|
||||
await extensions.openExtensionsViewlet();
|
||||
await extensions.searchForExtension(extensionName);
|
||||
await app.wait();
|
||||
await extensions.installExtension(extensionName);
|
||||
await app.wait();
|
||||
await extensions.getExtensionReloadText();
|
||||
|
||||
await app.stop();
|
||||
await app.wait(); // wait until all resources are released (e.g. locked local storage)
|
||||
await app.start();
|
||||
await extensions.activateExtension();
|
||||
const statusbarText = await extensions.verifyStatusbarItem();
|
||||
assert.equal(statusbarText, 'VS Code Smoke Test Check', 'Extension contribution text does not match expected.');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function networkAttached(): Promise<boolean> {
|
||||
return new Promise((res, rej) => {
|
||||
dns.resolve('marketplace.visualstudio.com', (err) => {
|
||||
err ? res(false) : res(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { Git } from '../areas/git';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testGit() {
|
||||
describe('Git', () => {
|
||||
let git: Git;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
git = new Git(app, common);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('verifies current changes are picked up by Git viewlet', async function () {
|
||||
const changesCount = await git.getScmIconChanges();
|
||||
assert.equal(changesCount, 2);
|
||||
await git.openGitViewlet();
|
||||
assert.ok(await git.verifyScmChange('app.js'), 'app.js change does not appear in SCM viewlet.');
|
||||
assert.ok(await git.verifyScmChange('launch.json'), 'launch.json change does not appear in SCM viewlet.');
|
||||
});
|
||||
|
||||
it(`verifies 'app.js' diff viewer changes`, async function () {
|
||||
await git.openGitViewlet();
|
||||
await common.openFile('app.js');
|
||||
const original = await git.getOriginalAppJsBodyVarName();
|
||||
assert.equal(original, 'bodyParser', 'Original value from diff view is wrong.');
|
||||
const modified = await git.getModifiedAppJsBodyVarName();
|
||||
assert.equal(modified, 'ydobParser', 'Modified value from diff view is wrong.');
|
||||
});
|
||||
|
||||
it(`stages 'app.js' changes and checks stage count`, async function () {
|
||||
await git.openGitViewlet();
|
||||
await app.wait();
|
||||
await git.stageFile('app.js');
|
||||
const stagedCount = await git.getStagedCount();
|
||||
assert.equal(stagedCount, 1);
|
||||
|
||||
// Return back to unstaged state
|
||||
await git.unstageFile('app.js');
|
||||
});
|
||||
|
||||
it(`stages, commits change to 'app.js' locally and verifies outgoing change`, async function () {
|
||||
await git.openGitViewlet();
|
||||
await app.wait();
|
||||
await git.stageFile('app.js');
|
||||
await git.focusOnCommitBox();
|
||||
await common.type('Test commit');
|
||||
await git.pressCommit();
|
||||
const changes = await git.getOutgoingChanges();
|
||||
assert.equal(changes, ' 0↓ 1↑', 'Changes indicator is wrong in a status bar.');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { IntegratedTerminal } from '../areas/integrated-terminal';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testIntegratedTerminal() {
|
||||
describe('Integrated Terminal', () => {
|
||||
let terminal: IntegratedTerminal;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
terminal = new IntegratedTerminal(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it(`opens terminal, runs 'echo' and verifies the output`, async function () {
|
||||
const command = 'echo test';
|
||||
await terminal.openTerminal(common);
|
||||
await app.wait();
|
||||
await common.type(command);
|
||||
await common.enter();
|
||||
await app.wait();
|
||||
assert.ok(await terminal.commandOutputHas('test'), 'Terminal output does not contain echo.');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { JavaScriptDebug } from '../areas/javascript-debug';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testJavaScriptDebug() {
|
||||
describe('Debugging JavaScript', () => {
|
||||
let jsDebug: JavaScriptDebug;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
jsDebug = new JavaScriptDebug(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('autodetects program attribute for launch.json', async function () {
|
||||
await jsDebug.openDebugViewlet();
|
||||
await jsDebug.pressConfigureLaunchJson();
|
||||
const value = await jsDebug.getProgramConfigValue();
|
||||
process.platform === 'win32' ? assert.equal(value, '${workspaceRoot}\\bin\\www') : assert.equal(value, '${workspaceRoot}/bin/www');
|
||||
});
|
||||
|
||||
it(`can set a breakpoint and verify if it's set`, async function () {
|
||||
await common.openFirstMatchFile('index.js');
|
||||
await jsDebug.setBreakpointOnLine(6);
|
||||
const breakpoint = await jsDebug.verifyBreakpointOnLine(6);
|
||||
assert.ok(breakpoint, 'Breakpoint was not found on line 6.');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { JavaScript } from '../areas/javascript';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testJavaScript() {
|
||||
describe('JavaScript', () => {
|
||||
let js: JavaScript;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
js = new JavaScript(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('shows correct quick outline', async function () {
|
||||
await common.openFirstMatchFile('bin/www');
|
||||
await js.openQuickOutline();
|
||||
await app.wait();
|
||||
const symbols = await common.getQuickOpenElements();
|
||||
assert.equal(symbols, 12, 'Quick outline elements count does not match to expected.');
|
||||
});
|
||||
|
||||
it(`finds 'All References' to 'app'`, async function () {
|
||||
await common.openFirstMatchFile('bin/www');
|
||||
await js.findAppReferences();
|
||||
await app.wait();
|
||||
const titleCount = await js.getTitleReferencesCount();
|
||||
assert.equal(titleCount, 3, 'References count in widget title is not as expected.');
|
||||
const treeCount = await js.getTreeReferencesCount();
|
||||
assert.equal(treeCount, 3, 'References count in tree is not as expected.');
|
||||
});
|
||||
|
||||
it(`renames local 'app' variable`, async function () {
|
||||
await common.openFirstMatchFile('bin/www');
|
||||
|
||||
const newVarName = 'newApp';
|
||||
await js.renameApp(newVarName);
|
||||
await common.enter();
|
||||
const newName = await js.getNewAppName();
|
||||
assert.equal(newName, newVarName);
|
||||
});
|
||||
|
||||
it('folds/unfolds the code correctly', async function () {
|
||||
await common.openFirstMatchFile('bin/www');
|
||||
// Fold
|
||||
await js.toggleFirstCommentFold();
|
||||
const foldedIcon = await js.getFirstCommentFoldedIcon();
|
||||
assert.ok(foldedIcon, 'Folded icon was not found in the margin.');
|
||||
let nextLineNumber = await js.getNextLineNumberAfterFold();
|
||||
assert.equal(nextLineNumber, 7, 'Line number after folded code is wrong.');
|
||||
// Unfold
|
||||
await js.toggleFirstCommentFold();
|
||||
nextLineNumber = await js.getNextLineNumberAfterFold();
|
||||
assert.equal(nextLineNumber, 4, 'Line number with unfolded code is wrong.');
|
||||
});
|
||||
|
||||
it(`verifies that 'Go To Definition' works`, async function () {
|
||||
await common.openFirstMatchFile('app.js');
|
||||
await js.goToExpressDefinition();
|
||||
await app.wait();
|
||||
assert.ok(await common.getTab('index.d.ts'), 'Tab opened when navigating to definition is not as expected.');
|
||||
});
|
||||
|
||||
it(`verifies that 'Peek Definition' works`, async function () {
|
||||
await common.openFirstMatchFile('app.js');
|
||||
await js.peekExpressDefinition();
|
||||
await app.wait();
|
||||
const definitionFilename = await js.getPeekExpressResultName();
|
||||
assert.equal(definitionFilename, 'index.d.ts', 'Peek result is not as expected.');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH, USER_DIR } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { Localization, ViewletType } from '../areas/localization';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testLocalization() {
|
||||
describe('Localization', () => {
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it(`starts with 'DE' locale and verifies title and viewlets text is in German`, async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.test.fullTitle(), this.test.currentRetry(), [WORKSPACE_PATH, '--locale=DE'], [`--user-data-dir=${USER_DIR}`]);
|
||||
common = new CommonActions(app);
|
||||
const locale = new Localization(app);
|
||||
common.removeDirectory(USER_DIR);
|
||||
|
||||
await app.start();
|
||||
|
||||
let text = await locale.getOpenEditorsText();
|
||||
assert.equal(text.toLowerCase(), 'geöffnete editoren');
|
||||
|
||||
await locale.openViewlet(ViewletType.SEARCH);
|
||||
text = await locale.getOpenedViewletTitle();
|
||||
assert.equal(text.toLowerCase(), 'suchen');
|
||||
|
||||
await locale.openViewlet(ViewletType.SCM);
|
||||
await app.wait(); // wait until git extension is loaded
|
||||
text = await locale.getOpenedViewletTitle();
|
||||
assert.equal(text.toLowerCase(), 'quellcodeverwaltung: git');
|
||||
|
||||
await locale.openViewlet(ViewletType.DEBUG);
|
||||
text = await locale.getOpenedViewletTitle();
|
||||
assert.equal(text.toLowerCase(), 'debuggen');
|
||||
|
||||
await locale.openViewlet(ViewletType.EXTENSIONS);
|
||||
text = await locale.getExtensionsSearchPlaceholder();
|
||||
assert.equal(text.toLowerCase(), 'nach erweiterungen im marketplace suchen');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, CODE_WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testMultiRoot() {
|
||||
describe('Multi Root', () => {
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [CODE_WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('shows results from all folders', async function () {
|
||||
await common.openQuickOpen();
|
||||
await app.wait();
|
||||
await common.type('*.*');
|
||||
await app.wait();
|
||||
const elCount = await common.getQuickOpenElements();
|
||||
assert.equal(elCount, 6);
|
||||
});
|
||||
|
||||
it('shows workspace name in title', async function () {
|
||||
await app.wait();
|
||||
const title = await common.getWindowTitle();
|
||||
assert.ok(title.indexOf('smoketest (Workspace)') >= 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { Search } from '../areas/search';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testSearch() {
|
||||
describe('Search', () => {
|
||||
let search: Search;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
search = new Search(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('searches for body & checks for correct result number', async function () {
|
||||
const s = search;
|
||||
await s.openSearchViewlet();
|
||||
await s.searchFor('body');
|
||||
const result = await s.getResultText();
|
||||
assert.equal(result, '7 results in 4 files');
|
||||
});
|
||||
|
||||
it('searches only for *.js files & checks for correct result number', async function () {
|
||||
const s = search;
|
||||
await s.openSearchViewlet();
|
||||
await s.searchFor('body');
|
||||
await s.toggleSearchDetails();
|
||||
await s.searchFor('*.js');
|
||||
const results = await s.getResultText();
|
||||
assert.equal(results, '4 results in 1 file');
|
||||
});
|
||||
|
||||
it('dismisses result & checks for correct result number', async function () {
|
||||
const s = search;
|
||||
await s.openSearchViewlet();
|
||||
await s.searchFor('body');
|
||||
await s.hoverOverResultCount();
|
||||
await s.dismissResult();
|
||||
await app.wait();
|
||||
const result = await s.getResultText();
|
||||
assert.equal(result, '3 results in 3 files', 'Result number after dismissal does not match to expected.');
|
||||
});
|
||||
|
||||
it('replaces first search result with a replace term', async function () {
|
||||
const s = search;
|
||||
await s.openSearchViewlet();
|
||||
await s.searchFor('body');
|
||||
await s.toggleReplace();
|
||||
await s.setReplaceText('ydob');
|
||||
await s.hoverOverResultCount();
|
||||
await s.replaceFirstMatch();
|
||||
await app.wait();
|
||||
await common.saveOpenedFile();
|
||||
const result = await s.getResultText();
|
||||
assert.equal(result, '3 results in 3 files', 'Result number after replacemenet does not match to expected.');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { CommonActions } from '../areas/common';
|
||||
import { StatusBarElement, StatusBar } from '../areas/statusbar';
|
||||
|
||||
let app: SpectronApplication;
|
||||
let common: CommonActions;
|
||||
|
||||
export function testStatusbar() {
|
||||
describe('Status Bar', () => {
|
||||
let statusBar: StatusBar;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
common = new CommonActions(app);
|
||||
statusBar = new StatusBar(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('verifies presence of all default status bar elements', async function () {
|
||||
await app.wait();
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.BRANCH_STATUS), 'Branch indicator is not visible.');
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.FEEDBACK_ICON), 'Feedback icon is not visible.');
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.SYNC_STATUS), 'Sync indicator is not visible.');
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.PROBLEMS_STATUS), 'Problems indicator is not visible.');
|
||||
|
||||
await common.openFirstMatchFile('app.js');
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.ENCODING_STATUS), 'Encoding indicator is not visible.');
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.EOL_STATUS), 'EOL indicator is not visible.');
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.INDENTATION_STATUS), 'Indentation indicator is not visible.');
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.LANGUAGE_STATUS), 'Language indicator is not visible.');
|
||||
assert.ok(await statusBar.isVisible(StatusBarElement.SELECTION_STATUS), 'Selection indicator is not visible.');
|
||||
});
|
||||
|
||||
it(`verifies that 'quick open' opens when clicking on status bar elements`, async function () {
|
||||
await app.wait();
|
||||
await statusBar.clickOn(StatusBarElement.BRANCH_STATUS);
|
||||
assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for branch indicator.');
|
||||
await common.closeQuickOpen();
|
||||
|
||||
await common.openFirstMatchFile('app.js');
|
||||
await statusBar.clickOn(StatusBarElement.INDENTATION_STATUS);
|
||||
assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for indentation indicator.');
|
||||
await common.closeQuickOpen();
|
||||
await statusBar.clickOn(StatusBarElement.ENCODING_STATUS);
|
||||
assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for encoding indicator.');
|
||||
await common.closeQuickOpen();
|
||||
await statusBar.clickOn(StatusBarElement.EOL_STATUS);
|
||||
assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for EOL indicator.');
|
||||
await common.closeQuickOpen();
|
||||
await statusBar.clickOn(StatusBarElement.LANGUAGE_STATUS);
|
||||
assert.ok(await statusBar.isQuickOpenWidgetVisible(), 'Quick open is not opened for language indicator.');
|
||||
await common.closeQuickOpen();
|
||||
});
|
||||
|
||||
it(`verifies that 'Problems View' appears when clicking on 'Problems' status element`, async function () {
|
||||
await statusBar.clickOn(StatusBarElement.PROBLEMS_STATUS);
|
||||
assert.ok(await statusBar.getProblemsView());
|
||||
});
|
||||
|
||||
it(`verifies that 'Tweet us feedback' pop-up appears when clicking on 'Feedback' icon`, async function () {
|
||||
await statusBar.clickOn(StatusBarElement.FEEDBACK_ICON);
|
||||
assert.ok(await statusBar.getFeedbackView());
|
||||
});
|
||||
|
||||
it(`checks if 'Go to Line' works if called from the status bar`, async function () {
|
||||
await common.openFirstMatchFile('app.js');
|
||||
await statusBar.clickOn(StatusBarElement.SELECTION_STATUS);
|
||||
const lineNumber = 15;
|
||||
await common.type(lineNumber.toString());
|
||||
await common.enter();
|
||||
assert.ok(await statusBar.getEditorHighlightedLine(lineNumber), 'Editor does not highlight the line.');
|
||||
});
|
||||
|
||||
it(`verifies if changing EOL is reflected in the status bar`, async function () {
|
||||
await common.openFirstMatchFile('app.js');
|
||||
await statusBar.clickOn(StatusBarElement.EOL_STATUS);
|
||||
await common.selectNextQuickOpenElement();
|
||||
await common.enter();
|
||||
const currentEOL = await statusBar.getEOLMode();
|
||||
assert.equal(currentEOL, 'CRLF');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../spectron/application';
|
||||
import { Tasks } from '../areas/tasks';
|
||||
|
||||
let app: SpectronApplication;
|
||||
|
||||
export function testTasks() {
|
||||
describe('Tasks', () => {
|
||||
let tasks: Tasks;
|
||||
|
||||
beforeEach(async function () {
|
||||
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
|
||||
tasks = new Tasks(app);
|
||||
|
||||
return await app.start();
|
||||
});
|
||||
afterEach(async function () {
|
||||
return await app.stop();
|
||||
});
|
||||
|
||||
it('verifies that eslint task results in 1 problem', async function () {
|
||||
const expectedOutput = '1 problem (0 errors, 1 warning)';
|
||||
await tasks.build();
|
||||
const actualOutput = await tasks.outputContains(expectedOutput);
|
||||
assert.ok(actualOutput, `Output does not contain the following string: '${expectedOutput}'`);
|
||||
});
|
||||
|
||||
it(`is able to select 'Git' output`, async function () {
|
||||
await tasks.build();
|
||||
await app.wait();
|
||||
await tasks.selectOutputViewType('Git');
|
||||
const viewType = await tasks.getOutputViewType();
|
||||
assert.equal(viewType, 'Git');
|
||||
});
|
||||
|
||||
it('ensures that build task produces no-unused-vars message', async function () {
|
||||
await tasks.build();
|
||||
assert.ok(await tasks.outputContains(`'next' is defined but never used`), `Output does not contain no-unused-vars message`);
|
||||
});
|
||||
|
||||
it(`verifies build error is reflected in 'Problems View'`, async function () {
|
||||
await tasks.build();
|
||||
await tasks.openProblemsView();
|
||||
const problemName = await tasks.getProblemsViewFirstElementName();
|
||||
assert.equal(problemName, 'index.js', `'index.js' is not a build error.`);
|
||||
const problemsCount = await tasks.getProblemsViewFirstElementCount();
|
||||
assert.equal(problemsCount, '1', `Problem count is different to expected.`);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user