提交 6c81aa26 编写于 作者: M Michel Kaporin 提交者: GitHub

Merge pull request #27843 from Microsoft/michelkaporin/smoketest

Implemented automated smoke test
.DS_Store
npm-debug.log
Thumbs.db
node_modules/
out/
keybindings.*.json
test_data/
\ No newline at end of file
# Architecture
* `main.js` is used to prepare all smoke test dependencies (fetching key bindings and 'Express' repository, running `npm install` there).
* `mocha-runner.js` launches Mocha programmatically. It is spawned in Node environment from main.js to ensure that it is possible to listen on `stderr`s (primary `process.stderr` is not readable otherwise). This is accomplished because WebDriverIO command deprecation warnings need to be redirected to a separate log. Those warnings are coming from WebDriverIO because ChromeDriver has not migrated from JsonWire to W3C WebDriver protocol.
* `tests.ts` contains the main smoke test suite. It includes all tests separated into mocha `describe()` groups that represent each of the areas of [Smoke Test document](https://github.com/Microsoft/vscode/wiki/Smoke-Test).
* `./areas/` folder contains a `.ts` file per each area of the document. E.g. `'Search'` area goes under `'search.ts'`. Every area file contains a list of methods with the name that represents the action that can be performed in the corresponding test. This reduces the amount of test suite code and means that if the UI changes, the fix need only be applied in one place. The name of the method reflects the action the tester would do if he would perform the test manually. See [Selenium Page Objects Wiki](https://github.com/SeleniumHQ/selenium/wiki/PageObjects) and [Selenium Bot Style Tests Wiki](https://github.com/SeleniumHQ/selenium/wiki/Bot-Style-Tests) for a good explanation of the implementation. Every smoke test area contains methods that are used in a bot-style approach in `main.ts`.
* `./spectron/` wraps the Spectron, with WebDriverIO API wrapped in `client.ts` and instance of Spectron Application is wrapped in `application.ts`.
* `./scripts/` contains scripts to run the smoke test.
# Adding new area
To contribute a new smoke test area, add `${area}.ts` file under `./areas`. This has to follow the bot-style approach described in the links mentioned above. Methods should be calling WebDriverIO API through `SpectronClient` class. If there is no existing WebDriverIO method, add it to the class.
# Adding new test
To add new test area or test, `main.ts` should be updated. The same instruction-style principle needs to be followed with the called area method names that reflect manual tester's actions.
# Debugging
1. Add the following configuration to launch.json, specifying binaries in `args`:
```json
{
"type": "node",
"request": "launch",
"name": "Launch Smoke Test",
"program": "${workspaceRoot}/test/smoke/src/main.js",
"cwd": "${workspaceRoot}/test/smoke",
"port": 9999,
"args": [
"-l",
"path/to/Code.exe"
],
"outFiles": [
"${cwd}/out/**/*.js"
]
},
```
2. In main.js add `--debug-brk=9999` argument to the place where `src/mocha-runner.js` is spawned.
\ No newline at end of file
# VS Code Smoke Testing
This repository contains the smoke test automation code with Spectron for Visual Studio Code.
The following command is used to run the tests: `npm test -- --latest "path/to/binary"`.
If you want to include 'Data Migration' area tests use `npm test -- --latest path/to/binary --stable path/to/currentStable` respectively.
# Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
{
"name": "code-oss-dev-smoke-test",
"version": "0.1.0",
"main": "./src/main.js",
"scripts": {
"compile": "tsc",
"pretest": "tsc",
"test": "node src/main.js"
},
"devDependencies": {
"@types/mocha": "^2.2.41",
"@types/node": "^6.0.70",
"@types/webdriverio": "^4.6.1",
"@types/electron": "^1.4.37",
"@types/rimraf": "^0.0.28",
"mocha": "^3.2.0",
"spectron": "^3.6.4",
"typescript": "^2.2.2",
"rimraf": "^2.6.1",
"commander": "^2.9.0",
"simple-git": "^1.73.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 { Util } from '../helpers/utilities';
/**
* Contains methods that are commonly used across test areas.
*/
export class CommonActions {
private util: Util;
constructor(private spectron: SpectronApplication) {
this.util = new Util();
}
public async getWindowTitle(): Promise<any> {
return this.spectron.client.getTitle();
}
public enter(): Promise<any> {
return this.spectron.client.keys(['Enter', 'NULL']);
}
public async addSetting(setting: string, value: string): Promise<any> {
await this.spectron.command('workbench.action.openGlobalSettings');
await this.spectron.wait();
await this.spectron.client.leftClick('.editable-preferences-editor-container .view-lines', 1, 1, false);
await this.spectron.client.keys(['ArrowDown', 'NULL', 'ArrowDown', 'NULL'], false);
await this.spectron.wait();
await this.spectron.client.keys(`"${setting}": "${value}"`);
await this.spectron.wait();
return this.saveOpenedFile();
}
public async newUntitledFile(): Promise<any> {
await this.spectron.command('workbench.action.files.newUntitledFile');
return this.spectron.wait();
}
public closeTab(): Promise<any> {
return this.spectron.client.keys(['Control', 'w', 'NULL']);
}
public async getTab(tabName: string, active?: boolean): Promise<any> {
await this.closeCurrentNotification(); // close any notification messages that could overlap tabs
let tabSelector = active ? '.tab.active' : 'div';
let el = await this.spectron.client.element(`.tabs-container ${tabSelector}[aria-label="${tabName}, tab"]`);
if (el.status === 0) {
return el;
}
return undefined;
}
public async selectTab(tabName: string): Promise<any> {
await this.closeCurrentNotification(); // close any notification messages that could overlap tabs
return this.spectron.client.click(`.tabs-container div[aria-label="${tabName}, tab"]`);
}
public async openFirstMatchFile(fileName: string): Promise<any> {
await this.openQuickOpen();
await this.type(fileName);
await this.spectron.wait();
await this.enter();
return this.spectron.wait();
}
public saveOpenedFile(): Promise<any> {
return this.spectron.command('workbench.action.files.save');
}
public type(text: string): Promise<any> {
let spectron = this.spectron;
return new Promise(function (res) {
let textSplit = text.split(' ');
async function type(i: number) {
if (!textSplit[i] || textSplit[i].length <= 0) {
return res();
}
const toType = textSplit[i + 1] ? `${textSplit[i]} ` : textSplit[i];
await spectron.client.keys(toType, false);
await spectron.client.keys(['NULL']);
await type(i + 1);
}
return type(0);
});
}
public showCommands(): Promise<any> {
return this.spectron.command('workbench.action.showCommands');
}
public openQuickOpen(): Promise<any> {
return this.spectron.command('workbench.action.quickOpen');
}
public closeQuickOpen(): Promise<any> {
return this.spectron.command('workbench.action.closeQuickOpen');
}
public selectNextQuickOpenElement(): Promise<any> {
return this.spectron.client.keys(['ArrowDown', 'NULL']);
}
public async getQuickOpenElements(): Promise<number> {
const elements = await this.spectron.waitFor(this.spectron.client.elements, 'div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties .monaco-tree-row');
return elements.value.length;
}
public async openFile(fileName: string, explorer?: boolean): Promise<any> {
let selector = `div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.getExtensionSelector(fileName)}`;
if (explorer) {
selector += ' explorer-item';
}
selector += '"]';
await this.spectron.waitFor(this.spectron.client.doubleClick, selector);
return this.spectron.wait();
}
public getExtensionSelector(fileName: string): string {
const extension = fileName.split('.')[1];
if (extension === 'js') {
return 'js-ext-file-icon javascript-lang-file-icon';
} else if (extension === 'json') {
return 'json-ext-file-icon json-lang-file-icon';
} else if (extension === 'md') {
return 'md-ext-file-icon markdown-lang-file-icon';
}
throw new Error('No class defined for this file extension');
}
public async getEditorFirstLinePlainText(): Promise<any> {
try {
const span = await this.spectron.client.getText('.view-lines span span');
if (Array.isArray(span)) {
return span[0];
}
return span;
} catch (e) {
return undefined;
}
}
public removeFile(filePath: string): void {
this.util.removeFile(filePath);
}
public removeDirectory(directory: string): Promise<any> {
try {
return this.util.rimraf(directory);
} catch (e) {
throw new Error(`Failed to remove ${directory} with an error: ${e}`);
}
}
private closeCurrentNotification(): Promise<any> {
return this.spectron.command('workbench.action.closeMessages');
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 getEditorLineNumbers(): any {
return this.spectron.client.elements('.line-numbers');
}
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;
};
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 CSSProblem {
WARNING = 0,
ERROR = 1
};
export class CSS {
constructor(private spectron: SpectronApplication) {
// noop
}
public openQuickOutline(): any {
return this.spectron.command('workbench.action.gotoSymbol');
}
public toggleProblemsView(): any {
return this.spectron.command('workbench.actions.view.problems');
}
public async getEditorProblem(problemType: CSSProblem): Promise<any> {
let selector;
if (problemType === CSSProblem.WARNING) {
selector = 'greensquiggly';
} else if (problemType === CSSProblem.ERROR) {
selector = 'redsquiggly';
} else {
throw new Error('No such problem type defined.');
}
let el = await this.spectron.client.element(`.view-overlays .cdr.${selector}`);
if (el.status === 0) {
return el;
}
return undefined;
}
public async getProblemsViewsProblem(problemType: CSSProblem): Promise<any> {
let selector;
if (problemType === CSSProblem.WARNING) {
selector = 'warning';
} else if (problemType === CSSProblem.ERROR) {
selector = 'error';
} else {
throw new Error('No such problem type defined.');
}
let el = await this.spectron.client.element(`div[aria-label="Problems grouped by files"] .icon.${selector}`);
if (el.status === 0) {
return el;
}
return undefined;
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 DataLoss {
constructor(private spectron: SpectronApplication) {
}
public openExplorerViewlet(): Promise<any> {
return this.spectron.command('workbench.view.explorer');
}
public async verifyTabIsDirty(tabName: string, active?: boolean): Promise<any> {
let activeSelector = active ? '.active' : '';
let el = await this.spectron.client.element(`.tabs-container .tab.dirty${activeSelector}[aria-label="${tabName}, tab"]`);
if (el.status === 0) {
return el;
}
return undefined;
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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";
export class Extensions {
private readonly extensionsViewletSelector = 'div[id="workbench.view.extensions"]';
constructor(private spectron: SpectronApplication, private common: CommonActions) {
}
public async openExtensionsViewlet(): Promise<any> {
await this.spectron.command('workbench.view.extensions');
return this.spectron.wait();
}
public async searchForExtension(name: string): Promise<any> {
const searchBoxSelector = `${this.extensionsViewletSelector} .search-box`;
await this.spectron.client.clearElement(searchBoxSelector);
await this.spectron.client.click(searchBoxSelector, false);
await this.spectron.client.keys(name);
return this.spectron.client.keys(['NULL', 'Enter', 'NULL']);
}
public installFirstResult(): Promise<any> {
return this.spectron.client.click(`${this.extensionsViewletSelector} .monaco-list-rows>:nth-child(1) .extension .extension-action.install`);
}
public getFirstReloadText(): Promise<any> {
return this.spectron.waitFor(this.spectron.client.getText, `${this.extensionsViewletSelector} .monaco-list-rows>:nth-child(1) .extension .extension-action.reload`);
}
public async selectMinimalIconsTheme(): Promise<any> {
await this.common.showCommands();
await this.common.type('File Icon Theme');
await this.spectron.wait();
await this.common.enter();
return this.spectron.client.keys(['ArrowDown', 'NULL', 'Enter', 'NULL']);
}
public async verifyFolderIconAppearance(): Promise<any> {
return this.spectron.waitFor(this.spectron.client.getHTML, 'style[class="contributedIconTheme"]');
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 FirstExperience {
constructor(private spectron: SpectronApplication) {
// noop
}
public async getWelcomeTab(): Promise<any> {
let el = await this.spectron.client.element('.vs_code_welcome_page-name-file-icon');
if (el.status === 0) {
return el;
}
return undefined;
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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";
export class Git {
private readonly bodyVarSelector = '.view-lines>:nth-child(6) .mtk11';
constructor(private spectron: SpectronApplication, private commonActions: CommonActions) {
// noop
}
public openGitViewlet(): Promise<any> {
return this.spectron.command('workbench.view.git');
}
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 = await this.spectron.client.element(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"]`);
if (el.status === 0) {
return el;
}
return undefined;
}
public getOriginalAppJsBodyVarName(): Promise<any> {
return this.spectron.waitFor(this.spectron.client.getText, `.editor.original ${this.bodyVarSelector}`);
}
public getModifiedAppJsBodyVarName(): Promise<any> {
return this.spectron.waitFor(this.spectron.client.getText, `.editor.modified ${this.bodyVarSelector}`);
}
public async stageFile(fileName: string): Promise<any> {
await this.spectron.client.moveToObject(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"`);
await this.spectron.wait();
await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-4');
return this.spectron.wait();
}
public async unstageFile(fileName: string): Promise<any> {
await this.spectron.client.moveToObject(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.commonActions.getExtensionSelector(fileName)}"`);
await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-6');
return this.spectron.wait();
}
public getStagedCount(): Promise<any> {
return this.spectron.waitFor(this.spectron.client.getText, '.scm-status.show-file-icons .monaco-list-rows>:nth-child(1) .monaco-count-badge');
}
public focusOnCommitBox(): Promise<any> {
return this.spectron.client.click('div[id="workbench.view.scm"] textarea');
}
public async pressCommit(): Promise<any> {
await this.spectron.client.click('.action-label.icon.contrib-cmd-icon-10');
return this.spectron.wait();
}
public getOutgoingChanges(): Promise<string> {
return this.spectron.client.getText('a[title="Synchronize changes"]');
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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";
export class IntegratedTerminal {
constructor(private spectron: SpectronApplication) {
// noop
}
public async openTerminal(commonActions: CommonActions): Promise<any> {
// Backquote dispatching does not work in OS X
if (process.platform === 'darwin') {
await commonActions.showCommands();
await commonActions.type('Toggle Integrated Terminal');
return commonActions.enter();
}
return this.spectron.command('workbench.action.terminal.toggleTerminal');
}
public async commandOutputHas(result: string): Promise<boolean> {
const selector = 'div[id="workbench.panel.terminal"] .xterm-rows';
const rows = await this.spectron.client.elements(`${selector} div`);
for (let i = 0; i < rows.value.length; i++) {
const rowText = await this.spectron.client.getText(`${selector}>:nth-child(${i+1})`);
if (rowText.trim() === result) {
return true;
}
}
return false;
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 JavaScriptDebug {
private readonly sidebarSelector = '.margin-view-overlays';
constructor(private spectron: SpectronApplication) {
// noop
}
public openDebugViewlet(): Promise<any> {
return this.spectron.command('workbench.view.debug');
}
public async pressConfigureLaunchJson(): Promise<any> {
await this.spectron.waitFor(this.spectron.client.click, 'ul[aria-label="Debug actions"] .action-label.icon.debug-action.configure');
await this.spectron.wait();
await this.spectron.client.keys(['ArrowDown', 'NULL', 'Enter']);
return this.spectron.wait();
}
public getProgramConfigValue(): Promise<any> {
return this.spectron.client.getText('.view-lines>:nth-child(11) .mtk7');
}
public setBreakpointOnLine(lineNumber: number): Promise<any> {
return this.spectron.client.leftClick(`${this.sidebarSelector}>:nth-child(${lineNumber})`, 5, 5);
}
public async verifyBreakpointOnLine(lineNumber: number): Promise<any> {
let el = await this.spectron.client.element(`${this.sidebarSelector}>:nth-child(${lineNumber}) .cgmr.debug-breakpoint-glyph`);
if (el.status === 0) {
return el;
}
return undefined;
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 JavaScript {
private readonly appVarSelector = '.view-lines>:nth-child(7) .mtk11';
private readonly firstCommentSelector = '.margin-view-overlays>:nth-child(3)';
private readonly expressVarSelector = '.view-lines>:nth-child(11) .mtk10';
constructor(private spectron: SpectronApplication) {
// noop
}
public openQuickOutline(): Promise<any> {
return this.spectron.command('workbench.action.gotoSymbol');
}
public async findAppReferences(): Promise<any> {
await this.spectron.client.click(this.appVarSelector, false);
return this.spectron.command('editor.action.referenceSearch.trigger');
}
public async getTitleReferencesCount(): Promise<any> {
const meta = await this.spectron.client.getText('.reference-zone-widget.results-loaded .peekview-title .meta');
return meta.match(/\d+/)[0];
}
public async getTreeReferencesCount(): Promise<any> {
const treeElems = await this.spectron.client.elements('.reference-zone-widget.results-loaded .ref-tree.inline .show-twisties .monaco-tree-row');
return treeElems.value.length;
}
public async renameApp(newValue: string): Promise<any> {
await this.spectron.client.click(this.appVarSelector);
await this.spectron.command('editor.action.rename');
await this.spectron.wait();
return this.spectron.client.keys(newValue, false);
}
public async getNewAppName(): Promise<any> {
return this.spectron.client.getText(this.appVarSelector);
}
public async toggleFirstCommentFold(): Promise<any> {
return this.spectron.client.click(`${this.firstCommentSelector} .cldr.folding`);
}
public async getFirstCommentFoldedIcon(): Promise<any> {
return this.spectron.client.getHTML(`${this.firstCommentSelector} .cldr.folding.collapsed`);
}
public async getNextLineNumberAfterFold(): Promise<any> {
return this.spectron.client.getText(`.margin-view-overlays>:nth-child(4) .line-numbers`)
}
public async goToExpressDefinition(): Promise<any> {
await this.spectron.client.click(this.expressVarSelector);
return this.spectron.command('editor.action.goToDeclaration');
}
public async peekExpressDefinition(): Promise<any> {
await this.spectron.client.click(this.expressVarSelector);
return this.spectron.command('editor.action.previewDeclaration');
}
public async getPeekExpressResultName(): Promise<any> {
return this.spectron.client.getText('.reference-zone-widget.results-loaded .filename');
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 ViewletType {
SEARCH = 0,
SCM = 1,
DEBUG = 2,
EXTENSIONS = 3
}
export class Localization {
constructor(private spectron: SpectronApplication) {
// noop
}
public async getOpenEditorsText(): Promise<string> {
const explorerTitles = await this.spectron.client.getText('div[id="workbench.view.explorer"] .title span');
return explorerTitles[0];
}
public openViewlet(type: ViewletType): Promise<any> {
let command;
switch (type) {
case ViewletType.SEARCH:
command = 'workbench.view.search';
break;
case ViewletType.SCM:
command = 'workbench.view.scm';
break;
case ViewletType.DEBUG:
command = 'workbench.view.debug';
break;
case ViewletType.EXTENSIONS:
command = 'workbench.view.extensions';
break;
}
return this.spectron.command(command, false);
}
public getOpenedViewletTitle(): Promise<string> {
return this.spectron.client.getText('div[id="workbench.parts.sidebar"] .title-label span');
}
public getExtensionsSearchPlaceholder(): Promise<string> {
return this.spectron.client.getAttribute('div[id="workbench.view.extensions"] .search-box', 'placeholder');
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 {
return this.spectron.client.setValue('.viewlet .input[title="Replace"]', text);
}
public replaceFirstMatch(): any {
return this.spectron.client.click('.monaco-tree-rows.show-twisties .action-label.icon.action-replace-all');
}
public getResultText(): any {
return this.spectron.waitFor(this.spectron.client.getText, '.search-viewlet .message>p');
}
public toggleSearchDetails(): any {
return this.spectron.client.click('.query-details .more');
}
public toggleReplace(): any {
return this.spectron.client.click('.monaco-button.toggle-replace-button.collapse');
}
public hoverOverResultCount(): any {
return this.spectron.waitFor(this.spectron.client.moveToObject, '.monaco-count-badge');
}
public dismissResult(): any {
return this.spectron.client.click('.action-label.icon.action-remove');
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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.');
}
return this.spectron.client.click(selector);
}
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`);
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 Tasks {
private readonly outputViewSelector = 'div[id="workbench.panel.output"] .view-lines';
private readonly workbenchPanelSelector = 'div[id="workbench.parts.panel"]';
private readonly problemsViewSelector = 'div[id="workbench.panel.markers"] .monaco-tree-row.expanded';
constructor(private spectron: SpectronApplication) {
// noop
}
public async build(): Promise<any> {
await this.spectron.command('workbench.action.tasks.build');
return this.spectron.wait(); // wait for build to finish
}
public openProblemsView(): Promise<any> {
return this.spectron.command('workbench.actions.view.problems');
}
public async firstOutputLineEndsWith(fileName: string): Promise<boolean> {
await this.spectron.command('workbench.action.toggleFullScreen'); // toggle full screen to prevent output view to be rendered as wrapped
const firstLine = await this.spectron.waitFor(this.spectron.client.getText, `${this.outputViewSelector}>:nth-child(2)`);
return firstLine.endsWith(fileName);
}
public async getOutputResult(): Promise<any> {
await this.spectron.command('workbench.action.toggleFullScreen'); // toggle full screen to prevent output view to be rendered as wrapped
return this.spectron.waitFor(this.spectron.client.getText, `${this.outputViewSelector}>:nth-child(5) span.mtk1`);
}
public selectOutputViewType(type: string): Promise<any> {
return this.spectron.client.selectByValue(`${this.workbenchPanelSelector} .select-box`, type);
}
public getOutputViewType(): Promise<any> {
return this.spectron.client.getValue(`${this.workbenchPanelSelector} .select-box`);
}
public getProblemsViewFirstElementName(): Promise<any> {
return this.spectron.waitFor(this.spectron.client.getText, `${this.problemsViewSelector} .label-name`);
}
public getProblemsViewFirstElementCount(): Promise<any> {
return this.spectron.waitFor(this.spectron.client.getText, `${this.problemsViewSelector} .monaco-count-badge`);
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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";
var fs = require('fs');
const __testTime = new Date().toISOString();
export class Screenshot {
private index: number = 0;
private testPath: string;
constructor(private spectron: SpectronApplication, testName: string) {
const testTime = this.sanitizeFolderName(__testTime);
testName = this.sanitizeFolderName(testName);
this.testPath = `test_data/screenshots/${testTime}/${testName}`;
this.createFolder(this.testPath);
}
public capture(): Promise<any> {
return new Promise(async (res, rej) => {
const image: Electron.NativeImage = await this.spectron.app.browserWindow.capturePage();
fs.writeFile(`${this.testPath}/${this.index}.png`, image, (err) => {
if (err) {
rej(err);
}
});
this.index++;
res();
});
}
private createFolder(name: string) {
name.split('/').forEach((folderName, i, fullPath) => {
const folder = fullPath.slice(0, i + 1).join('/');
if (!fs.existsSync(folder)) {
fs.mkdirSync(folder);
}
});
}
private sanitizeFolderName(name: string): string {
return name.replace(/[&*:\/]/g, '');
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var fs = require('fs');
var rimraf = require('rimraf');
/**
* Contains methods that are commonly used across test areas.
*/
export class Util {
constructor() {
// noop
}
public removeFile(filePath: string): void {
try {
fs.unlinkSync(`${filePath}`);
} catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
}
}
public rimraf(directory: string): Promise<any> {
return new Promise((res, rej) => {
rimraf(directory, (err) => {
if (err) {
rej(err);
}
res();
});
});
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var fs = require('fs');
var https = require('https');
var program = require('commander');
var git = require('simple-git')();
var child_process = require('child_process');
var path = require('path');
var tempFolder = 'test_data';
var testRepoUrl = 'https://github.com/Microsoft/vscode-smoketest-express';
var testRepoLocalDir = path.join(process.cwd(), `${tempFolder}/vscode-smoketest-express`);
var keybindingsUrl = 'https://raw.githubusercontent.com/Microsoft/vscode-docs/master/scripts/keybindings';
program
.option('-l, --latest <file path>', 'path to the latest VS Code to test')
.option('-s, --stable [file path]', 'path to the stable VS Code to be used in data migration tests');
program.on('--help', () => {
console.log(' Examples:');
console.log('');
console.log(' $ npm test -- --latest path/to/binary');
console.log(' $ npm test -- -l path/to/binary');
console.log('');
console.log(' $ npm test -- --latest path/to/latest/binary --stable path/to/stable/binary');
console.log(' $ npm test -- -l path/to/latest/binary -s path/to/stable/binary');
console.log('');
});
program.parse(process.argv);
if (!program.latest) {
console.error('You must specify the binary to run the smoke test against');
process.exit(1);
}
if (!binaryExists(program.latest) || (program.stable && !binaryExists(program.stable))) {
console.error('The file path to electron binary does not exist or permissions do not allow to execute it. Please check the path provided.');
process.exit(1);
}
// Setting up environment variables
process.env.VSCODE_LATEST_PATH = program.latest;
if (program.stable) process.env.VSCODE_STABLE_PATH = program.stable;
process.env.SMOKETEST_REPO = testRepoLocalDir;
if (program.stable && program.stable.toLowerCase().startsWith('insiders')) process.env.VSCODE_EDITION = 'insiders';
// Setting up 'vscode-smoketest-express' project
var os = process.platform;
if (os === 'darwin') os = 'osx';
else if (os === 'win32') os = 'win';
var promises = [];
try {
promises.push(getKeybindings(`${keybindingsUrl}/doc.keybindings.${os}.json`, `${tempFolder}/keybindings.json`));
promises.push(cleanOrClone(testRepoUrl, testRepoLocalDir));
Promise.all(promises).then(() => { execute('npm install', testRepoLocalDir).then(() => runTests()); });
} catch (e) {
throw new Error('Error caught running the smoke test: ' + e);
}
function runTests() {
console.log('Running tests...')
const spawn = require('child_process').spawn;
var proc = spawn(process.execPath, [
'src/mocha-runner.js'
]);
proc.stdout.on('data', data => {
console.log(data.toString());
});
proc.stderr.on('data', data => {
var date = new Date().toLocaleString();
fs.appendFile(`${tempFolder}/errors.log`, `${date}: ${data.toString()}`, (err) => {
if (err) throw new Error(`Could not write stderr to errors.log with the following error: ${err}`);
});
});
proc.on('exit', (code) => {
process.exit(code);
});
}
function cleanOrClone(repo, dir) {
console.log('Cleaning or cloning test project repository...');
return new Promise((res, rej) => {
if (!folderExists(dir)) {
git.clone(repo, dir, () => {
console.log('Test repository successfully cloned.');
res();
});
} else {
git.cwd(dir);
git.fetch((err) => {
if (err) rej(err);
resetAndClean();
});
}
var resetAndClean = () => {
git.reset(['FETCH_HEAD', '--hard'], (err) => {
if (err) rej(err);
git.clean('f', ['-d'], (err) => {
if (err) rej(err);
console.log('Test project was successfully reset to initial state.');
res();
});
});
}
});
}
function execute(cmd, dir) {
return new Promise((res, rej) => {
console.log(`Running ${cmd}...`);
child_process.exec(cmd, { cwd: dir, stdio: [0, 1, 2] }, (error, stdout, stderr) => {
if (error) rej(error);
if (stderr) console.error(stderr);
console.log(stdout);
res();
});
});
}
function getKeybindings(url, location) {
console.log(`Fetching keybindings from ${url}...`);
return new Promise((resolve, reject) => {
https.get(url, (res) => {
if (res.statusCode != 200) {
reject(`Failed to obtain key bindings with response code: ${res.statusCode}`);
}
var buffer = [];
res.on('data', (chunk) => buffer.push(chunk));
res.on('end', () => {
fs.writeFile(location, Buffer.concat(buffer), 'utf8', () => {
console.log('Keybindings were successfully fetched.');
resolve();
});
});
}).on('error', (e) => {
reject(`Failed to obtain key bindings with an error: ${e}`);
});
});
}
function folderExists(folder) {
try {
fs.accessSync(folder, 'rw');
return true;
} catch (e) {
return false;
}
}
function binaryExists(filePath) {
try {
fs.accessSync(filePath, 'x');
return true;
} catch (e) {
return false;
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var Mocha = require('mocha');
var path = require('path');
var mocha = new Mocha({
timeout: 360000,
retries: 2,
slow: 50000,
useColors: true
});
mocha.addFile(path.join(process.cwd(), 'out/test.js'));
mocha.run((failures) => {
process.on('exit', () => {
process.exit(failures);
});
});
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Application } from 'spectron';
import { SpectronClient } from './client';
import { Screenshot } from "../helpers/screenshot";
var fs = require('fs');
var path = require('path');
export const LATEST_PATH = process.env.VSCODE_LATEST_PATH;
export const STABLE_PATH = process.env.VSCODE_STABLE_PATH;
export const WORKSPACE_PATH = process.env.SMOKETEST_REPO;
export const USER_DIR = 'test_data/temp_user_dir';
export const EXTENSIONS_DIR = 'test_data/temp_extensions_dir';
/**
* Wraps Spectron's Application instance with its used methods.
*/
export class SpectronApplication {
public client: SpectronClient;
private spectron: Application;
private readonly pollTrials = 5;
private readonly pollTimeout = 3; // in secs
private keybindings: any[];
private screenshot: Screenshot;
constructor(electronPath: string, testName: string, private testRetry: number, args?: string[], chromeDriverArgs?: string[]) {
if (!args) {
args = [];
}
this.spectron = new Application({
path: electronPath,
args: args.concat(['--skip-getting-started']), // prevent 'Getting Started' web page from opening on clean user-data-dir
chromeDriverArgs: chromeDriverArgs
});
this.screenshot = new Screenshot(this, testName);
this.client = new SpectronClient(this.spectron, this.screenshot);
this.testRetry += 1; // avoid multiplication by 0 for wait times
this.retrieveKeybindings();
}
public get app(): Application {
return this.spectron;
}
public async start(): Promise<any> {
try {
await this.spectron.start();
await this.focusOnWindow(1); // focuses on main renderer window
return this.checkWindowReady();
} catch (err) {
throw err;
}
}
public async stop(): Promise<any> {
if (this.spectron && this.spectron.isRunning()) {
return await this.spectron.stop();
}
}
public waitFor(func: (...args: any[]) => any, args: any): Promise<any> {
return this.callClientAPI(func, args, 0);
}
public wait(): Promise<any> {
return new Promise(resolve => setTimeout(resolve, this.testRetry * this.pollTimeout * 1000));
}
public focusOnWindow(index: number): Promise<any> {
return this.client.windowByIndex(index);
}
private checkWindowReady(): Promise<any> {
return this.waitFor(this.spectron.client.getHTML, '[id="workbench.main.container"]');
}
private retrieveKeybindings() {
fs.readFile(path.join(process.cwd(), `test_data/keybindings.json`), 'utf8', (err, data) => {
if (err) {
throw err;
}
try {
this.keybindings = JSON.parse(data);
} catch (e) {
throw new Error(`Error parsing keybindings JSON: ${e}`);
}
});
}
private callClientAPI(func: (...args: any[]) => Promise<any>, args: any, trial: number): Promise<any> {
if (trial > this.pollTrials) {
return Promise.reject(`Could not retrieve the element in ${this.testRetry * this.pollTrials * this.pollTimeout} seconds.`);
}
return new Promise(async (res, rej) => {
let resolved = false, capture = false;
const tryCall = async (resolve: any, reject: any): Promise<any> => {
await this.wait();
try {
const result = await this.callClientAPI(func, args, ++trial);
res(result);
} catch (error) {
rej(error);
}
}
try {
const result = await func.call(this.client, args, capture);
if (!resolved && result === '') {
resolved = true;
await tryCall(res, rej);
} else if (!resolved) {
resolved = true;
await this.screenshot.capture();
res(result);
}
} catch (e) {
if (!resolved) {
resolved = true;
await tryCall(res, rej);
}
}
});
}
/**
* Retrieves the command from keybindings file and executes it with WebdriverIO client API
* @param command command (e.g. 'workbench.action.files.newUntitledFile')
*/
public command(command: string, capture?: boolean): Promise<any> {
const binding = this.keybindings.find(x => x['command'] === command);
const keys: string = binding.key;
let keysToPress: string[] = [];
const chords = keys.split(' ');
chords.forEach((chord) => {
const keys = chord.split('+');
keys.forEach((key) => keysToPress.push(this.transliterate(key)));
keysToPress.push('NULL');
});
return this.client.keys(keysToPress, capture);
}
/**
* Transliterates key names from keybindings file to WebdriverIO keyboard actions defined in:
* https://w3c.github.io/webdriver/webdriver-spec.html#keyboard-actions
*/
private transliterate(key: string): string {
switch (key) {
case 'ctrl':
return 'Control';
case 'cmd':
return 'Meta';
default:
return key.length === 1 ? key : key.charAt(0).toUpperCase() + key.slice(1);
};
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Application } from 'spectron';
import { Screenshot } from '../helpers/screenshot';
/**
* Abstracts the Spectron's WebdriverIO managed client property on the created Application instances.
*/
export class SpectronClient {
constructor(private spectron: Application, private shot: Screenshot) {
// noop
}
public windowByIndex(index: number): Promise<any> {
return this.spectron.client.windowByIndex(index);
}
public async keys(keys: string[] | string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.keys(keys);
}
public async getText(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.getText(selector);
}
public async getHTML(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.getHTML(selector);
}
public async click(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.click(selector);
}
public async doubleClick(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.doubleClick(selector);
}
public async leftClick(selector: string, xoffset: number, yoffset: number, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.leftClick(selector, xoffset, yoffset);
}
public async rightClick(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.rightClick(selector);
}
public async moveToObject(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.moveToObject(selector);
}
public async setValue(selector: string, text: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.setValue(selector, text);
}
public async elements(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.elements(selector);
}
public async element(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.element(selector);
}
public async dragAndDrop(sourceElem: string, destinationElem: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.dragAndDrop(sourceElem, destinationElem);
}
public async selectByValue(selector: string, value: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.selectByValue(selector, value);
}
public async getValue(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.getValue(selector);
}
public async getAttribute(selector: string, attribute: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return Promise.resolve(this.spectron.client.getAttribute(selector, attribute));
}
public clearElement(selector: string): any {
return this.spectron.client.clearElement(selector);
}
public buttonDown(): any {
return this.spectron.client.buttonDown();
}
public buttonUp(): any {
return this.spectron.client.buttonUp();
}
public async isVisible(selector: string, capture: boolean = true): Promise<any> {
await this.execute(capture);
return this.spectron.client.isVisible(selector);
}
public getTitle(): string {
return this.spectron.client.getTitle();
}
private async execute(capture: boolean): Promise<any> {
if (capture) {
try {
await this.shot.capture();
} catch (e) {
throw new Error(`Screenshot could not be captured: ${e}`);
}
}
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { testDataLoss } from "./tests/data-loss";
import { testDataMigration } from "./tests/data-migration";
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";
describe('Smoke Test Suite', () => {
testDataMigration();
testDataLoss();
testExplorer();
testConfigViews();
testSearch();
testCSS();
testJavaScript();
testJavaScriptDebug();
testGit();
testIntegratedTerminal();
testStatusbar();
testTasks();
testExtensions();
testLocalization();
});
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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() {
context('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 elements = await configView.getEditorLineNumbers();
assert.equal(elements.value.length, 1);
await common.addSetting('editor.lineNumbers', 'off');
await app.wait();
elements = await configView.getEditorLineNumbers();
assert.equal(elements.value.length, 0);
});
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);;
await app.wait();
await configView.toggleActivityBarPosition();
html = await configView.getActivityBar(ActivityBarPosition.RIGHT);
assert.ok(html);
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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() {
context('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);
});
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);
await css.toggleProblemsView();
warning = await css.getProblemsViewsProblem(CSSProblem.WARNING);
assert.ok(warning);
});
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);
await css.toggleProblemsView();
error = await css.getProblemsViewsProblem(CSSProblem.ERROR);
assert.ok(error);
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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() {
context('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);
return 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));
assert.ok(await common.getTab(fileName, true));
// check if they marked as dirty (icon) and active tab is the last opened
assert.ok(await dl.verifyTabIsDirty(untitled));
assert.ok(await dl.verifyTabIsDirty(fileName, true));
});
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, 'Hello, Code'); // ignore '!' as it is a separate <span/>, first part is enough
await common.selectTab('Untitled-1');
fileDirt = await common.getEditorFirstLinePlainText();
assert.equal(fileDirt, textToType);
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { CommonActions } from '../areas/common';
let app: SpectronApplication;
let common: CommonActions;
export function testDataMigration() {
if (!STABLE_PATH) {
return;
}
context('Data Migration', () => {
afterEach(async function () {
await app.stop();
await common.removeDirectory(USER_DIR);
return await common.removeDirectory(EXTENSIONS_DIR);
});
function setupSpectron(context: Mocha.ITestCallbackContext, appPath: string, workspace?: string[]): void {
app = new SpectronApplication(appPath, context.test.fullTitle(), context.test.currentRetry(), workspace, [`--user-data-dir=${USER_DIR}`, `--extensions-dir=${EXTENSIONS_DIR}`]);
common = new CommonActions(app);
}
it('checks if the Untitled file is restored migrating from stable to latest', async function () {
const textToType = 'Very dirty file';
// Setting up stable version
setupSpectron(this, STABLE_PATH);
await app.start();
await common.newUntitledFile();
await common.type(textToType);
await app.stop();
await app.wait(); // wait until all resources are released (e.g. locked local storage)
// Checking latest version for the restored state
setupSpectron(this, LATEST_PATH);
await app.start();
assert.ok(await common.getTab('Untitled-1'));
await common.selectTab('Untitled-1');
const editorText = await common.getEditorFirstLinePlainText();
assert.equal(editorText, 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 common.removeFile(`${fileName}`);
await app.start();
await common.type(firstTextPart);
await common.saveOpenedFile();
await app.wait();
await common.type(secondTextPart);
await app.stop();
await app.wait(); // wait until all resources are released (e.g. locked local storage)
// Checking latest version for the restored state
setupSpectron(this, LATEST_PATH);
await app.start();
assert.ok(await common.getTab(fileName.split('/')[1]));
await common.selectTab(fileName.split('/')[1]);
const editorText = await common.getEditorFirstLinePlainText();
assert.equal(editorText, firstTextPart.concat(secondTextPart));
// Cleanup
await common.removeFile(`${fileName}`);
});
it('cheks if opened tabs are restored migrating from stable to latest', async function () {
const fileName1 = 'app.js', fileName2 = 'jsconfig.json', fileName3 = 'readme.md';
setupSpectron(this, STABLE_PATH, [WORKSPACE_PATH]);
await app.start();
await common.openFile(fileName1, true);
await common.openFile(fileName2, true);
await common.openFile(fileName3, true);
await app.stop();
setupSpectron(this, LATEST_PATH, [WORKSPACE_PATH]);
await app.start();
assert.ok(await common.getTab(fileName1));
assert.ok(await common.getTab(fileName2));
assert.ok(await common.getTab(fileName3));
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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() {
context('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);
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 async function testExtensions() {
const network = await networkAttached();
if (!network) {
return;
}
context('Extensions', () => {
let extensions: Extensions;
beforeEach(async function () {
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-icons' extension and verifies reload is prompted`, async function () {
await extensions.openExtensionsViewlet();
await extensions.searchForExtension('vscode-icons');
await app.wait();
await extensions.installFirstResult();
await app.wait();
assert.ok(await extensions.getFirstReloadText());
});
it(`installs an extension and checks if it works on restart`, async function () {
await extensions.openExtensionsViewlet();
await extensions.searchForExtension('vscode-icons');
await app.wait();
await extensions.installFirstResult();
await app.wait();
await extensions.getFirstReloadText();
await app.stop();
await app.wait(); // wait until all resources are released (e.g. locked local storage)
await app.start();
await extensions.selectMinimalIconsTheme();
const x = await extensions.verifyFolderIconAppearance();
assert.ok(x);
});
});
}
function networkAttached(): Promise<boolean> {
return new Promise((res, rej) => {
dns.resolve('marketplace.visualstudio.com', (err) => {
err ? res(false) : res(true);
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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() {
context('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'));
assert.ok(await git.verifyScmChange('launch.json'));
});
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');
const modified = await git.getModifiedAppJsBodyVarName();
assert.equal(modified, 'ydobParser');
});
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↑');
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { IntegratedTerminal } from "../areas/integrated-terminal";
let app: SpectronApplication;
let common: CommonActions;
export function testIntegratedTerminal() {
context('Integrated Terminal', () => {
let terminal: IntegratedTerminal;
beforeEach(async function () {
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
common = new CommonActions(app);
terminal = new IntegratedTerminal(app);
return await app.start();
});
afterEach(async function () {
return await app.stop();
});
it(`opens terminal, runs 'echo' and verifies the output`, async function () {
const command = 'echo test';
await terminal.openTerminal(common);
await app.wait();
await common.type(command);
await common.enter();
await app.wait();
assert.ok(await terminal.commandOutputHas('test'));
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { JavaScriptDebug } from "../areas/javascript-debug";
let app: SpectronApplication;
let common: CommonActions;
export function testJavaScriptDebug() {
context('Debugging JavaScript', () => {
let jsDebug: JavaScriptDebug;
beforeEach(async function () {
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
common = new CommonActions(app);
jsDebug = new JavaScriptDebug(app);
return await app.start();
});
afterEach(async function () {
return await app.stop();
});
it('autodetects program attribute for launch.json', async function () {
await jsDebug.openDebugViewlet();
await jsDebug.pressConfigureLaunchJson();
const value = await jsDebug.getProgramConfigValue();
process.platform === 'win32' ? assert.equal(value, '"${workspaceRoot}\\\\bin\\\\www"') : assert.equal(value, '"${workspaceRoot}/bin/www"');
});
it(`can set a breakpoint and verify if it's set`, async function () {
await common.openFirstMatchFile('index.js');
await jsDebug.setBreakpointOnLine(6);
const breakpoint = await jsDebug.verifyBreakpointOnLine(6);
assert.ok(breakpoint);
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { JavaScript } from "../areas/javascript";
let app: SpectronApplication;
let common: CommonActions;
export function testJavaScript() {
context('JavaScript', () => {
let js: JavaScript;
beforeEach(async function () {
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
common = new CommonActions(app);
js = new JavaScript(app);
return await app.start();
});
afterEach(async function () {
return await app.stop();
});
it('shows correct quick outline', async function () {
await common.openFirstMatchFile('bin/www');
await js.openQuickOutline();
await app.wait();
const symbols = await common.getQuickOpenElements();
assert.equal(symbols, 12);
});
it(`finds 'All References' to 'app'`, async function () {
await common.openFirstMatchFile('bin/www');
await js.findAppReferences();
await app.wait();
const titleCount = await js.getTitleReferencesCount();
assert.equal(titleCount, 3);
const treeCount = await js.getTreeReferencesCount();
assert.equal(treeCount, 3);
});
it(`renames local 'app' variable`, async function () {
await common.openFirstMatchFile('bin/www');
const newVarName = 'newApp';
await js.renameApp(newVarName);
await common.enter();
const newName = await js.getNewAppName();
assert.equal(newName, newVarName);
});
it('folds/unfolds the code correctly', async function () {
await common.openFirstMatchFile('bin/www');
// Fold
await js.toggleFirstCommentFold();
const foldedIcon = await js.getFirstCommentFoldedIcon();
assert.ok(foldedIcon);
let nextLineNumber = await js.getNextLineNumberAfterFold();
assert.equal(nextLineNumber, 7);
// Unfold
await js.toggleFirstCommentFold();
nextLineNumber = await js.getNextLineNumberAfterFold();
assert.equal(nextLineNumber, 4);
});
it(`verifies that 'Go To Definition' works`, async function () {
await common.openFirstMatchFile('app.js');
await js.goToExpressDefinition();
await app.wait();
assert.ok(await common.getTab('index.d.ts'));
});
it(`verifies that 'Peek Definition' works`, async function () {
await common.openFirstMatchFile('app.js');
await js.peekExpressDefinition();
await app.wait();
const definitionFilename = await js.getPeekExpressResultName();
assert.equal(definitionFilename, 'index.d.ts');
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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, USER_DIR } from "../spectron/application";
import { CommonActions } from '../areas/common';
import { Localization, ViewletType } from "../areas/localization";
let app: SpectronApplication;
let common: CommonActions;
export function testLocalization() {
context('Localization', () => {
afterEach(async function () {
return await app.stop();
});
it(`starts with 'DE' locale and verifies title and viewlets text is in German`, async function () {
app = new SpectronApplication(LATEST_PATH, this.test.fullTitle(), this.test.currentRetry(), [WORKSPACE_PATH, '--locale=DE'], [`--user-data-dir=${USER_DIR}`]);
common = new CommonActions(app);
const locale = new Localization(app);
common.removeDirectory(USER_DIR);
await app.start();
let text = await locale.getOpenEditorsText();
assert.equal(text.toLowerCase(), 'geöffnete editoren');
await locale.openViewlet(ViewletType.SEARCH);
text = await locale.getOpenedViewletTitle();
assert.equal(text.toLowerCase(), 'suchen');
await locale.openViewlet(ViewletType.SCM);
text = await locale.getOpenedViewletTitle();
assert.equal(text.toLowerCase(), 'quellcodeverwaltung: git');
await locale.openViewlet(ViewletType.DEBUG);
text = await locale.getOpenedViewletTitle();
assert.equal(text.toLowerCase(), 'debuggen');
await locale.openViewlet(ViewletType.EXTENSIONS);
text = await locale.getExtensionsSearchPlaceholder();
assert.equal(text.toLowerCase(), 'nach erweiterungen im marketplace suchen');
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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() {
context('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');
});
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');
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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() {
context('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));
assert.ok(await statusBar.isVisible(StatusBarElement.FEEDBACK_ICON));
assert.ok(await statusBar.isVisible(StatusBarElement.SYNC_STATUS));
assert.ok(await statusBar.isVisible(StatusBarElement.PROBLEMS_STATUS));
await common.openFirstMatchFile('app.js');
assert.ok(await statusBar.isVisible(StatusBarElement.ENCODING_STATUS));
assert.ok(await statusBar.isVisible(StatusBarElement.EOL_STATUS));
assert.ok(await statusBar.isVisible(StatusBarElement.INDENTATION_STATUS));
assert.ok(await statusBar.isVisible(StatusBarElement.LANGUAGE_STATUS));
assert.ok(await statusBar.isVisible(StatusBarElement.SELECTION_STATUS));
});
it(`verifies that 'quick open' opens when clicking on 'Branch', 'Indentation Status, 'Encoding', 'EOL' and 'Language' status elements`, async function () {
await app.wait();
await statusBar.clickOn(StatusBarElement.BRANCH_STATUS);
assert.ok(await statusBar.isQuickOpenWidgetVisible());
await common.closeQuickOpen();
await common.openFirstMatchFile('app.js');
await statusBar.clickOn(StatusBarElement.INDENTATION_STATUS);
assert.ok(await statusBar.isQuickOpenWidgetVisible());
await common.closeQuickOpen();
await statusBar.clickOn(StatusBarElement.ENCODING_STATUS);
assert.ok(await statusBar.isQuickOpenWidgetVisible());
await common.closeQuickOpen();
await statusBar.clickOn(StatusBarElement.EOL_STATUS);
assert.ok(await statusBar.isQuickOpenWidgetVisible());
await common.closeQuickOpen();
await statusBar.clickOn(StatusBarElement.LANGUAGE_STATUS);
assert.ok(await statusBar.isQuickOpenWidgetVisible());
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));
});
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');
});
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { Tasks } from "../areas/tasks";
let app: SpectronApplication;
export function testTasks() {
context('Tasks', () => {
let tasks: Tasks;
beforeEach(async function () {
app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]);
tasks = new Tasks(app);
return await app.start();
});
afterEach(async function () {
return await app.stop();
});
it('verifies that eslint task results in 1 problem', async function () {
await tasks.build();
const res = await tasks.getOutputResult();
assert.equal(res, '✖ 1 problem (0 errors, 1 warning)');
});
it(`is able to select 'Git' output`, async function () {
await tasks.build();
await app.wait();
await tasks.selectOutputViewType('Git');
const viewType = await tasks.getOutputViewType();
assert.equal(viewType, 'Git');
});
it('ensures that build task produces errors in index.js', async function () {
await tasks.build();
assert.ok(await tasks.firstOutputLineEndsWith('index.js'));
});
it(`verifies build errors are reflected in 'Problems View'`, async function () {
await tasks.build();
await tasks.openProblemsView();
const problemName = await tasks.getProblemsViewFirstElementName();
assert.equal(problemName, 'index.js');
const problemsCount = await tasks.getProblemsViewFirstElementCount();
assert.equal(problemsCount, '1');
});
});
}
\ No newline at end of file
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": false,
"removeComments": false,
"preserveConstEnums": true,
"target": "es2016",
"strictNullChecks": true,
"noUnusedParameters": false,
"noUnusedLocals": true,
"outDir": "out",
"sourceMap": true,
"lib": [
"es2016",
"dom"
]
},
"exclude": [
"node_modules"
]
}
......@@ -66,7 +66,8 @@
"node",
"electron-main",
"electron-browser",
"extensions"
"extensions",
"smoke"
]
]
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册