Merge branch 'smoketest'

This commit is contained in:
Joao 2017-09-05 11:31:37 +02:00
commit 9f05aa015f
60 changed files with 1783 additions and 1151 deletions

3
.gitignore vendored
View file

@ -11,4 +11,5 @@ out-monaco-editor-core/
out-vscode/
out-vscode-min/
build/node_modules
coverage/
coverage/
test_data/

21
.vscode/launch.json vendored
View file

@ -168,6 +168,27 @@
"outFiles": [
"${workspaceRoot}/out/**/*.js"
]
},
{
"type": "node",
"request": "launch",
"name": "Launch Smoke Test",
"program": "${workspaceRoot}/test/smoke/out/main.js",
"cwd": "${workspaceRoot}/test/smoke",
"timeout": 240000,
"port": 9999,
"args": [
"-l",
"${workspaceRoot}/.build/electron/Code - OSS.app/Contents/MacOS/Electron"
],
"outFiles": [
"${cwd}/out/**/*.js"
],
"env": {
"NODE_ENV": "development",
"VSCODE_DEV": "1",
"VSCODE_CLI": "1"
}
}
],
"compounds": [

View file

@ -16,7 +16,8 @@
".build/**": true,
"out*/**": true,
"i18n/**": true,
"extensions/**/out/**": true
"extensions/**/out/**": true,
"test/smoke/out/**": true
},
"tslint.enable": true,
"lcov.path": [
@ -32,4 +33,4 @@
}
}
]
}
}

View file

@ -196,6 +196,7 @@ export abstract class BaseTextEditor extends BaseEditor {
// Update editor options after having set the input. We do this because there can be
// editor input specific options (e.g. an ARIA label depending on the input showing)
this.updateEditorConfiguration();
this._editorContainer.getHTMLElement().setAttribute('aria-label', this.computeAriaLabel());
});
}

View file

@ -565,7 +565,7 @@ export class SCMViewlet extends PersistentViewsViewlet {
}
protected getDefaultViewSize(): number | undefined {
return this.dimension && this.dimension.height / this.views.length;
return this.dimension && this.dimension.height / Math.max(this.views.length, 1);
}
getOptimalWidth(): number {

View file

@ -4,7 +4,8 @@
"main": "./src/main.js",
"scripts": {
"postinstall": "tsc",
"watch": "tsc --watch"
"watch": "tsc --watch",
"mocha": "mocha"
},
"devDependencies": {
"@types/electron": "~1.4.37",

View file

@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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}`);
}
}

View file

@ -25,12 +25,22 @@ export class CommonActions {
}
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.openUserSettings();
await this.spectron.client.keys(['ArrowDown', 'NULL'], false);
await this.spectron.client.waitForElement(`.editable-preferences-editor-container .monaco-editor.focused`);
await this.spectron.client.keys(['ArrowRight', 'NULL'], false);
await this.spectron.client.keys(`"${setting}": "${value}"`);
await this.spectron.wait();
return this.saveOpenedFile();
await this.saveOpenedFile();
}
public async openUserSettings(): Promise<void> {
await this.spectron.command('workbench.action.openGlobalSettings');
await this.spectron.client.waitForElement('.settings-search-input .synthetic-focus');
}
public async openKeybindings(): Promise<void> {
await this.spectron.command('workbench.action.openGlobalKeybindings');
await this.spectron.client.waitForElement('.settings-search-input .synthetic-focus');
}
public async newUntitledFile(): Promise<any> {
@ -46,8 +56,8 @@ export class CommonActions {
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) {
let el = await this.spectron.client.waitForElement(`.tabs-container ${tabSelector}[aria-label="${tabName}, tab"]`);
if (el) {
return el;
}
@ -56,7 +66,13 @@ export class CommonActions {
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"]`);
await this.spectron.client.waitAndClick(`.tabs-container div[aria-selected="false"][aria-label="${tabName}, tab"]`);
await this.spectron.client.waitForElement(`.tabs-container div[aria-selected="true"][aria-label="${tabName}, tab"]`);
return this.waitForEditorFocus();
}
private async waitForEditorFocus(): Promise<void> {
this.spectron.client.waitForElement(`.monaco-editor.focused`);
}
public async openFirstMatchFile(fileName: string): Promise<any> {
@ -67,8 +83,15 @@ export class CommonActions {
return this.spectron.wait();
}
public saveOpenedFile(): Promise<any> {
return this.spectron.command('workbench.action.files.save');
public async saveOpenedFile(): Promise<any> {
try {
await this.spectron.client.waitForElement('.tabs-container .tab.active.dirty');
} catch (e) {
// ignore if there is no dirty file
return Promise.resolve();
}
await this.spectron.command('workbench.action.files.save');
return this.spectron.client.waitForElement('.tabs-container .tab.active.dirty', element => !element);
}
public type(text: string): Promise<any> {
@ -109,7 +132,7 @@ export class CommonActions {
}
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');
const elements = await this.spectron.waitFor(this.spectron.client.waitForElements, 'div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties .monaco-tree-row');
return elements.value.length;
}
@ -121,12 +144,12 @@ export class CommonActions {
selector += '"]';
try {
await this.spectron.waitFor(this.spectron.client.doubleClick, selector);
await this.spectron.client.doubleClickAndWait(selector);
await this.spectron.client.waitForElement(`.tabs-container div[aria-label="${fileName}, tab"]`);
await this.spectron.client.waitForElement(`.monaco-editor.focused`);
} catch (e) {
return Promise.reject(`Cannot fine ${fileName} in a viewlet.`);
}
return this.spectron.wait();
}
public getExtensionSelector(fileName: string): string {

View file

@ -1,64 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../spectron/application';
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;
};
}
}

View file

@ -34,8 +34,8 @@ export class CSS {
throw new Error('No such problem type defined.');
}
let el = await this.spectron.client.element(`.view-overlays .cdr.${selector}`);
if (el.status === 0) {
let el = await this.spectron.client.waitForElement(`.view-overlays .cdr.${selector}`);
if (el) {
return el;
}
@ -52,8 +52,8 @@ export class CSS {
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) {
let el = await this.spectron.client.waitForElement(`div[aria-label="Problems grouped by files"] .icon.${selector}`);
if (el) {
return el;
}

View file

@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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 { ProblemSeverity, Problems } from '../problems/problems';
import { QuickOutline } from '../editor/quickoutline';
import { SettingsEditor } from '../preferences/settings';
describe('CSS', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [WORKSPACE_PATH]);
before(() => app.start());
after(() => app.stop());
it('verifies quick outline', async function () {
await app.workbench.quickopen.openFile('style.css');
const outline = new QuickOutline(app);
await outline.openSymbols();
const elements = await app.client.waitForElements(QuickOutline.QUICK_OPEN_ENTRY_SELECTOR, elements => elements.length === 2);
assert.ok(elements, `Did not find two outline elements`);
});
it('verifies warnings for the empty rule', async function () {
await app.workbench.quickopen.openFile('style.css');
await app.client.waitForElement(`.monaco-editor.focused`);
await app.type('.foo{}');
let warning = await app.client.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.WARNING));
assert.ok(warning, `Warning squiggle is not shown in 'style.css'.`);
const problems = new Problems(app);
await problems.showProblemsView();
warning = await app.client.waitForElement(Problems.getSelectorInProblemsView(ProblemSeverity.WARNING));
assert.ok(warning, 'Warning does not appear in Problems view.');
await problems.hideProblemsView();
});
it('verifies that warning becomes an error once setting changed', async function () {
await new SettingsEditor(app).addUserSetting('css.lint.emptyRules', '"error"');
await app.workbench.quickopen.openFile('style.css');
await app.type('.foo{}');
let error = await app.client.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.ERROR));
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));
assert.ok(error, 'Warning does not appear in Problems view.');
await problems.hideProblemsView();
});
});

View file

@ -16,8 +16,8 @@ export class DataLoss {
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) {
let el = await this.spectron.client.waitForElement(`.tabs-container .tab.dirty${activeSelector}[aria-label="${tabName}, tab"]`);
if (el) {
return el;
}

View file

@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
export class Editor {
constructor(private spectron: SpectronApplication) {
}
public async getEditorFirstLineText(): Promise<string> {
const result = await this.spectron.client.waitForText('.monaco-editor.focused .view-lines span span:nth-child(1)');
return Array.isArray(result) ? result.join() : result;
}
public async waitForHighlightingLine(line: number): Promise<void> {
const currentLineIndex = await this.spectron.client.waitFor<number>(async () => {
const lineNumbers = await this.spectron.webclient.selectorExecute(`.monaco-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;
});
await this.spectron.client.waitForElement(`.monaco-editor .view-overlays>:nth-child(${currentLineIndex}) .current-line`);
}
}

View file

@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { QuickOpen } from '../quickopen/quickopen';
export class QuickOutline extends QuickOpen {
constructor(spectron: SpectronApplication) {
super(spectron);
}
public async openSymbols(): Promise<void> {
await this.spectron.client.waitFor(async () => {
await this.spectron.command('workbench.action.gotoSymbol');
const element = await this.spectron.client.element('div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row[aria-label="body, symbols, picker"] .quick-open-entry');
if (element) {
return element;
}
await this.closeQuickOpen();
});
}
}

View file

@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../../spectron/application';
describe('Explorer', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [WORKSPACE_PATH]);
before(() => app.start());
after(() => app.stop());
it('quick open search produces correct result', async function () {
await app.workbench.quickopen.openQuickOpen();
await app.client.type('.js');
const elements = await app.workbench.quickopen.getQuickOpenElements();
await app.client.keys(['Escape', 'NULL']);
assert.equal(elements.length, 7, 'There are 7 elements in quick open');
});
it('quick open respects fuzzy matching', async function () {
await app.workbench.quickopen.openQuickOpen();
await app.client.type('a.s');
const elements = await app.workbench.quickopen.getQuickOpenElements();
await app.client.keys(['Escape', 'NULL']);
assert.equal(elements.length, 3, 'There are 3 elements in quick open');
});
});

View file

@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
export class Explorer {
constructor(private spectron: SpectronApplication) {
}
public openExplorerView(): Promise<any> {
return this.spectron.command('workbench.view.explorer');
}
public async openFile(fileName: string): Promise<any> {
let selector = `div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.getExtensionSelector(fileName)} explorer-item"]`;
try {
await this.spectron.client.doubleClickAndWait(selector);
await this.spectron.client.waitForElement(`.tabs-container div[aria-label="${fileName}, tab"]`);
await this.spectron.client.waitForElement(`.monaco-editor.focused`);
} catch (e) {
return Promise.reject(`Cannot fine ${fileName} in a viewlet.`);
}
}
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');
}
}

View file

@ -26,7 +26,7 @@ export class Extensions {
await this.spectron.client.clearElement(searchBoxSelector);
try {
await this.spectron.client.click(searchBoxSelector, false);
await this.spectron.client.waitAndClick(searchBoxSelector);
} catch (e) {
return Promise.reject('Failed to click on search box in extensions viewlet.');
}
@ -40,7 +40,7 @@ export class Extensions {
this.viewletExtensionIndex = await this.getExtensionIndex(name, extensionListSelector);
try {
return this.spectron.client.click(`${extensionListSelector}>:nth-child(${this.viewletExtensionIndex}) .extension .extension-action.install`);
return this.spectron.client.waitAndClick(`${extensionListSelector}>:nth-child(${this.viewletExtensionIndex}) .extension .extension-action.install`);
} catch (e) {
return Promise.reject('Failed to click on install button for selected extension.');
}
@ -70,7 +70,7 @@ export class Extensions {
}
private getExtensionIndex(name: string, extensionListSelector: string): Promise<number> {
return this.spectron.waitFor(this.spectron.client.getHTML, extensionListSelector).then(html => {
return this.spectron.waitFor(this.spectron.client.waitForHTML, extensionListSelector).then(html => {
return new Promise<number>((res, rej) => {
let extensionIndex: number = 0;
let extension: boolean;

View file

@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../../spectron/application';
describe('Extensions', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [WORKSPACE_PATH]);
before(() => app.start());
after(() => app.stop());
it(`install and activate vscode-smoketest-check extension`, async function () {
if (app.inDevMode) {
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.commandPallette.runCommand('Smoke Test Check');
const statusbarText = await app.workbench.statusbar.getStatusbarTextByTitle('smoke test');
assert.equal(statusbarText, 'VS Code Smoke Test Check');
});
});

View file

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
export class Extensions {
constructor(private spectron: SpectronApplication) {
}
public async openExtensionsViewlet(): Promise<any> {
await this.spectron.command('workbench.view.extensions');
await this.waitForExtensionsViewlet();
}
public async waitForExtensionsViewlet(): Promise<any> {
await this.spectron.client.waitForElement('div.extensions-viewlet[id="workbench.view.extensions"] .search-box.synthetic-focus[placeholder="Search Extensions in Marketplace"]');
}
public async searchForExtension(name: string): Promise<any> {
const searchBoxSelector = 'div.extensions-viewlet[id="workbench.view.extensions"] .search-box[placeholder="Search Extensions in Marketplace"]';
await this.spectron.client.clearElement(searchBoxSelector);
await this.spectron.client.click(searchBoxSelector);
await this.spectron.client.waitForElement('div.extensions-viewlet[id="workbench.view.extensions"] .search-box.synthetic-focus[placeholder="Search Extensions in Marketplace"]');
await this.spectron.client.keys(name);
}
public async installExtension(name: string): Promise<boolean> {
await this.searchForExtension(name);
await this.spectron.client.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${name}"] .extension li[class='action-item'] .extension-action.install`);
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;
}
}

View file

@ -11,8 +11,8 @@ export class FirstExperience {
}
public async getWelcomeTab(): Promise<any> {
let el = await this.spectron.client.element('.vs_code_welcome_page-name-file-icon');
if (el.status === 0) {
let el = await this.spectron.client.waitForElement('.vs_code_welcome_page-name-file-icon');
if (el) {
return el;
}

View file

@ -1,167 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../spectron/application';
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);
});
});
}
}

View file

@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } 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', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [WORKSPACE_PATH]);
before(() => app.start());
after(() => app.stop());
it('reflects working tree changes', async function () {
await app.workbench.scm.openSCMViewlet();
await app.workbench.openFile('app.js');
await app.type('.foo{}');
await app.workbench.saveOpenedFile();
await app.workbench.openFile('index.jade');
await app.type('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');
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 () {
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 () {
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 change to 'app.js' locally and verifies outgoing change`, async function () {
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('hello world');
await app.client.waitForText(SYNC_STATUSBAR, ' 0↓ 1↑');
});
});

View file

@ -0,0 +1,250 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { SpectronApplication } from '../../spectron/application';
const VIEWLET = 'div[id="workbench.view.scm"]';
const SCM_INPUT = `${VIEWLET} .scm-editor textarea`;
const SCM_RESOURCE = `${VIEWLET} .monaco-list-row > .resource`;
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}"]`;
export interface Change {
id: string;
name: string;
type: string;
actions: { id: string, title: string; }[];
}
export class SCM {
// private editorChangeIndex: number;
constructor(private spectron: SpectronApplication) {
// noop
}
async openSCMViewlet(): Promise<any> {
await this.spectron.command('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();
for (const change of changes) {
if (func(change)) {
return change;
}
}
return undefined;
});
}
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('.decoration-icon') 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,
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 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.click(SCM_INPUT);
await this.spectron.type(message);
await this.spectron.client.click(COMMIT_COMMAND);
}
// async getChanges(expectedCount: number): Promise<Change[]> {
// await this.spectron.client.waitForElements(SCM_RESOURCE, r => r.length === expectedCount);
// }
// public async verifyScmChange(fileName: string): Promise<any> {
// let el;
// try {
// el = await this.spectron.client.waitForElement(`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.waitAndClick('.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.waitAndClick('.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.waitAndClick('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.waitAndClick('.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.waitForHTML, 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);
// });
// });
// }
}

View file

@ -27,7 +27,7 @@ export class IntegratedTerminal {
// If no terminal panel was opened, try triggering terminal from quick open
try {
await this.spectron.client.getHTML(IntegratedTerminal.terminalSelector);
await this.spectron.client.waitForHTML(IntegratedTerminal.terminalSelector);
} catch (e) {
await commonActions.openQuickOpen();
await this.spectron.client.keys('>Toggle Integrated Terminal');
@ -36,8 +36,8 @@ export class IntegratedTerminal {
}
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++) {
const rows = await this.spectron.client.waitForElements(`${IntegratedTerminal.terminalRowsSelector} div`);
for (let i = 0; i < rows.length; i++) {
let rowText;
try {
rowText = await this.spectron.client.getText(`${IntegratedTerminal.terminalRowsSelector}>:nth-child(${i + 1})`);

View file

@ -19,7 +19,7 @@ export class JavaScriptDebug {
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');
await this.spectron.waitFor(this.spectron.client.waitAndClick, 'ul[aria-label="Debug actions"] .action-label.icon.debug-action.configure');
} catch (e) {
return Promise.reject('Clicking on debug configuration gear failed.');
}
@ -43,8 +43,8 @@ export class JavaScriptDebug {
}
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) {
let el = await this.spectron.client.waitForElement(`${this.sidebarSelector}>:nth-child(${lineNumber}) .cgmr.debug-breakpoint-glyph`);
if (el) {
return el;
}

View file

@ -25,7 +25,7 @@ export class JavaScript {
public async findAppReferences(): Promise<any> {
await this.setAppVarSelector();
try {
await this.spectron.client.click(this.appVarSelector, false);
await this.spectron.client.waitAndClick(this.appVarSelector);
} catch (e) {
return Promise.reject(`Failed to select 'app' variable.`);
}
@ -40,16 +40,16 @@ export class JavaScript {
}
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');
const treeElems = await this.spectron.client.waitForElements('.reference-zone-widget.results-loaded .ref-tree.inline .show-twisties .monaco-tree-row');
return treeElems.value.length;
return treeElems.length;
}
public async renameApp(newValue: string): Promise<any> {
await this.setAppVarSelector();
try {
await this.spectron.client.click(this.appVarSelector);
await this.spectron.client.waitAndClick(this.appVarSelector);
} catch (e) {
return Promise.reject(`Failed to select 'app' variable.`);
}
@ -67,7 +67,7 @@ export class JavaScript {
this.foldSelector = `.margin-view-overlays>:nth-child(${this.foldLine})`;
try {
return this.spectron.client.click(`${this.foldSelector} .cldr.folding`);
return this.spectron.client.waitAndClick(`${this.foldSelector} .cldr.folding`);
} catch (e) {
return Promise.reject('Clicking on fold element failed ' + e);
}
@ -78,7 +78,7 @@ export class JavaScript {
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`);
return this.spectron.client.waitForHTML(`${this.foldSelector} .cldr.folding.collapsed`);
}
public async getNextLineNumberAfterFold(): Promise<any> {
@ -92,7 +92,7 @@ export class JavaScript {
public async goToExpressDefinition(): Promise<any> {
await this.setExpressVarSelector();
try {
await this.spectron.client.click(this.expressVarSelector);
await this.spectron.client.waitAndClick(this.expressVarSelector);
} catch (e) {
return Promise.reject(`Clicking on express variable failed: ` + e);
}
@ -103,7 +103,7 @@ export class JavaScript {
public async peekExpressDefinition(): Promise<any> {
await this.setExpressVarSelector();
try {
await this.spectron.client.click(this.expressVarSelector);
await this.spectron.client.waitAndClick(this.expressVarSelector);
} catch (e) {
return Promise.reject('Clicking on express variable failed: ' + e);
}
@ -130,7 +130,7 @@ export class JavaScript {
}
private getLineIndexOfFirst(string: string, selector: string): Promise<number> {
return this.spectron.waitFor(this.spectron.client.getHTML, selector).then(html => {
return this.spectron.waitFor(this.spectron.client.waitForHTML, selector).then(html => {
return new Promise<number>((res, rej) => {
let lineIndex: number = 0;
let stringFound: boolean;
@ -159,7 +159,7 @@ export class JavaScript {
}
private getLineIndexOfFirstFoldableElement(selector: string): Promise<number> {
return this.spectron.waitFor(this.spectron.client.getHTML, selector).then(html => {
return this.spectron.waitFor(this.spectron.client.waitForHTML, selector).then(html => {
return new Promise<number>((res, rej) => {
let lineIndex: number = 0;
let foldFound: boolean;

View file

@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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 { QuickOpen } from '../quickopen/quickopen';
import { Window } from '../window';
describe('Multi Root', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [CODE_WORKSPACE_PATH]);
before(() => app.start());
after(() => app.stop());
it('shows results from all folders', async function () {
let quickOpen = new QuickOpen(app);
await quickOpen.openQuickOpen();
await app.type('*.*');
const elements = await quickOpen.getQuickOpenElements();
assert.equal(elements.length, 6);
});
it('shows workspace name in title', async function () {
const title = await new Window(app).getTitle();
assert.ok(title.indexOf('smoketest (Workspace)') >= 0);
});
});

View file

@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
export class KeybindingsEditor {
constructor(private spectron: SpectronApplication) {
// noop
}
public async openKeybindings(): Promise<void> {
await this.spectron.command('workbench.action.openGlobalKeybindings');
await this.spectron.client.waitForElement('.settings-search-input .synthetic-focus');
}
public async search(text: string, select: boolean = false): Promise<void> {
await this.spectron.type(text);
if (select) {
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');
}
}
public async openDefineKeybindingDialog(): Promise<any> {
await this.spectron.client.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item .action-item .icon.add');
return this.spectron.client.waitForElement('.defineKeybindingWidget .monaco-inputbox.synthetic-focus');
}
public async updateKeybinding(command: string, keys: string[], ariaLabel: string): Promise<any> {
await this.search(command, true);
await this.openDefineKeybindingDialog();
await this.spectron.client.keys(keys);
await this.spectron.client.keys(['Enter', 'NULL']);
await this.spectron.client.waitForElement(`div[aria-label="Keybindings"] div[aria-label="Keybinding is ${ariaLabel}."]`);
}
}

View file

@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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 { ActivityBarPosition } from '../activitybar/activityBar';
describe('Preferences', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [WORKSPACE_PATH]);
before(() => app.start());
after(() => app.stop());
it('turns off editor line numbers and verifies the live change', async function () {
await app.workbench.explorer.openFile('app.js');
let lineNumbers = await app.client.waitForElements('.line-numbers');
assert.ok(!!lineNumbers.length, 'Line numbers are not present in the editor before disabling them.');
await app.workbench.settingsEditor.openUserSettings();
await app.workbench.settingsEditor.focusEditableSettings();
await app.client.keys(`"editor.lineNumbers": "off"`);
await app.workbench.saveOpenedFile();
await app.workbench.selectTab('app.js');
lineNumbers = await app.client.waitForElements('.line-numbers', result => !result || result.length === 0);
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 () {
let activityBarElement = await app.workbench.activitybar.getActivityBar(ActivityBarPosition.LEFT);
assert.ok(activityBarElement, 'Activity bar should be positioned on the left.');
await app.workbench.keybindingsEditor.openKeybindings();
await app.workbench.keybindingsEditor.updateKeybinding('workbench.action.toggleSidebarPosition', ['Control', 'u', 'NULL'], 'Control+U');
await app.client.keys(['Control', 'u', 'NULL']);
activityBarElement = await app.workbench.activitybar.getActivityBar(ActivityBarPosition.RIGHT);
assert.ok(activityBarElement, 'Activity bar was not moved to right after toggling its position.');
});
});

View file

@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { Element } from 'webdriverio';
export enum ActivityBarPosition {
LEFT = 0,
RIGHT = 1
};
export class SettingsEditor {
constructor(private spectron: SpectronApplication) {
// noop
}
public async openUserSettings(): Promise<Element> {
await this.spectron.command('workbench.action.openGlobalSettings');
return this.spectron.client.waitForElement('.settings-search-input input:focus');
}
public async focusEditableSettings(): Promise<void> {
await this.spectron.client.keys(['ArrowDown', 'NULL'], false);
await this.spectron.client.waitForElement(`.editable-preferences-editor-container .monaco-editor.focused`);
await this.spectron.client.keys(['ArrowRight', 'NULL'], false);
}
public async addUserSetting(setting: string, value: string): Promise<void> {
await this.openUserSettings();
// await this.spectron.wait(1);
await this.focusEditableSettings();
await this.spectron.client.keys(`"${setting}": ${value}`);
await this.spectron.workbench.saveOpenedFile();
}
}

View file

@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
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.command('workbench.actions.view.problems');
await this.spectron.client.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR);
}
}
public async hideProblemsView(): Promise<any> {
if (await this.isVisible()) {
await this.spectron.command('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 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 ? 'greensquiggly' : 'redsquiggly';
return `.view-overlays .cdr.${selector}`;
}
}

View file

@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { Element } from 'webdriverio';
export class QuickOpen {
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) {
}
public async openQuickOpen(): Promise<void> {
await this.spectron.command('workbench.action.quickOpen');
await this.waitForQuickOpenOpened();
}
public async closeQuickOpen(): Promise<void> {
await this.spectron.command('workbench.action.closeQuickOpen');
await this.waitForQuickOpenClosed();
}
public async getQuickOpenElements(): Promise<Element[]> {
return this.spectron.client.waitForElements(QuickOpen.QUICK_OPEN_ENTRY_SELECTOR);
}
public async openFile(fileName: string): Promise<void> {
await this.openQuickOpen();
await this.spectron.type(fileName);
await this.getQuickOpenElements();
await this.spectron.client.keys(['Enter', 'NULL']);
await this.spectron.client.waitForElement(`.tabs-container div[aria-selected="true"][aria-label="${fileName}, tab"]`);
await this.spectron.client.waitForElement(`div.editor-container[aria-label="${fileName}. Text file editor., Group 1."]`);
await this.spectron.workbench.waitForEditorFocus(fileName);
}
protected waitForQuickOpenOpened(): Promise<Element> {
return this.spectron.client.waitForElement('div.quick-open-widget[aria-hidden="false"]');
}
protected waitForQuickOpenClosed(): Promise<Element> {
return this.spectron.client.waitForElement('div.quick-open-widget[aria-hidden="true"]');
}
public async isQuickOpenVisible(): Promise<boolean> {
await this.waitForQuickOpenOpened();
return true;
}
public async submit(text: string): Promise<void> {
await this.spectron.type(text);
await this.spectron.client.keys(['Enter', 'NULL']);
await this.waitForQuickOpenClosed();
}
public async selectQuickOpenElement(index: number): Promise<void> {
await this.waitForQuickOpenOpened();
for (let from = 0; from < index; from++) {
await this.spectron.client.keys(['ArrowDown', 'NULL']);
this.spectron.wait(3);
}
await this.spectron.client.keys(['Enter', 'NULL']);
await this.waitForQuickOpenClosed();
}
}

View file

@ -1,74 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../spectron/application';
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);
}
}
}

View file

@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { SpectronApplication, LATEST_PATH, WORKSPACE_PATH } from '../../spectron/application';
describe('Search', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [WORKSPACE_PATH]);
before(() => app.start());
after(() => app.stop());
it('searches for body & checks for correct result number', async function () {
await app.workbench.search.openSearchViewlet();
await app.workbench.search.searchFor('body');
const result = await app.workbench.search.getResultText();
assert.equal(result, '7 results in 4 files');
});
it('searches only for *.js files & checks for correct result number', async function () {
await app.workbench.search.openSearchViewlet();
await app.workbench.search.searchFor('body');
await app.workbench.search.showQueryDetails();
await app.workbench.search.setFilesToIncludeTextAndSearch('*.js');
await app.workbench.search.submitSearch();
const results = await app.workbench.search.getResultText();
await app.workbench.search.setFilesToIncludeTextAndSearch('');
await app.workbench.search.hideQueryDetails();
assert.equal(results, '4 results in 1 file');
});
it('dismisses result & checks for correct result number', async function () {
await app.workbench.search.openSearchViewlet();
await app.workbench.search.searchFor('body');
await app.workbench.search.removeFileMatch(1);
const result = await app.workbench.search.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 () {
await app.workbench.search.openSearchViewlet();
await app.workbench.search.searchFor('body');
await app.workbench.search.setReplaceText('ydob');
await app.workbench.search.replaceFileMatch(1);
await app.workbench.saveOpenedFile();
const result = await app.workbench.search.getResultText();
assert.equal(result, '3 results in 3 files', 'Result number after replacemenet does not match to expected.');
});
});

View file

@ -0,0 +1,96 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
export class Search {
static SEARCH_VIEWLET_XPATH = 'div[id="workbench.view.search"] .search-viewlet';
constructor(private spectron: SpectronApplication) {
// noop
}
public async openSearchViewlet(): Promise<any> {
if (!await this.isSearchViewletFocused()) {
await this.spectron.command('workbench.view.search');
await this.spectron.client.waitForElement(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .monaco-inputbox.synthetic-focus input[placeholder="Search"]`);
}
}
public async isSearchViewletFocused(): Promise<boolean> {
const element = await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .monaco-inputbox.synthetic-focus input[placeholder="Search"]`);
return !!element;
}
public async searchFor(text: string): Promise<void> {
const searchBoxSelector = `${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox input[placeholder="Search"]`;
await this.spectron.client.clearElement(searchBoxSelector);
await this.spectron.client.click(searchBoxSelector);
await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox.synthetic-focus input[placeholder="Search"]`);
await this.spectron.client.keys(text);
await this.submitSearch();
}
public async submitSearch(): Promise<void> {
await this.spectron.client.click(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox input[placeholder="Search"]`);
await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox.synthetic-focus input[placeholder="Search"]`);
await this.spectron.client.keys(['NULL', 'Enter', 'NULL'], false);
await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .messages[aria-hidden="false"]`);
}
public async setFilesToIncludeTextAndSearch(text: string): Promise<void> {
await this.spectron.client.click(`${Search.SEARCH_VIEWLET_XPATH} .query-details .monaco-inputbox input[aria-label="Search Include Patterns"]`);
await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .query-details .monaco-inputbox.synthetic-focus input[aria-label="Search Include Patterns"]`);
await this.spectron.client.clearElement(`${Search.SEARCH_VIEWLET_XPATH} .query-details .monaco-inputbox.synthetic-focus input[aria-label="Search Include Patterns"]`);
if (text) {
await this.spectron.client.keys(text);
}
}
public async showQueryDetails(): Promise<void> {
if (!await this.areDetailsVisible()) {
await this.spectron.client.waitAndClick(`${Search.SEARCH_VIEWLET_XPATH} .query-details .more`);
}
}
public async hideQueryDetails(): Promise<void> {
if (await this.areDetailsVisible()) {
await this.spectron.client.waitAndClick(`${Search.SEARCH_VIEWLET_XPATH} .query-details.more .more`);
}
}
public async areDetailsVisible(): Promise<boolean> {
const element = await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .query-details.more`);
return !!element;
}
public async removeFileMatch(index: number): Promise<void> {
await this.spectron.client.waitAndmoveToObject(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch`);
const file = await this.spectron.client.waitForText(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`);
await this.spectron.client.click(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-remove`);
await this.spectron.client.waitForText(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`, void 0, result => result !== file);
}
public async setReplaceText(text: string): Promise<void> {
await this.spectron.client.waitAndClick(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .monaco-button.toggle-replace-button.collapse`);
await this.spectron.client.waitAndClick(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .replace-container .monaco-inputbox input[title="Replace"]`);
await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`);
await this.spectron.client.setValue(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`, text);
}
public async replaceFileMatch(index: number): Promise<void> {
await this.spectron.client.waitAndmoveToObject(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch`);
await this.spectron.client.click(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-replace-all`);
}
public async getResultText(): Promise<string> {
return this.spectron.client.waitForText(`${Search.SEARCH_VIEWLET_XPATH} .messages[aria-hidden="false"] .message>p`);
}
}

View file

@ -1,103 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../spectron/application';
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`);
}
}

View file

@ -0,0 +1,83 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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 { StatusBarElement } from './statusbar';
describe('Statusbar', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [WORKSPACE_PATH]);
before(() => app.start());
after(() => app.stop());
it('verifies presence of all default status bar elements', async function () {
assert.ok(app.workbench.statusbar.isVisible(StatusBarElement.BRANCH_STATUS), 'Branch indicator is not visible.');
assert.ok(app.workbench.statusbar.isVisible(StatusBarElement.FEEDBACK_ICON), 'Feedback icon is not visible.');
assert.ok(app.workbench.statusbar.isVisible(StatusBarElement.SYNC_STATUS), 'Sync indicator is not visible.');
assert.ok(app.workbench.statusbar.isVisible(StatusBarElement.PROBLEMS_STATUS), 'Problems indicator is not visible.');
await app.workbench.quickopen.openFile('app.js');
assert.ok(app.workbench.statusbar.isVisible(StatusBarElement.ENCODING_STATUS), 'Encoding indicator is not visible.');
assert.ok(app.workbench.statusbar.isVisible(StatusBarElement.EOL_STATUS), 'EOL indicator is not visible.');
assert.ok(app.workbench.statusbar.isVisible(StatusBarElement.INDENTATION_STATUS), 'Indentation indicator is not visible.');
assert.ok(app.workbench.statusbar.isVisible(StatusBarElement.LANGUAGE_STATUS), 'Language indicator is not visible.');
assert.ok(app.workbench.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.workbench.statusbar.clickOn(StatusBarElement.BRANCH_STATUS);
assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for branch indicator.');
await app.workbench.quickopen.closeQuickOpen();
await app.workbench.quickopen.openFile('app.js');
await app.workbench.statusbar.clickOn(StatusBarElement.INDENTATION_STATUS);
assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for indentation indicator.');
await app.workbench.quickopen.closeQuickOpen();
await app.workbench.statusbar.clickOn(StatusBarElement.ENCODING_STATUS);
assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for encoding indicator.');
await app.workbench.quickopen.closeQuickOpen();
await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS);
assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for EOL indicator.');
await app.workbench.quickopen.closeQuickOpen();
await app.workbench.statusbar.clickOn(StatusBarElement.LANGUAGE_STATUS);
assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for language indicator.');
await app.workbench.quickopen.closeQuickOpen();
});
it(`verifies that 'Problems View' appears when clicking on 'Problems' status element`, async function () {
await app.workbench.statusbar.clickOn(StatusBarElement.PROBLEMS_STATUS);
assert.ok(await app.workbench.problems.isVisible());
});
it(`verifies that 'Tweet us feedback' pop-up appears when clicking on 'Feedback' icon`, async function () {
if (app.inDevMode) {
return;
}
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 () {
await app.workbench.quickopen.openFile('app.js');
await app.workbench.statusbar.clickOn(StatusBarElement.SELECTION_STATUS);
assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened line number selection.');
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 () {
await app.workbench.quickopen.openFile('app.js');
await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS);
assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened line number selection.');
app.workbench.quickopen.selectQuickOpenElement(1);
await app.workbench.statusbar.waitForEOL('CRLF');
});
});

View file

@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
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) {
}
public async isVisible(element: StatusBarElement): Promise<boolean> {
return this.spectron.client.isVisible(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} .octicon.octicon-git-branch`;
case StatusBarElement.SYNC_STATUS:
return `${this.mainSelector} .octicon.octicon-sync`;
case StatusBarElement.PROBLEMS_STATUS:
return `${this.mainSelector} .task-statusbar-item[title="Problems"]`;
case StatusBarElement.SELECTION_STATUS:
return `${this.mainSelector} .editor-status-selection`;
case StatusBarElement.INDENTATION_STATUS:
return `${this.mainSelector} .editor-status-indentation`;
case StatusBarElement.ENCODING_STATUS:
return `${this.mainSelector} .editor-status-encoding`;
case StatusBarElement.EOL_STATUS:
return `${this.mainSelector} .editor-status-eol`;
case StatusBarElement.LANGUAGE_STATUS:
return `${this.mainSelector} .editor-status-mode`;
case StatusBarElement.FEEDBACK_ICON:
return `${this.mainSelector} .dropdown.send-feedback`;
default:
throw new Error(element);
}
}
}

View file

@ -3,15 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const MochaTest = require('mocha');
const path = require('path');
import { SpectronApplication } from '../spectron/application';
const mochaTest = new MochaTest({
timeout: 60000,
slow: 10000,
useColors: true
});
mochaTest.addFile(path.join(__dirname, 'test.js'));
mochaTest.run((failures) => {
process.exit(failures);
});
export class Window {
constructor(private spectron: SpectronApplication) {
}
public async getTitle(): Promise<string> {
return this.spectron.client.getTitle();
}
}

View file

@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { QuickOpen } from '../quickopen/quickopen';
export class CommandPallette extends QuickOpen {
constructor(spectron: SpectronApplication) {
super(spectron);
}
public async runCommand(commandText: string): Promise<void> {
await this.spectron.command('workbench.action.showCommands');
await this.waitForQuickOpenOpened();
await this.spectron.type(commandText);
await this.spectron.client.waitForText(`div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row:nth-child(1) .quick-open-entry a.label-name span.monaco-highlighted-label span.highlight`, commandText);
await this.spectron.client.waitAndClick(`div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row:nth-child(1) .quick-open-entry`);
}
}

View file

@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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 * as path from 'path';
describe('Dataloss', () => {
let app: SpectronApplication = new SpectronApplication(LATEST_PATH, '', 0, [WORKSPACE_PATH], [`--user-data-dir=${path.join(USER_DIR, new Date().getTime().toString(), 'dataloss')}`]);
before(() => app.start());
after(() => app.stop());
it(`verifies that 'hot exit' works for dirty files`, async function () {
const textToType = 'Hello, Code', textToTypeInUntitled = 'Hello, Unitled Code', fileName = 'readme.md', untitled = 'Untitled-1';
await app.workbench.newUntitledFile();
await app.client.type(textToTypeInUntitled);
await app.workbench.explorer.openFile(fileName);
await app.client.type(textToType);
await app.reload();
assert.ok(await app.workbench.waitForActiveOpen(fileName, true), `${fileName} tab is not present or is not active after reopening.`);
let actual = await app.workbench.editor.getEditorFirstLineText();
assert.ok(actual.startsWith(textToType), `${actual} did not start with ${textToType}`);
assert.ok(await app.workbench.waitForOpen(untitled, true), `${untitled} tab is not present after reopening.`);
await app.workbench.selectTab('Untitled-1', true);
actual = await app.workbench.editor.getEditorFirstLineText();
assert.ok(actual.startsWith(textToTypeInUntitled), `${actual} did not start with ${textToTypeInUntitled}`);
});
});

View file

@ -0,0 +1,101 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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 { Util } from '../../helpers/utilities';
let app: SpectronApplication;
export function testDataMigration() {
if (!STABLE_PATH) {
return;
}
describe('Data Migration', () => {
afterEach(async function () {
await app.stop();
await Util.rimraf(USER_DIR);
return await Util.rimraf(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}`]);
}
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 app.workbench.newUntitledFile();
await app.type(textToType);
await app.stop();
await app.wait(.5); // 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 app.workbench.waitForActiveOpen('Untitled-1', true), `Untitled-1 tab is not present after migration.`);
const actual = await app.workbench.editor.getEditorFirstLineText();
assert.ok(actual.startsWith(textToType), `${actual} did not start with ${textToType}`);
});
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 Util.removeFile(`${fileName}`);
await app.start();
await app.workbench.waitForActiveOpen(fileName);
await app.type(firstTextPart);
await app.workbench.saveOpenedFile();
await app.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 app.workbench.waitForActiveOpen(fileName.split('/')[1]), `Untitled-1 tab is not present after migration.`);
const actual = await app.workbench.editor.getEditorFirstLineText();
assert.ok(actual.startsWith(firstTextPart.concat(secondTextPart)), `${actual} did not start with ${firstTextPart.concat(secondTextPart)}`);
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';
setupSpectron(this, STABLE_PATH, [WORKSPACE_PATH]);
await app.start();
await app.workbench.quickopen.openFile(fileName1);
await app.workbench.quickopen.openFile(fileName2);
await app.workbench.quickopen.openFile(fileName3);
await app.stop();
setupSpectron(this, LATEST_PATH, [WORKSPACE_PATH]);
await app.start();
assert.ok(await app.workbench.waitForOpen(fileName1), `${fileName1} tab was not restored after migration.`);
assert.ok(await app.workbench.waitForOpen(fileName2), `${fileName2} tab was not restored after migration.`);
assert.ok(await app.workbench.waitForOpen(fileName3), `${fileName3} tab was not restored after migration.`);
});
});
}

View file

@ -0,0 +1,90 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { Explorer } from '../explorer/explorer';
import { ActivityBar } from '../activitybar/activityBar';
import { Element } from 'webdriverio';
import { QuickOpen } from '../quickopen/quickopen';
import { Extensions } from '../extensions/extensions';
import { CommandPallette } from './commandPallette';
import { Search } from '../search/search';
import { Editor } from '../editor/editor';
import { SCM } from '../git/scm';
import { StatusBar } from '../statusbar/statusbar';
import { Problems } from '../problems/problems';
import { SettingsEditor } from '../preferences/settings';
import { KeybindingsEditor } from '../preferences/keybindings';
export class Workbench {
readonly explorer: Explorer;
readonly activitybar: ActivityBar;
readonly commandPallette: CommandPallette;
readonly quickopen: QuickOpen;
readonly search: Search;
readonly extensions: Extensions;
readonly editor: Editor;
readonly scm: SCM;
readonly statusbar: StatusBar;
readonly problems: Problems;
readonly settingsEditor: SettingsEditor;
readonly keybindingsEditor: KeybindingsEditor;
constructor(private spectron: SpectronApplication) {
this.explorer = new Explorer(spectron);
this.activitybar = new ActivityBar(spectron);
this.quickopen = new QuickOpen(spectron);
this.commandPallette = new CommandPallette(spectron);
this.search = new Search(spectron);
this.extensions = new Extensions(spectron);
this.editor = new Editor(spectron);
this.scm = new SCM(spectron);
this.statusbar = new StatusBar(spectron);
this.problems = new Problems(spectron);
this.settingsEditor = new SettingsEditor(spectron);
this.keybindingsEditor = new KeybindingsEditor(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.command('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<any> {
await this.spectron.client.waitAndClick(`.tabs-container div.tab[aria-label="${tabName}, tab"]`);
await this.waitForActiveOpen(tabName);
return this.waitForEditorFocus(tabName, untitled);
}
public async waitForEditorFocus(fileName: string, untitled: boolean = false): Promise<Element> {
return this.spectron.client.waitForElement(`.editor-container[aria-label="${fileName}. ${untitled ? 'Untitled file text editor.' : 'Text file editor.'}, Group 1."] .monaco-editor.focused`);
}
public async waitForActiveOpen(fileName: string, isDirty: boolean = false): Promise<boolean> {
return this.spectron.client.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][aria-label="${fileName}, tab"]`).then(() => true);
}
public async waitForOpen(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<any> {
await this.spectron.command('workbench.action.files.newUntitledFile');
await this.waitForActiveOpen('Untitled-1');
await this.waitForEditorFocus('Untitled-1', true);
}
async openFile(fileName: string): Promise<void> {
await this.quickopen.openFile(fileName);
await this.spectron.client.waitForElement(`.monaco-editor.focused`);
}
}

View file

@ -8,7 +8,12 @@ var fs = require('fs');
const __testTime = new Date().toISOString();
export class Screenshot {
export interface IScreenshot {
capture(): Promise<void>;
}
export class Screenshot implements IScreenshot {
private index: number = 0;
private testPath: string;
@ -20,7 +25,7 @@ export class Screenshot {
this.createFolder(this.testPath);
}
public async capture(): Promise<any> {
public async capture(): Promise<void> {
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()));
}
@ -37,4 +42,12 @@ export class Screenshot {
private sanitizeFolderName(name: string): string {
return name.replace(/[&*:\/]/g, '');
}
}
export class NullScreenshot implements IScreenshot {
public async capture(): Promise<void> {
return Promise.resolve();
}
}

View file

@ -34,4 +34,25 @@ export class Util {
});
});
}
public static 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;
}
}
}
}

View file

@ -8,6 +8,10 @@ import * as https from 'https';
import * as cp from 'child_process';
import * as path from 'path';
import * as mkdirp from 'mkdirp';
import * as minimist from 'minimist';
const [, , ...args] = process.argv;
const opts = minimist(args, { string: ['build', 'stable-build'] });
const testDataPath = path.join(__dirname, '..', 'test_data');
const workspacePath = path.join(testDataPath, 'smoketest.code-workspace');
@ -32,7 +36,7 @@ function getDevElectronPath(): string {
switch (process.platform) {
case 'darwin':
return path.join(buildPath, `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron');
return path.join(buildPath, 'electron', `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron');
case 'linux':
return path.join(buildPath, 'electron', `${product.applicationName}`);
case 'win32':
@ -42,7 +46,8 @@ function getDevElectronPath(): string {
}
}
let [, , testCodePath, stableCodePath] = process.argv;
let testCodePath = opts.build;
let stableCodePath = opts['stable-build'];
if (testCodePath) {
process.env.VSCODE_PATH = testCodePath;
@ -86,8 +91,10 @@ function toUri(path: string): string {
}
async function main(): Promise<void> {
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 from ${keybindingsUrl}...`);
console.log('*** Fetching keybindings...');
await new Promise((c, e) => {
https.get(keybindingsUrl, res => {
@ -100,7 +107,7 @@ async function main(): Promise<void> {
});
if (!fs.existsSync(workspacePath)) {
console.log('Creating workspace file...');
console.log('*** Creating workspace file...');
const workspace = {
id: (Date.now() + Math.round(Math.random() * 1000)).toString(),
folders: [
@ -114,21 +121,50 @@ async function main(): Promise<void> {
}
if (!fs.existsSync(testRepoLocalDir)) {
console.log('Cloning test project repository...');
console.log('*** Cloning test project repository...');
cp.spawnSync('git', ['clone', testRepoUrl, testRepoLocalDir]);
} else {
console.log('Cleaning test project repository...');
console.log('*** Cleaning test project repository...');
cp.spawnSync('git', ['fetch'], { cwd: testRepoLocalDir });
cp.spawnSync('git', ['reset', '--hard', 'FETCH_HEAD'], { cwd: testRepoLocalDir });
cp.spawnSync('git', ['clean', '-xdf'], { cwd: testRepoLocalDir });
}
console.log('Running npm install...');
cp.execSync('npm install', { cwd: testRepoLocalDir, stdio: 'inherit' });
console.log('*** Running npm install...');
// cp.execSync('npm install', { cwd: testRepoLocalDir, stdio: 'inherit' });
console.log('Running tests...');
const mocha = cp.spawnSync(process.execPath, ['out/mocha-runner.js'], { cwd: path.join(__dirname, '..'), stdio: 'inherit' });
process.exit(mocha.status);
console.log('*** Smoketest setup done!\n');
}
main().catch(fail);
/**
* 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 () => main());
import './areas/css/css.test';
import './areas/explorer/explorer.test';
import './areas/preferences/preferences.test';
import './areas/multiroot/multiroot.test';
import './areas/extensions/extensions.test';
import './areas/search/search.test';
import './areas/workbench/data-loss.test';
import './areas/git/git.test';
import './areas/statusbar/statusbar.test';
// import './areas/workbench/data-migration.test';

View file

@ -3,38 +3,37 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Application } from 'spectron';
import { Application, SpectronClient as WebClient } from 'spectron';
import { SpectronClient } from './client';
import { Screenshot } from '../helpers/screenshot';
var fs = require('fs');
var path = require('path');
import { NullScreenshot, IScreenshot, Screenshot } from '../helpers/screenshot';
import { Workbench } from '../areas/workbench/workbench';
import * as fs from 'fs';
import * as path from 'path';
export const LATEST_PATH = process.env.VSCODE_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';
export const USER_DIR = path.join(__dirname, '../../test_data/temp_user_dir');
export const EXTENSIONS_DIR = path.join(__dirname, 'test_data/temp_extensions_dir');
/**
* Wraps Spectron's Application instance with its used methods.
*/
export class SpectronApplication {
public client: SpectronClient;
public readonly client: SpectronClient;
public readonly workbench: Workbench;
private spectron: Application;
private keybindings: any[];
private screenshot: Screenshot;
private screenshot: IScreenshot;
private readonly sampleExtensionsDir: string = 'test_data/sample_extensions_dir';
private readonly sampleExtensionsDir: string = path.join(EXTENSIONS_DIR, new Date().getTime().toString());
private readonly pollTrials = 50;
private readonly pollTimeout = 1; // in secs
constructor(electronPath: string, testName: string, private testRetry: number, args?: string[], chromeDriverArgs?: string[]) {
if (!args) {
args = [];
}
constructor(electronPath: string, testName: string, private testRetry: number, args: string[] = [], chromeDriverArgs: string[] = []) {
// Prevent 'Getting Started' web page from opening on clean user-data-dir
args.push('--skip-getting-started');
@ -49,6 +48,16 @@ export class SpectronApplication {
if (!extensionDirIsSet) {
args.push(`--extensions-dir=${this.sampleExtensionsDir}`);
}
let userDataDirIsSet = false;
for (let arg of chromeDriverArgs) {
if (arg.startsWith('--user-data-dir')) {
userDataDirIsSet = true;
break;
}
}
if (!userDataDirIsSet) {
chromeDriverArgs.push(`--user-data-dir=${path.join(USER_DIR, new Date().getTime().toString())}`);
}
const repo = process.env.VSCODE_REPOSITORY;
if (repo) {
@ -63,21 +72,38 @@ export class SpectronApplication {
requireName: 'nodeRequire'
});
this.testRetry += 1; // avoid multiplication by 0 for wait times
this.screenshot = new Screenshot(this, testName, testRetry);
this.screenshot = args.indexOf('--no-screenshot') === -1 ? new NullScreenshot() : new Screenshot(this, testName, testRetry);
this.client = new SpectronClient(this.spectron, this.screenshot);
this.retrieveKeybindings();
this.workbench = new Workbench(this);
}
public get inDevMode(): boolean {
return process.env.VSCODE_DEV === '1';
}
public get app(): Application {
return this.spectron;
}
public get webclient(): WebClient {
return this.spectron.client;
}
public async start(): Promise<any> {
await this.spectron.start();
await this.focusOnWindow(1); // focuses on main renderer window
await this.checkWindowReady();
}
public async reload(): Promise<any> {
await this.workbench.commandPallette.runCommand('Reload Window');
// TODO @sandy: Find a proper condition to wait for reload
await this.wait(.5);
await this.client.waitForHTML('[id="workbench.main.container"]');
}
public async stop(): Promise<any> {
if (this.spectron && this.spectron.isRunning()) {
return await this.spectron.stop();
@ -88,8 +114,8 @@ export class SpectronApplication {
return this.callClientAPI(func, args);
}
public wait(): Promise<any> {
return new Promise(resolve => setTimeout(resolve, this.testRetry * this.pollTimeout * 1000));
public wait(seconds: number = this.testRetry * this.pollTimeout): Promise<any> {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}
public focusOnWindow(index: number): Promise<any> {
@ -97,7 +123,9 @@ export class SpectronApplication {
}
private async checkWindowReady(): Promise<any> {
await this.waitFor(this.spectron.client.getHTML, '[id="workbench.main.container"]');
await this.client.waitForHTML('[id="workbench.main.container"]');
await this.client.waitForElement('.explorer-folders-view');
await this.client.waitForElement(`.editor-container[id="workbench.editor.walkThroughPart"] .welcomePage`);
}
private retrieveKeybindings() {
@ -143,7 +171,7 @@ export class SpectronApplication {
public command(command: string, capture?: boolean): 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.commandPallette.runCommand(command);
}
const keys: string = binding.key;
@ -173,4 +201,24 @@ export class SpectronApplication {
return key.length === 1 ? key : key.charAt(0).toUpperCase() + key.slice(1);
};
}
// TODO: Sandy remove this
public 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.client.keys(toType, false);
await this.client.keys(['NULL']);
await type(i + 1);
};
return type(0);
});
}
}

View file

@ -4,15 +4,18 @@
*--------------------------------------------------------------------------------------------*/
import { Application } from 'spectron';
import { Screenshot } from '../helpers/screenshot';
import { IScreenshot } from '../helpers/screenshot';
import { RawResult, Element } from 'webdriverio';
/**
* Abstracts the Spectron's WebdriverIO managed client property on the created Application instances.
*/
export class SpectronClient {
constructor(private spectron: Application, private shot: Screenshot) {
// noop
private readonly retryCount = 50;
private readonly retryDuration = 100; // in milliseconds
constructor(public spectron: Application, private shot: IScreenshot) {
}
public windowByIndex(index: number): Promise<any> {
@ -29,19 +32,30 @@ export class SpectronClient {
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);
public async waitForText(selector: string, text?: string, accept?: (result: string) => boolean): Promise<string> {
await this.screenshot();
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);
public async waitForHTML(selector: string, accept: (result: string) => boolean = (result: string) => !!result): Promise<any> {
await this.screenshot();
return this.waitFor(() => this.spectron.client.getHTML(selector), accept, `getHTML with selector ${selector}`);
}
public async waitAndClick(selector: string): Promise<any> {
await this.screenshot();
return this.waitFor(() => this.spectron.client.click(selector), void 0, `click with selector ${selector}`);
}
public async click(selector: string): Promise<any> {
await this.screenshot();
return this.spectron.client.click(selector);
}
public async doubleClick(selector: string, capture: boolean = true): Promise<any> {
public async doubleClickAndWait(selector: string, capture: boolean = true): Promise<any> {
await this.screenshot(capture);
return this.spectron.client.doubleClick(selector);
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> {
@ -59,19 +73,42 @@ export class SpectronClient {
return this.spectron.client.moveToObject(selector);
}
public async waitAndmoveToObject(selector: string): Promise<any> {
await this.screenshot();
return this.waitFor(() => this.spectron.client.moveToObject(selector), void 0, `move to object with selector ${selector}`);
}
public async setValue(selector: string, text: string, capture: boolean = true): Promise<any> {
await this.screenshot(capture);
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);
public async waitForElements(selector: string, accept: (result: Element[]) => boolean = result => result.length > 0): Promise<Element[]> {
await this.screenshot(true);
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);
public async waitForElement(selector: string, accept: (result: Element | undefined) => boolean = result => !!result): Promise<Element> {
await this.screenshot();
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 element(selector: string): Promise<Element> {
await this.screenshot();
return this.spectron.client.element(selector)
.then(result => result.value);
}
public async waitForActiveElement(accept: (result: Element | undefined) => boolean = result => !!result): Promise<any> {
await this.screenshot();
return this.waitFor<RawResult<Element>>(() => this.spectron.client.elementActive(), result => accept(result ? result.value : void 0), `elementActive`);
}
public async waitForAttribute(selector: string, attribute: string, accept: (result: string) => boolean = result => !!result): Promise<string> {
await this.screenshot();
return this.waitFor<string>(() => this.spectron.client.getAttribute(selector), accept, `attribute with selector ${selector}`);
}
public async dragAndDrop(sourceElem: string, destinationElem: string, capture: boolean = true): Promise<any> {
@ -111,17 +148,58 @@ export class SpectronClient {
return this.spectron.client.isVisible(selector);
}
public getTitle(): string {
public 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 async screenshot(capture: boolean = true): Promise<any> {
try {
await this.shot.capture();
} catch (e) {
throw new Error(`Screenshot could not be captured: ${e}`);
}
}
public async waitFor<T>(func: () => T | Promise<T | undefined>, accept?: (result: T) => boolean | Promise<boolean>, timeoutMessage?: string): Promise<T>;
public async waitFor<T>(func: () => T | Promise<T>, accept: (result: T) => boolean | Promise<boolean> = result => !!result, timeoutMessage?: string): Promise<T> {
let trial = 1;
while (true) {
if (trial > this.retryCount) {
throw new Error(`${timeoutMessage}: Timed out after ${this.retryCount * this.retryDuration} seconds.`);
}
let result;
try {
result = await func();
} catch (e) {
}
if (accept(result)) {
return result;
}
await new Promise(resolve => setTimeout(resolve, this.retryDuration));
trial++;
}
}
public 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, false);
await this.keys(['NULL']);
await type(i + 1);
};
return type(0);
});
}
}

View file

@ -1,40 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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
}
});

View file

@ -1,57 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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.');
});
});
}

View file

@ -1,61 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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`);
});
});
}

View file

@ -1,74 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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.');
});
});
}

View file

@ -1,43 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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);
});
});
}

View file

@ -1,74 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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);
});
});
}

View file

@ -1,69 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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.');
});
});
}

View file

@ -1,43 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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);
});
});
}

View file

@ -1,73 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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.');
});
});
}

View file

@ -1,94 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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');
});
});
}

View file

@ -0,0 +1,3 @@
--timeout 60000
--slow 10000
out/main.js

View file

@ -18,4 +18,4 @@
"exclude": [
"node_modules"
]
}
}