Merge VS Code 1.26.1 (#2394)

* Squash merge commits for 1.26 (#1) (#2323)

* Polish tag search as per feedback (#55269)

* Polish tag search as per feedback

* Updated regex

* Allow users to opt-out of features that send online requests in the background (#55097)

* settings sweep #54690

* Minor css tweaks to enable eoverflow elipsis in more places (#55277)

* fix an issue with titlebarheight when not scaling with zoom

* Settings descriptions update #54690

* fixes #55209

* Settings editor - many padding fixes

* More space above level 2 label

* Fixing Cannot debug npm script using Yarn #55103

* Settings editor - show ellipsis when description overflows

* Settings editor - ... fix measuring around links, relayout

* Setting descriptions

* Settings editor - fix ... for some short lines, fix select container width

* Settings editor - overlay trees so scrollable shadow is full width

* Fix #54133 - missing extension settings after reload

* Settings color token description tweak

* Settings editor - disable overflow indicator temporarily, needs to be faster

* Added command to Run the selected npm script

* fixes #54452

* fixes #54929

* fixes #55248

* prefix command with extension name

* Contribute run selected to the context menu

* node-debug@1.26.6

* Allow terminal rendererType to be swapped out at runtime

Part of #53274
Fixes #55344

* Settings editor - fix not focusing search when restoring editor
setInput must be actually async. Will be fixed naturally when we aren't using winJS promises...

* Settings editor - TOC should only expand the section with a selected item

* Bump node-debug2

* Settings editor - Tree focus outlines

* Settings editor - don't blink the scrollbar when toc selection changes
And hide TOC correctly when the editor is narrow

* Settings editor - header rows should not be selectable

* fixes #54877

* change debug assignee to isi

* Settings sweep (#54690)

* workaround for #55051

* Settings sweep (#54690)

* settings sweep

#54690

* Don't try closing tags when you type > after another >

* Describe what implementation code lens does

Fixes #55370

* fix javadoc formatter setting description

* fixes #55325

* update to officical TS version

* Settings editor - Even more padding, use semibold instead of bold

* Fix #55357 - fix TOC twistie

* fixes #55288

* explorer: refresh on di change file system provider registration

fixes #53256

* Disable push to Linux repo to test standalone publisher

* New env var to notify log level to extensions #54001

* Disable snippets in extension search (when not in suggest dropdown) (#55281)

* Disable snippits in extension search (when not in suggest dropdown)

* Add monaco input contributions

* Fix bug preventing snippetSuggestions from taking effect in sub-editors

* Latest emmet helper to fix #52366

* Fix comment updates for threads within same file

* Allow extensions to log telemetry to log files #54001

* Pull latest css grammar

* files.exclude control - use same style for "add" vs "edit"

* files.exclude control - focus/keyboard behavior

* don't show menubar too early

* files.exclude - better styling

* Place cursor at end of extensions search box on autofill (#55254)

* Place cursor at end of extensions search box on autofill

* Use position instead of selection

* fix linux build issue (empty if block)

* Settings editor - fix extension category prefixes

* Settings editor - add simple ellipsis for first line that overflows, doesn't cover case when first line does not overflow but there is more text, TODO

* File/Text search provider docs

* Fixes #52655

* Include epoch (#55008)

* Fixes #53385

* Fixes #49480

*  VS Code Insiders (Users) not opening Fixes #55353

* Better handling of the case when the extension host fails to start

* Fixes #53966

*  Remove confusing Start from wordPartLeft commands ID

* vscode-xterm@3.6.0-beta12

Fixes #55488

* Initial size is set to infinity!! Fixes #55461

* Polish embeddedEditorBackground

* configuration service misses event

* Fix #55224 - fix duplicate results in multiroot workspace from splitting the diskseach query

* Select all not working in issue reporter on mac, fixes #55424

* Disable fuzzy matching for extensions autosuggest (#55498)

* Fix clipping of extensions search border in some third party themes (#55504)

* fixes #55538

* Fix bug causing an aria alert to not be shown the third time
 (and odd numbers thereafter)

* Settings editor - work around rendering glitch with webkit-line-clamp

* Settings editor - revert earlier '...' changes

* Settings editor - move enumDescription to its own div, because it disturbs -webkit-line-clamp for some reason

* Settings editor - better overflow indicator

* Don't show existing filters in autocomplete (#55495)

* Dont show existing filters in autocomplete

* Simplify

* Settings Editor: Add aria labels for input elements Fixes: #54836 (#55543)

* fixes #55223

* Update vscode-css-languageservice to 3.0.10-next.1

* Fix #55509 - settings navigation

* Fix #55519

* Fix #55520

* FIx #55524

* Fix #55556 - include wordSeparators in all search queries, so findTextInFiles can respect isWordMatch correctly

* oss updates for endgame

* Fix unit tests

* fixes #55522

* Avoid missing manifest error from bubbling up #54757

* Settings format crawl

* Search provider - Fix FileSearchProvider to return array, not progress

* Fix #55598

* Settings editor - fix NPE rendering settings with no description

* dont render inden guides in search box (#55600)

* fixes #55454

* More settings crawl

* Another change for #55598 - maxResults applies to FileSearch and TextSearch but not FileIndex

* Fix FileSearchProvider unit tests for progress change

* fixes #55561

* Settings description update for #54690

* Update setting descriptions for online services

* Minor edits

* fixes #55513

* fixes #55451

* Fix #55612 - fix findTextInFiles cancellation

* fixes #55539

* More setting description tweaks

* Setting to disable online experiments #54354

* fixes #55507

* fixes #55515

* Show online services action only in Insiders for now

* Settings editor - change toc behavior default to 'filter'

* Settings editor - nicer filter count style during search

* Fix #55617 - search viewlet icons

* Settings editor - better styling for element count indicator

* SearchProvider - fix NPE when searching extraFileResources

* Allow extends to work without json suffix

Fixes #16905

* Remove accessability options logic entirely

Follow up on #55451

* use latest version of DAP

* fixes #55490

* fixes #55122

* fixes #52332

* Avoid assumptions about git: URIs (fixes #36236)

* relative path for descriptions

* resourece: get rid of isFile context key

fixes #48275

* Register previous ids for compatibility (#53497)

* more tuning for #48275

* no need to always re-read "files explorer"

fixes #52003

* read out active composites properly

fixes #51967

* Update link colors for hc theme to meet color contrast ratio, fixes #55651

Also updated link color for `textLinkActiveForeground` to be the same as `textLinkForeground` as it wasn't properly updated

* detect 'winpty-agent.exe'; fixes #55672

* node-debug@1.26.7

* reset counter on new label

* Settings editor - fix multiple setting links in one description

* Settings editor - color code blocks in setting descriptions, fix #55532

* Settings editor - hover color in TOC

* Settings editor - fix navigation NPE

* Settings editor - fix text control width

* Settings editor - maybe fix #55684

* Fix bug causing cursor to not move on paste

* fixes #53582

* Use ctrlCmd instead of ctrl for go down from search box

* fixes #55264

* fixes #55456

* filter for spcaes before triggering search (#55611)

* Fix #55698 - don't lose filtered TOC counts when refreshing TOC

* fixes #55421

* fixes #28979

* fixes #55576

* only add check for updates to windows/linux help

* readonly files: append decoration to label

fixes #53022

* debug: do not show toolbar while initialising

fixes #55026

* Opening launch.json should not activate debug extensions

fixes #55029

* fixes #55435

* fixes #55434

* fixes #55439

* trigger menu only on altkey up

* Fix #50555 - fix settings editor memory leak

* Fix #55712 - no need to focus 'a' anymore when restoring control focus after tree render

* fixes #55335

* proper fix for readonly model

fixes #53022

* improve FoldingRangeKind spec (for #55686)

* Use class with static fields (fixes #55494)

* Fixes #53671

* fixes #54630

* [html] should disable ionic suggestions by default. Currently forces deprecated Ionic v1 suggestions in .html files while typing. Fixes #53324

* cleanup deps

* debug issues back to andre

* update electron for smoketest

* Fix #55757 - prevent settings tabs from overflowing

* Fix #53897 - revert setting menu defaults to old editor

* Add enum descriptions to `typescript.preferences.importModuleSpecifier`

* Fix #55767 - leaking style elements from settings editor

* Fix #55521 - prevent flashing when clicking in exclude control

* Update Git modified color for contrast ratio, fixes #53140

* Revert "Merge branch 'master' of github.com:Microsoft/vscode"

This reverts commit bf46b6bfbae0cab99c2863e1244a916181fa9fbc, reversing
changes made to e275a424483dfb4ed33b428c97d5e2c441d6b917.

* Revert "Revert "Merge branch 'master' of github.com:Microsoft/vscode""

This reverts commit 53949d963f39e40757557c6526332354a31d9154.

* don't ask to install an incomplete menu

* Fix NPE in terminal AccessibilityManager

Fixes #55744

* don't display fallback menu unless we've closed the last window

* fixes #55547

* Fix smoke tests for extension search box

* Update OSSREADME.json for Electron 2.0.5

* Update distro

Includes Chromium license changes

* fix #55455

* fix #55865

* fixes #55893

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation (#55805)

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation

* ONly show on @recommended or @recommended:workspace

* Make more consistant

* Fix #55911

* Understand json activity (#55926)

* Understand json file activity

* Refactoring

* adding composer.json

* Distro update for experiments

* use terminal.processId for auto-attach; fixes #55918

* Reject invalid URI with vscode.openFolder (for #55891)

* improve win32 setup system vs user detection

fixes #55840

fixes #55840

delay winreg import

related to #55840

show notification earlier

related to #55840

fix #55840

update inno setup message

related to #55840

* Fix #55593 - this code only operates on local paths, so use fsPath and Uri.file instead

* Bring back the old menu due to electron 2.0 issues (#55913)

* add the old menu back for native menus

* make menu labels match

* `vscode.openFolder`: treat missing URI schema gracefully (for #55891)

* delay EH reattach; fixes #55955

* Mark all json files under appSettingsHome as settings

* Use localized strings for telemetry opt-out

* Exception when saving file editor opened from remote file provider (fixes #55051)

* Remove terminal menu from stable

Fixes 56003

* VSCode Insiders crashes on open with TypeError: Cannot read property 'lastIndexOf' of undefined. Fixes #54933

* improve fix for #55891

* fix #55916

* Improve #55891

* increase EH debugging restart delay; fixes #55955

* Revert "Don't include non-resource entries in history quick pick"

This reverts commit 37209a838e9f7e9abe6dc53ed73cdf1e03b72060.

* Diff editor: horizontal scrollbar height is smaller (fixes #56062)

* improve openFolder uri fix (correctly treat backslashes)

* fixes #56116
repair ipc for native menubar keybindings

* Fix #56240 - Open the JSON settings editor instead of the UI editor

* Fix #55536

* uriDisplay: if no formatter is registered fall back to getPathlabel

fixes #56104

* VSCode hangs when opening python file. Fixes #56377

* VS Code Hangs When Opening Specific PowerShell File. Fixes #56430

* Fix #56433 - search extraFileResources even when no folders open

* Workaround #55649

* Fix in master #56371

* Fix tests #56371

* Fix in master #56317

* increase version to 1.26.1

* Fixes #56387: Handle SIGPIPE in extension host

* fixes #56185

* Fix merge issues (part 1)

* Fix build breaks (part 1)

* Build breaks (part 2)

* Build breaks (part 3)

* More build breaks (part 4)

* Fix build breaks (part 5)

* WIP

* Fix menus

* Render query result and message panels (#2363)

* Put back query editor hot exit changes

* Fix grid changes that broke profiler (#2365)

* Update APIs for saving query editor state

* Fix restore view state for profiler and edit data

* Updating custom default themes to support 4.5:1 contrast ratio

* Test updates

* Fix Extension Manager and Windows Setup

* Update license headers

* Add appveyor and travis files back

* Fix hidden modal dropdown issue
This commit is contained in:
Karl Burtram
2018-09-04 14:55:00 -07:00
committed by GitHub
parent 3763278366
commit 81329fa7fa
2638 changed files with 118456 additions and 64012 deletions

View File

@@ -4,7 +4,7 @@
"name": "Jxck/assert",
"license": "MIT",
"licenseDetail": [
"The Source EULA",
"The MIT License (MIT)",
"",
"Copyright (c) 2011 Jxck",
"",

View File

@@ -9,6 +9,7 @@ const { join } = require('path');
const path = require('path');
const mocha = require('mocha');
const events = require('events');
const MochaJUnitReporter = require('mocha-junit-reporter');
const defaultReporterName = process.platform === 'win32' ? 'list' : 'spec';
@@ -21,7 +22,7 @@ const optimist = require('optimist')
.describe('debug', 'open dev tools, keep window open, reuse app data').string('debug')
.describe('reporter', 'the mocha reporter').string('reporter').default('reporter', defaultReporterName)
.describe('reporter-options', 'the mocha reporter options').string('reporter-options').default('reporter-options', '')
.describe('tfs').boolean('tfs')
.describe('tfs').string('tfs')
.describe('help', 'show the help').alias('help', 'h');
const argv = optimist.argv;
@@ -96,26 +97,6 @@ function parseReporterOption(value) {
return r ? { [r[1]]: r[2] } : {};
}
class TFSReporter extends mocha.reporters.Base {
constructor(runner) {
super(runner);
runner.on('pending', test => {
console.log('PEND', test.fullTitle());
});
runner.on('pass', test => {
console.log('OK ', test.fullTitle(), `(${test.duration}ms)`);
});
runner.on('fail', test => {
console.log('FAIL', test.fullTitle(), `(${test.duration}ms)`);
});
runner.once('end', () => {
this.epilogue();
});
}
}
app.on('ready', () => {
const win = new BrowserWindow({
@@ -141,7 +122,13 @@ app.on('ready', () => {
const runner = new IPCRunner();
if (argv.tfs) {
new TFSReporter(runner);
new mocha.reporters.Spec(runner);
new MochaJUnitReporter(runner, {
reporterOptions: {
testsuitesTitle: `${argv.tfs} ${process.platform}`,
mochaFile: process.env.BUILD_ARTIFACTSTAGINGDIRECTORY ? path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${argv.tfs.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) : undefined
}
});
} else {
const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter);
let Reporter;

View File

@@ -270,5 +270,5 @@ function runTests(opts) {
ipcRenderer.on('run', (e, opts) => {
initLoader(opts);
runTests(opts).catch(err => console.error(err));
runTests(opts).catch(err => console.error(typeof err === 'string' ? err : JSON.stringify(err)));
});

View File

@@ -1,59 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Grid Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../out/vs/loader.js"></script>
<script>
require.config({ baseUrl: '../out' });
require(['vs/base/browser/ui/splitview/grid'], ({ Grid }) => {
const grid = new Grid(document.body);
const layout = () => grid.layout(document.body.clientWidth, document.body.clientHeight);
window.onresize = layout;
layout();
});
</script>
<style>
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.node {
width: 100%;
height: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
}
.action {
display: inline-block;
width: 16px;
height: 16px;
border: 1px solid #3c3c3c;
text-align: center;
line-height: 16px;
border-radius: 3px;
}
.action:hover {
cursor: pointer;
background-color: #ffffff54;
}
</style>
</head>
<body>
</body>
</html>

View File

@@ -7,8 +7,6 @@
<body>
<div id="mocha"></div>
<!--<script src="https://cdn.rawgit.com/jquery/jquery/2.1.4/dist/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/Automattic/expect.js/0.3.1/index.js"></script>-->
<script src="/out/vs/loader.js"></script>
<script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>

View File

@@ -1,20 +1,39 @@
# VS Code Smoke Test
## How to run
### Run
```
# Dev
yarn smoketest
# Build
yarn smoketest --build "path/to/code"
yarn smoketest --build PATH_TO_BUILD
```
The script calls mocha, so all mocha arguments should work fine. For example, use `-f Git` to filter all tests except the `Git` tests.
### Run for a release
A `--verbose` flag can be used to log to the console all the low level driver calls make to Code.
You must always run the smoketest version which matches the release you are testing. So, if you want to run the smoketest for a release build (eg `release/1.22`), you need that version of the smoke tests too:
Screenshots can be captured when tests fail. In order to get them,you need to use the argument `--screenshots SCREENSHOT_DIR`.
```
git checkout release/1.22
yarn
yarn smoketest --build PATH_TO_RELEASE_BUILD
```
### Debug
- `--verbose` logs all the low level driver calls made to Code;
- `-f PATTERN` filters the tests to be run. You can also use pretty much any mocha argument;
- `--screenshots SCREENSHOT_DIR` captures screenshots when tests fail.
### Develop
Start a watch task in `test/smoke`:
```
cd test/smoke
yarn watch
```
## Pitfalls

View File

@@ -22,10 +22,12 @@
"@types/webdriverio": "4.6.1",
"concurrently": "^3.5.1",
"cpx": "^1.5.0",
"electron": "1.7.7",
"electron": "^2.0.6",
"htmlparser2": "^3.9.2",
"mkdirp": "^0.5.1",
"mocha": "^3.2.0",
"mocha": "^5.2.0",
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",
"ncp": "^2.0.0",
"portastic": "^1.0.1",
"rimraf": "^2.6.1",

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { Workbench } from './areas/workbench/workbench';
import * as fs from 'fs';
import * as cp from 'child_process';
import { Code, spawn, SpawnOptions } from './vscode/code';
import { Logger } from './logger';
@@ -26,7 +25,6 @@ export class Application {
private _code: Code | undefined;
private _workbench: Workbench;
private keybindings: any[];
constructor(private options: ApplicationOptions) { }
@@ -65,7 +63,7 @@ export class Application {
async start(): Promise<any> {
await this._start();
await this.code.waitForElement('.explorer-folders-view');
await this.code.waitForActiveElement(`.editor-container[id="workbench.editor.walkThroughPart"] > div > div[tabIndex="0"]`);
await this.code.waitForActiveElement(`.editor-instance[id="workbench.editor.walkThroughPart"] > div > div[tabIndex="0"]`);
}
async restart(options: { workspaceOrFolder?: string, extraArgs?: string[] }): Promise<any> {
@@ -75,7 +73,6 @@ export class Application {
}
private async _start(workspaceOrFolder = this.options.workspacePath, extraArgs: string[] = []): Promise<any> {
await this.retrieveKeybindings();
cp.execSync('git checkout .', { cwd: this.options.workspacePath });
await this.startApplication(workspaceOrFolder, extraArgs);
await this.checkWindowReady();
@@ -108,10 +105,12 @@ export class Application {
userDataDir: this.options.userDataDir,
extensionsPath: this.options.extensionsPath,
logger: this.options.logger,
extraArgs
verbose: this.options.verbose,
log: this.options.log,
extraArgs,
});
this._workbench = new Workbench(this._code, this.keybindings, this.userDataPath);
this._workbench = new Workbench(this._code, this.userDataPath);
}
private async checkWindowReady(): Promise<any> {
@@ -127,20 +126,4 @@ export class Application {
// as soon as they open (eg quick open)
await new Promise(c => setTimeout(c, 500));
}
private retrieveKeybindings(): Promise<void> {
return new Promise((c, e) => {
fs.readFile(process.env.VSCODE_KEYBINDINGS_PATH as string, 'utf8', (err, data) => {
if (err) {
throw err;
}
try {
this.keybindings = JSON.parse(data);
c();
} catch (e) {
throw new Error(`Error parsing keybindings JSON: ${e}`);
}
});
});
}
}

View File

@@ -38,7 +38,7 @@ export function setup() {
await app.code.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.ERROR));
const problems = new Problems(app.code, app.workbench);
const problems = new Problems(app.code);
await problems.showProblemsView();
await app.code.waitForElement(Problems.getSelectorInProblemsView(ProblemSeverity.ERROR));
await problems.hideProblemsView();

View File

@@ -25,6 +25,8 @@ export function setup() {
config.configurations[0].protocol = 'inspector';
fs.writeFileSync(launchJsonPath, JSON.stringify(config, undefined, 4), 'utf8');
// force load from disk since file events are sometimes missing
await app.workbench.quickopen.runCommand('File: Revert File');
await app.workbench.editor.waitForEditorContents('launch.json', contents => /"protocol": "inspector"/.test(contents));
assert.equal(config.configurations[0].request, 'launch');
@@ -47,15 +49,12 @@ export function setup() {
it('start debugging', async function () {
const app = this.app as Application;
// TODO@isidor
await new Promise(c => setTimeout(c, 100));
port = await app.workbench.debug.startDebugging();
await new Promise((c, e) => {
const request = http.get(`http://localhost:${port}`);
request.on('error', e);
app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6, 'looking for index.js and line 6').then(c, e);
app.workbench.debug.waitForStackFrame(sf => /index\.js$/.test(sf.name) && sf.lineNumber === 6, 'looking for index.js and line 6').then(c, e);
});
});
@@ -79,13 +78,13 @@ export function setup() {
await app.workbench.debug.stepIn();
const first = await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js', 'looking for response.js');
const first = await app.workbench.debug.waitForStackFrame(sf => /response\.js$/.test(sf.name), 'looking for response.js');
await app.workbench.debug.stepOver();
await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js' && sf.lineNumber === first.lineNumber + 1, `looking for response.js and line ${first.lineNumber + 1}`);
await app.workbench.debug.waitForStackFrame(sf => /response\.js$/.test(sf.name) && sf.lineNumber === first.lineNumber + 1, `looking for response.js and line ${first.lineNumber + 1}`);
await app.workbench.debug.stepOut();
await app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 7, `looking for index.js and line 7`);
await app.workbench.debug.waitForStackFrame(sf => /index\.js$/.test(sf.name) && sf.lineNumber === 7, `looking for index.js and line 7`);
});
it('continue', async function () {
@@ -96,7 +95,7 @@ export function setup() {
await new Promise((c, e) => {
const request = http.get(`http://localhost:${port}`);
request.on('error', e);
app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6, `looking for index.js and line 6`).then(c, e);
app.workbench.debug.waitForStackFrame(sf => /index\.js$/.test(sf.name) && sf.lineNumber === 6, `looking for index.js and line 6`).then(c, e);
});
});
@@ -113,4 +112,4 @@ export function setup() {
await app.workbench.debug.stopDebugging();
});
});
}
}

View File

@@ -55,7 +55,12 @@ export class Debug extends Viewlet {
}
async openDebugViewlet(): Promise<any> {
await this.commands.runCommand('workbench.view.debug');
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+shift+d');
} else {
await this.code.dispatchKeybinding('ctrl+shift+d');
}
await this.code.waitForElement(DEBUG_VIEW);
}
@@ -71,7 +76,7 @@ export class Debug extends Viewlet {
}
async startDebugging(): Promise<number> {
await this.commands.runCommand('workbench.action.debug.start');
await this.code.dispatchKeybinding('f5');
await this.code.waitForElement(PAUSE);
await this.code.waitForElement(DEBUG_STATUS_BAR);
const portPrefix = 'Port: ';
@@ -115,7 +120,7 @@ export class Debug extends Viewlet {
}
async focusStackFrame(name: string, message: string): Promise<any> {
await this.code.waitAndClick(SPECIFIC_STACK_FRAME(name));
await this.code.waitAndClick(SPECIFIC_STACK_FRAME(name), 0, 0);
await this.editors.waitForTab(name);
}
@@ -125,7 +130,7 @@ export class Debug extends Viewlet {
await this.code.waitForSetValue(REPL_FOCUSED, text);
// Wait for the keys to be picked up by the editor model such that repl evalutes what just got typed
await this.editor.waitForEditorContents('debug:input', s => s.indexOf(text) >= 0);
await this.editor.waitForEditorContents('debug:replinput', s => s.indexOf(text) >= 0);
await this.code.dispatchKeybinding('enter');
await this.code.waitForElement(CONSOLE_INPUT_OUTPUT);
await this.waitForOutput(output => accept(output[output.length - 1] || ''));

View File

@@ -53,18 +53,18 @@ export function setup() {
const app = this.app as Application;
await app.workbench.quickopen.openFile('app.js');
await app.workbench.editor.gotoDefinition('app.js', 'express', 11);
await app.workbench.editor.gotoDefinition('app.js', 'app', 14);
await app.workbench.editors.waitForActiveTab('index.d.ts');
await app.workbench.editor.waitForHighlightingLine('app.js', 11);
});
it(`verifies that 'Peek Definition' works`, async function () {
const app = this.app as Application;
await app.workbench.quickopen.openFile('app.js');
const peek = await app.workbench.editor.peekDefinition('app.js', 'express', 11);
const peek = await app.workbench.editor.peekDefinition('app.js', 'app', 14);
await peek.waitForFile('index.d.ts');
await peek.waitForFile('app.js');
});
});
}

View File

@@ -3,15 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
export class Editors {
constructor(private code: Code, private commands: Commands) { }
constructor(private code: Code) { }
async saveOpenedFile(): Promise<any> {
await this.commands.runCommand('workbench.action.files.save');
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+s');
} else {
await this.code.dispatchKeybinding('ctrl+s');
}
}
async selectTab(tabName: string, untitled: boolean = false): Promise<void> {
@@ -20,7 +23,7 @@ export class Editors {
}
async waitForActiveEditor(filename: string): Promise<any> {
const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] textarea`;
const selector = `.editor-instance .monaco-editor[data-uri$="${filename}"] textarea`;
return this.code.waitForActiveElement(selector);
}
@@ -38,7 +41,12 @@ export class Editors {
}
async newUntitledFile(): Promise<void> {
await this.commands.runCommand('workbench.action.files.newUntitledFile');
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+n');
} else {
await this.code.dispatchKeybinding('ctrl+n');
}
await this.waitForEditorFocus('Untitled-1', true);
}
}

View File

@@ -5,7 +5,6 @@
import { Viewlet } from '../workbench/viewlet';
import { Editors } from '../editor/editors';
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
export class Explorer extends Viewlet {
@@ -13,12 +12,16 @@ export class Explorer extends Viewlet {
private static readonly EXPLORER_VIEWLET = 'div[id="workbench.view.explorer"]';
private static readonly OPEN_EDITORS_VIEW = `${Explorer.EXPLORER_VIEWLET} .split-view-view:nth-child(1) .title`;
constructor(code: Code, private commands: Commands, private editors: Editors) {
constructor(code: Code, private editors: Editors) {
super(code);
}
openExplorerView(): Promise<any> {
return this.commands.runCommand('workbench.view.explorer');
async openExplorerView(): Promise<any> {
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+shift+e');
} else {
await this.code.dispatchKeybinding('ctrl+shift+e');
}
}
async waitForOpenEditorsViewTitle(fn: (title: string) => boolean): Promise<void> {

View File

@@ -22,7 +22,7 @@ export function setup() {
await app.reload();
await app.workbench.extensions.openExtensionsViewlet();
await app.workbench.runCommand('Smoke Test Check');
await app.workbench.quickopen.runCommand('Smoke Test Check');
await app.workbench.statusbar.waitForStatusbarText('smoke test', 'VS Code Smoke Test Check');
});
});

View File

@@ -4,31 +4,36 @@
*--------------------------------------------------------------------------------------------*/
import { Viewlet } from '../workbench/viewlet';
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
const SEARCH_BOX = 'div.extensions-viewlet[id="workbench.view.extensions"] input.search-box';
const SEARCH_BOX = 'div.extensions-viewlet[id="workbench.view.extensions"] .monaco-editor textarea';
export class Extensions extends Viewlet {
constructor(code: Code, private commands: Commands) {
constructor(code: Code) {
super(code);
}
async openExtensionsViewlet(): Promise<any> {
await this.commands.runCommand('workbench.view.extensions');
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+shift+x');
} else {
await this.code.dispatchKeybinding('ctrl+shift+x');
}
await this.code.waitForActiveElement(SEARCH_BOX);
}
async searchForExtension(name: string): Promise<any> {
await this.code.waitAndClick(SEARCH_BOX);
await this.code.waitForActiveElement(SEARCH_BOX);
await this.code.waitForSetValue(SEARCH_BOX, `name:"${name}"`);
await this.code.waitForTypeInEditor(SEARCH_BOX, `name:"${name}"`);
}
async installExtension(name: string): Promise<void> {
await this.searchForExtension(name);
await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${name}"] .extension li[class='action-item'] .extension-action.install`);
await this.code.waitForElement(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${name}"] .extension li[class='action-item'] .extension-action.reload`);
const ariaLabel = `${name}. Press enter for extension details.`;
await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${ariaLabel}"] .extension li[class='action-item'] .extension-action.install`);
await this.code.waitForElement(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${ariaLabel}"] .extension li[class='action-item'] .extension-action.reload`);
}
}

View File

@@ -48,29 +48,24 @@ export function setup() {
const app = this.app as Application;
await app.workbench.scm.openSCMViewlet();
await app.workbench.scm.waitForChange('app.js', 'Modified');
await app.workbench.scm.stage('app.js');
await app.workbench.scm.waitForChange('app.js', 'Index Modified');
await app.workbench.scm.unstage('app.js');
await app.workbench.scm.waitForChange('app.js', 'Modified');
});
it(`stages, commits changes and verifies outgoing change`, async function () {
const app = this.app as Application;
await app.workbench.scm.openSCMViewlet();
await app.workbench.scm.waitForChange('app.js', 'Modified');
await app.workbench.scm.stage('app.js');
await app.workbench.scm.waitForChange('app.js', 'Index Modified');
await app.workbench.scm.commit('first commit');
await app.code.waitForTextContent(SYNC_STATUSBAR, ' 0↓ 1↑');
await app.workbench.runCommand('Git: Stage All Changes');
await app.workbench.quickopen.runCommand('Git: Stage All Changes');
await app.workbench.scm.waitForChange('index.jade', 'Index Modified');
await app.workbench.scm.commit('second commit');

View File

@@ -4,19 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import { Viewlet } from '../workbench/viewlet';
import { Commands } from '../workbench/workbench';
import { IElement } from '../../vscode/driver';
import { findElement, findElements, Code } from '../../vscode/code';
const VIEWLET = 'div[id="workbench.view.scm"]';
const SCM_INPUT = `${VIEWLET} .scm-editor textarea`;
const SCM_RESOURCE = `${VIEWLET} .monaco-list-row > .resource`;
const SCM_RESOURCE_GROUP = `${VIEWLET} .monaco-list-row > .resource-group`;
const REFRESH_COMMAND = `div[id="workbench.parts.sidebar"] .actions-container a.action-label[title="Refresh"]`;
const COMMIT_COMMAND = `div[id="workbench.parts.sidebar"] .actions-container a.action-label[title="Commit"]`;
const SCM_RESOURCE_CLICK = (name: string) => `${SCM_RESOURCE} .monaco-icon-label[title*="${name}"] .label-name`;
const SCM_RESOURCE_ACTION_CLICK = (name: string, actionName: string) => `${SCM_RESOURCE} .monaco-icon-label[title*="${name}"] .actions .action-label[title="${actionName}"]`;
const SCM_RESOURCE_GROUP_COMMAND_CLICK = (name: string) => `${SCM_RESOURCE_GROUP} .actions .action-label[title="${name}"]`;
interface Change {
name: string;
@@ -41,12 +38,12 @@ function toChange(element: IElement): Change {
export class SCM extends Viewlet {
constructor(code: Code, private commands: Commands) {
constructor(code: Code) {
super(code);
}
async openSCMViewlet(): Promise<any> {
await this.commands.runCommand('workbench.view.scm');
await this.code.dispatchKeybinding('ctrl+shift+g');
await this.code.waitForElement(SCM_INPUT);
}
@@ -65,14 +62,12 @@ export class SCM extends Viewlet {
async stage(name: string): Promise<void> {
await this.code.waitAndClick(SCM_RESOURCE_ACTION_CLICK(name, 'Stage Changes'));
}
async stageAll(): Promise<void> {
await this.code.waitAndClick(SCM_RESOURCE_GROUP_COMMAND_CLICK('Stage All Changes'));
await this.waitForChange(name, 'Index Modified');
}
async unstage(name: string): Promise<void> {
await this.code.waitAndClick(SCM_RESOURCE_ACTION_CLICK(name, 'Unstage Changes'));
await this.waitForChange('app.js', 'Modified');
}
async commit(message: string): Promise<void> {

View File

@@ -3,28 +3,32 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
const SEARCH_INPUT = '.settings-search-input input';
export class KeybindingsEditor {
constructor(private code: Code, private commands: Commands) { }
constructor(private code: Code) { }
async updateKeybinding(command: string, keybinding: string, ariaLabel: string): Promise<any> {
await this.commands.runCommand('workbench.action.openGlobalKeybindings');
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+k cmd+s');
} else {
await this.code.dispatchKeybinding('ctrl+k ctrl+s');
}
await this.code.waitForActiveElement(SEARCH_INPUT);
await this.code.waitForSetValue(SEARCH_INPUT, command);
await this.code.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item');
await this.code.waitForElement('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item.focused.selected');
await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item');
await this.code.waitForElement('.keybindings-list-container .monaco-list-row.keybinding-item.focused.selected');
await this.code.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item .action-item .icon.add');
await this.code.waitForElement('.defineKeybindingWidget .monaco-inputbox.synthetic-focus');
await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item .action-item .icon.add');
await this.code.waitForActiveElement('.defineKeybindingWidget .monaco-inputbox input');
await this.code.dispatchKeybinding(keybinding);
await this.code.dispatchKeybinding('enter');
await this.code.waitForElement(`div[aria-label="Keybindings"] div[aria-label="Keybinding is ${ariaLabel}."]`);
await this.code.waitForElement(`.keybindings-list-container div[aria-label="Keybinding is ${ariaLabel}."]`);
}
}

View File

@@ -11,7 +11,7 @@ export function setup() {
it('turns off editor line numbers and verifies the live change', async function () {
const app = this.app as Application;
await app.workbench.explorer.openFile('app.js');
await app.workbench.quickopen.openFile('app.js');
await app.code.waitForElements('.line-numbers', false, elements => !!elements.length);
await app.workbench.settingsEditor.addUserSetting('editor.lineNumbers', '"off"');

View File

@@ -5,10 +5,10 @@
import * as fs from 'fs';
import * as path from 'path';
import { Commands } from '../workbench/workbench';
import { Editor } from '../editor/editor';
import { Editors } from '../editor/editors';
import { Code } from '../../vscode/code';
import { QuickOpen } from '../quickopen/quickopen';
export enum ActivityBarPosition {
LEFT = 0,
@@ -19,10 +19,10 @@ const SEARCH_INPUT = '.settings-search-input input';
export class SettingsEditor {
constructor(private code: Code, private userDataPath: string, private commands: Commands, private editors: Editors, private editor: Editor) { }
constructor(private code: Code, private userDataPath: string, private editors: Editors, private editor: Editor, private quickopen: QuickOpen) { }
async addUserSetting(setting: string, value: string): Promise<void> {
await this.commands.runCommand('workbench.action.openGlobalSettings');
await this.openSettings();
await this.code.waitAndClick(SEARCH_INPUT);
await this.code.waitForActiveElement(SEARCH_INPUT);
@@ -37,7 +37,11 @@ export class SettingsEditor {
const settingsPath = path.join(this.userDataPath, 'User', 'settings.json');
await new Promise((c, e) => fs.writeFile(settingsPath, '{}', 'utf8', err => err ? e(err) : c()));
await this.commands.runCommand('workbench.action.openGlobalSettings');
await this.openSettings();
await this.editor.waitForEditorContents('settings.json', c => c === '{}', '.editable-preferences-editor-container');
}
private async openSettings(): Promise<void> {
await this.quickopen.runCommand('Preferences: Open User Settings');
}
}

View File

@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
export enum ProblemSeverity {
@@ -15,18 +14,26 @@ export class Problems {
static PROBLEMS_VIEW_SELECTOR = '.panel.markers-panel';
constructor(private code: Code, private commands: Commands) { }
constructor(private code: Code) { }
public async showProblemsView(): Promise<any> {
await this.commands.runCommand('workbench.actions.view.problems');
await this.toggleProblemsView();
await this.waitForProblemsView();
}
public async hideProblemsView(): Promise<any> {
await this.commands.runCommand('workbench.actions.view.problems');
await this.toggleProblemsView();
await this.code.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR, el => !el);
}
private async toggleProblemsView(): Promise<void> {
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+shift+m');
} else {
await this.code.dispatchKeybinding('ctrl+shift+m');
}
}
public async waitForProblemsView(): Promise<void> {
await this.code.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR);
}

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Code } from '../../vscode/code';
export class QuickInput {
static QUICK_INPUT = '.quick-input-widget';
static QUICK_INPUT_INPUT = `${QuickInput.QUICK_INPUT} .quick-input-box input`;
static QUICK_INPUT_FOCUSED_ELEMENT = `${QuickInput.QUICK_INPUT} .quick-open-tree .monaco-tree-row.focused .monaco-highlighted-label`;
constructor(private code: Code) { }
async closeQuickInput(): Promise<void> {
await this.code.dispatchKeybinding('escape');
await this.waitForQuickInputClosed();
}
async waitForQuickInputOpened(retryCount?: number): Promise<void> {
await this.code.waitForActiveElement(QuickInput.QUICK_INPUT_INPUT, retryCount);
}
private async waitForQuickInputClosed(): Promise<void> {
await this.code.waitForElement(QuickInput.QUICK_INPUT, r => !!r && r.attributes.style.indexOf('display: none;') !== -1);
}
}

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { Editors } from '../editor/editors';
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
export class QuickOpen {
@@ -16,14 +15,18 @@ export class QuickOpen {
static QUICK_OPEN_ENTRY_SELECTOR = 'div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties .monaco-tree-row .quick-open-entry';
static QUICK_OPEN_ENTRY_LABEL_SELECTOR = 'div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties .monaco-tree-row .quick-open-entry .label-name';
constructor(private code: Code, private commands: Commands, private editors: Editors) { }
constructor(private code: Code, private editors: Editors) { }
async openQuickOpen(value: string): Promise<void> {
let retries = 0;
// other parts of code might steal focus away from quickopen :(
while (retries < 5) {
await this.commands.runCommand('workbench.action.quickOpen');
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+p');
} else {
await this.code.dispatchKeybinding('ctrl+p');
}
try {
await this.waitForQuickOpenOpened(10);
@@ -43,7 +46,7 @@ export class QuickOpen {
}
async closeQuickOpen(): Promise<void> {
await this.commands.runCommand('workbench.action.closeQuickOpen');
await this.code.dispatchKeybinding('escape');
await this.waitForQuickOpenClosed();
}
@@ -97,7 +100,11 @@ export class QuickOpen {
let retries = 0;
while (++retries < 10) {
await this.commands.runCommand('workbench.action.gotoSymbol');
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+shift+o');
} else {
await this.code.dispatchKeybinding('ctrl+shift+o');
}
const text = await this.code.waitForTextContent('div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row .quick-open-entry .monaco-icon-label .label-name .monaco-highlighted-label span');

View File

@@ -3,16 +3,23 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import { Application } from '../../application';
export function setup() {
describe('Search', () => {
after(function () {
const app = this.app as Application;
cp.execSync('git checkout .', { cwd: app.workspacePath });
cp.execSync('git reset --hard origin/master', { cwd: app.workspacePath });
});
it('searches for body & checks for correct result number', async function () {
const app = this.app as Application;
await app.workbench.search.openSearchViewlet();
await app.workbench.search.searchFor('body');
await app.workbench.search.waitForResultText('14 results in 5 files');
await app.workbench.search.waitForResultText('21 results in 6 files');
});
it('searches only for *.js files & checks for correct result number', async function () {
@@ -30,8 +37,8 @@ export function setup() {
it('dismisses result & checks for correct result number', async function () {
const app = this.app as Application;
await app.workbench.search.searchFor('body');
await app.workbench.search.removeFileMatch(1);
await app.workbench.search.waitForResultText('10 results in 4 files');
await app.workbench.search.removeFileMatch('app.js');
await app.workbench.search.waitForResultText('17 results in 5 files');
});
it('replaces first search result with a replace term', async function () {
@@ -40,13 +47,13 @@ export function setup() {
await app.workbench.search.searchFor('body');
await app.workbench.search.expandReplace();
await app.workbench.search.setReplaceText('ydob');
await app.workbench.search.replaceFileMatch(1);
await app.workbench.search.waitForResultText('10 results in 4 files');
await app.workbench.search.replaceFileMatch('app.js');
await app.workbench.search.waitForResultText('17 results in 5 files');
await app.workbench.search.searchFor('ydob');
await app.workbench.search.setReplaceText('body');
await app.workbench.search.replaceFileMatch(1);
await app.workbench.search.replaceFileMatch('app.js');
await app.workbench.search.waitForNoResultText();
});
});
}

View File

@@ -4,42 +4,60 @@
*--------------------------------------------------------------------------------------------*/
import { Viewlet } from '../workbench/viewlet';
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
const VIEWLET = 'div[id="workbench.view.search"] .search-view';
const INPUT = `${VIEWLET} .search-widget .search-container .monaco-inputbox input`;
const INCLUDE_INPUT = `${VIEWLET} .query-details .file-types.includes .monaco-inputbox input`;
const FILE_MATCH = filename => `${VIEWLET} .results .filematch[data-resource$="${filename}"]`;
async function retry(setup: () => Promise<any>, attempt: () => Promise<any>) {
let count = 0;
while (true) {
await setup();
try {
await attempt();
return;
} catch (err) {
if (++count > 5) {
throw err;
}
}
}
}
export class Search extends Viewlet {
constructor(code: Code, private commands: Commands) {
constructor(code: Code) {
super(code);
}
async openSearchViewlet(): Promise<any> {
await this.commands.runCommand('workbench.view.search');
await this.code.waitForActiveElement(INPUT);
if (process.platform === 'darwin') {
await this.code.dispatchKeybinding('cmd+shift+f');
} else {
await this.code.dispatchKeybinding('ctrl+shift+f');
}
await this.waitForInputFocus(INPUT);
}
async searchFor(text: string): Promise<void> {
await this.code.waitAndClick(INPUT);
await this.code.waitForActiveElement(INPUT);
await this.waitForInputFocus(INPUT);
await this.code.waitForSetValue(INPUT, text);
await this.submitSearch();
}
async submitSearch(): Promise<void> {
await this.code.waitAndClick(INPUT);
await this.code.waitForActiveElement(INPUT);
await this.waitForInputFocus(INPUT);
await this.code.dispatchKeybinding('enter');
await this.code.waitForElement(`${VIEWLET} .messages[aria-hidden="false"]`);
}
async setFilesToIncludeText(text: string): Promise<void> {
await this.code.waitAndClick(INCLUDE_INPUT);
await this.code.waitForActiveElement(INCLUDE_INPUT);
await this.waitForInputFocus(INCLUDE_INPUT);
await this.code.waitForSetValue(INCLUDE_INPUT, text || '');
}
@@ -51,29 +69,68 @@ export class Search extends Viewlet {
await this.code.waitAndClick(`${VIEWLET} .query-details.more .more`);
}
async removeFileMatch(index: number): Promise<void> {
await this.code.waitAndMove(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch`);
const file = await this.code.waitForTextContent(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`);
await this.code.waitAndClick(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-remove`);
await this.code.waitForTextContent(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`, void 0, result => result !== file);
async removeFileMatch(filename: string): Promise<void> {
const fileMatch = FILE_MATCH(filename);
await retry(
() => this.code.waitAndClick(fileMatch),
() => this.code.waitForElement(`${fileMatch} .action-label.icon.action-remove`, el => !!el && el.top > 0 && el.left > 0, 10)
);
// ¯\_(ツ)_/¯
await new Promise(c => setTimeout(c, 500));
await this.code.waitAndClick(`${fileMatch} .action-label.icon.action-remove`);
await this.code.waitForElement(fileMatch, el => !el);
}
async expandReplace(): Promise<void> {
await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.collapse`);
}
async setReplaceText(text: string): Promise<void> {
await this.code.waitAndClick(`${VIEWLET} .search-widget .replace-container .monaco-inputbox input[title="Replace"]`);
await this.code.waitForElement(`${VIEWLET} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`);
await this.code.waitForSetValue(`${VIEWLET} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`, text);
async collapseReplace(): Promise<void> {
await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.expand`);
}
async replaceFileMatch(index: number): Promise<void> {
await this.code.waitAndMove(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch`);
await this.code.waitAndClick(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-replace-all`);
async setReplaceText(text: string): Promise<void> {
await this.code.waitForSetValue(`${VIEWLET} .search-widget .replace-container .monaco-inputbox input[title="Replace"]`, text);
}
async replaceFileMatch(filename: string): Promise<void> {
const fileMatch = FILE_MATCH(filename);
await retry(
() => this.code.waitAndClick(fileMatch),
() => this.code.waitForElement(`${fileMatch} .action-label.icon.action-replace-all`, el => !!el && el.top > 0 && el.left > 0, 10)
);
// ¯\_(ツ)_/¯
await new Promise(c => setTimeout(c, 500));
await this.code.waitAndClick(`${fileMatch} .action-label.icon.action-replace-all`);
}
async waitForResultText(text: string): Promise<void> {
await this.code.waitForTextContent(`${VIEWLET} .messages[aria-hidden="false"] .message>p`, text);
}
async waitForNoResultText(): Promise<void> {
await this.code.waitForElement(`${VIEWLET} .messages[aria-hidden="false"] .message>p`, el => !el);
}
private async waitForInputFocus(selector: string): Promise<void> {
let retries = 0;
// other parts of code might steal focus away from input boxes :(
while (retries < 5) {
await this.code.waitAndClick(INPUT, 2, 2);
try {
await this.code.waitForActiveElement(INPUT, 10);
break;
} catch (err) {
if (++retries > 5) {
throw err;
}
}
}
}
}

View File

@@ -30,8 +30,8 @@ export function setup() {
const app = this.app as Application;
await app.workbench.statusbar.clickOn(StatusBarElement.BRANCH_STATUS);
await app.workbench.quickopen.waitForQuickOpenOpened();
await app.workbench.quickopen.closeQuickOpen();
await app.workbench.quickinput.waitForQuickInputOpened();
await app.workbench.quickinput.closeQuickInput();
await app.workbench.quickopen.openFile('app.js');
await app.workbench.statusbar.clickOn(StatusBarElement.INDENTATION_STATUS);

View File

@@ -3,24 +3,24 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// import { Application } from '../../application';
import { Application } from '../../application';
export function setup() {
describe('Terminal', () => {
// it(`opens terminal, runs 'echo' and verifies the output`, async function () {
// const app = this.app as Application;
it(`opens terminal, runs 'echo' and verifies the output`, async function () {
const app = this.app as Application;
// const expected = new Date().getTime().toString();
// await app.workbench.terminal.showTerminal();
// await app.workbench.terminal.runCommand(`echo ${expected}`);
// await app.workbench.terminal.waitForTerminalText(terminalText => {
// for (let index = terminalText.length - 2; index >= 0; index--) {
// if (!!terminalText[index] && terminalText[index].trim() === expected) {
// return true;
// }
// }
// return false;
// });
// });
const expected = new Date().getTime().toString();
await app.workbench.terminal.showTerminal();
await app.workbench.terminal.runCommand(`echo ${expected}`);
await app.workbench.terminal.waitForTerminalText(terminalText => {
for (let index = terminalText.length - 2; index >= 0; index--) {
if (!!terminalText[index] && terminalText[index].trim() === expected) {
return true;
}
}
return false;
});
});
});
}

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { Code } from '../../vscode/code';
import { Commands } from '../workbench/workbench';
const PANEL_SELECTOR = 'div[id="workbench.panel.terminal"]';
const XTERM_SELECTOR = `${PANEL_SELECTOR} .terminal-wrapper`;
@@ -12,16 +11,18 @@ const XTERM_TEXTAREA = `${XTERM_SELECTOR} textarea.xterm-helper-textarea`;
export class Terminal {
constructor(private code: Code, private commands: Commands) { }
constructor(private code: Code) { }
async showTerminal(): Promise<void> {
await this.commands.runCommand('workbench.action.terminal.toggleTerminal');
await this.code.dispatchKeybinding('ctrl+`');
await this.code.waitForActiveElement(XTERM_TEXTAREA);
await this.code.waitForTerminalBuffer(XTERM_SELECTOR, lines => lines.some(line => line.length > 0));
}
async runCommand(commandText: string): Promise<void> {
await this.code.waitForPaste(XTERM_TEXTAREA, commandText);
await this.code.writeInTerminal(XTERM_SELECTOR, commandText);
// hold your horses
await new Promise(c => setTimeout(c, 500));
await this.code.dispatchKeybinding('enter');
}

View File

@@ -17,7 +17,7 @@ export function setup() {
const readmeMd = 'readme.md';
const textToType = 'Hello, Code';
await app.workbench.explorer.openFile(readmeMd);
await app.workbench.quickopen.openFile(readmeMd);
await app.workbench.editor.waitForTypeInEditor(readmeMd, textToType);
await app.reload();

View File

@@ -14,6 +14,10 @@ export function setup() {
return;
}
const extensionName = 'German Language Pack for Visual Studio Code';
await app.workbench.extensions.openExtensionsViewlet();
await app.workbench.extensions.installExtension(extensionName);
await app.restart({ extraArgs: ['--locale=DE'] });
});

View File

@@ -12,6 +12,6 @@ export abstract class Viewlet {
constructor(protected code: Code) { }
async waitForTitle(fn: (title: string) => boolean): Promise<void> {
await this.code.waitForTextContent('.monaco-workbench-container .part.sidebar > .title > .title-label > span', undefined, fn);
await this.code.waitForTextContent('.monaco-workbench .part.sidebar > .title > .title-label > h2', undefined, fn);
}
}

View File

@@ -6,6 +6,7 @@
import { Explorer } from '../explorer/explorer';
import { ActivityBar } from '../activitybar/activityBar';
import { QuickOpen } from '../quickopen/quickopen';
import { QuickInput } from '../quickinput/quickinput';
import { Extensions } from '../extensions/extensions';
import { Search } from '../search/search';
import { Editor } from '../editor/editor';
@@ -23,9 +24,10 @@ export interface Commands {
runCommand(command: string): Promise<any>;
}
export class Workbench implements Commands {
export class Workbench {
readonly quickopen: QuickOpen;
readonly quickinput: QuickInput;
readonly editors: Editors;
readonly explorer: Explorer;
readonly activitybar: ActivityBar;
@@ -40,35 +42,22 @@ export class Workbench implements Commands {
readonly keybindingsEditor: KeybindingsEditor;
readonly terminal: Terminal;
constructor(private code: Code, private keybindings: any[], userDataPath: string) {
this.editors = new Editors(code, this);
this.quickopen = new QuickOpen(code, this, this.editors);
this.explorer = new Explorer(code, this.quickopen, this.editors);
constructor(code: Code, userDataPath: string) {
this.editors = new Editors(code);
this.quickopen = new QuickOpen(code, this.editors);
this.quickinput = new QuickInput(code);
this.explorer = new Explorer(code, this.editors);
this.activitybar = new ActivityBar(code);
this.search = new Search(code, this);
this.extensions = new Extensions(code, this);
this.editor = new Editor(code, this);
this.scm = new SCM(code, this);
this.debug = new Debug(code, this, this.editors, this.editor);
this.search = new Search(code);
this.extensions = new Extensions(code);
this.editor = new Editor(code, this.quickopen);
this.scm = new SCM(code);
this.debug = new Debug(code, this.quickopen, this.editors, this.editor);
this.statusbar = new StatusBar(code);
this.problems = new Problems(code, this);
this.settingsEditor = new SettingsEditor(code, userDataPath, this, this.editors, this.editor);
this.keybindingsEditor = new KeybindingsEditor(code, this);
this.terminal = new Terminal(code, this);
}
/**
* Retrieves the command from keybindings file and executes it with WebdriverIO client API
* @param command command (e.g. 'workbench.action.files.newUntitledFile')
*/
async runCommand(command: string): Promise<void> {
const binding = this.keybindings.find(x => x['command'] === command);
if (binding) {
await this.code.dispatchKeybinding(binding.key);
} else {
await this.quickopen.runCommand(command);
}
this.problems = new Problems(code);
this.settingsEditor = new SettingsEditor(code, userDataPath, this.editors, this.editor, this.quickopen);
this.keybindingsEditor = new KeybindingsEditor(code);
this.terminal = new Terminal(code);
}
}

View File

@@ -4,13 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as https from 'https';
import * as cp from 'child_process';
import * as path from 'path';
import * as minimist from 'minimist';
import * as tmp from 'tmp';
import * as rimraf from 'rimraf';
import * as mkdirp from 'mkdirp';
import { ncp } from 'ncp';
import { Application, Quality } from './application';
import { setup as setupDataMigrationTests } from './areas/workbench/data-migration.test';
@@ -40,7 +40,6 @@ const opts = minimist(args, {
'stable-build',
'wait-time',
'test-repo',
'keybindings',
'screenshots',
'log'
],
@@ -55,7 +54,6 @@ const opts = minimist(args, {
const workspaceFilePath = path.join(testDataPath, 'smoketest.code-workspace');
const testRepoUrl = 'https://github.com/Microsoft/vscode-smoketest-express';
const workspacePath = path.join(testDataPath, 'vscode-smoketest-express');
const keybindingsPath = path.join(testDataPath, 'keybindings.json');
const extensionsPath = path.join(testDataPath, 'extensions-dir');
mkdirp.sync(extensionsPath);
@@ -133,8 +131,6 @@ if (!fs.existsSync(electronPath || '')) {
}
const userDataDir = path.join(testDataPath, 'd');
// process.env.VSCODE_WORKSPACE_PATH = workspaceFilePath;
process.env.VSCODE_KEYBINDINGS_PATH = keybindingsPath;
let quality: Quality;
if (process.env.VSCODE_DEV === '1') {
@@ -145,14 +141,6 @@ if (process.env.VSCODE_DEV === '1') {
quality = Quality.Stable;
}
function getKeybindingPlatform(): string {
switch (process.platform) {
case 'darwin': return 'osx';
case 'win32': return 'win';
default: return process.platform;
}
}
function toUri(path: string): string {
if (process.platform === 'win32') {
return `${path.replace(/\\/g, '/')}`;
@@ -161,27 +149,6 @@ function toUri(path: string): string {
return `${path}`;
}
async function getKeybindings(): Promise<void> {
if (opts.keybindings) {
console.log('*** Using keybindings: ', opts.keybindings);
const rawKeybindings = fs.readFileSync(opts.keybindings);
fs.writeFileSync(keybindingsPath, rawKeybindings);
} else {
const keybindingsUrl = `https://raw.githubusercontent.com/Microsoft/vscode-docs/master/build/keybindings/doc.keybindings.${getKeybindingPlatform()}.json`;
console.log('*** Fetching keybindings...');
await new Promise((c, e) => {
https.get(keybindingsUrl, res => {
const output = fs.createWriteStream(keybindingsPath);
res.on('error', e);
output.on('error', e);
output.on('close', c);
res.pipe(output);
}).on('error', e);
});
}
}
async function createWorkspaceFile(): Promise<void> {
if (fs.existsSync(workspaceFilePath)) {
return;
@@ -222,8 +189,8 @@ async function setupRepository(): Promise<void> {
cp.spawnSync('git', ['clean', '-xdf'], { cwd: workspacePath });
}
console.log('*** Running npm install...');
cp.execSync('npm install', { cwd: workspacePath, stdio: 'inherit' });
console.log('*** Running yarn...');
cp.execSync('yarn', { cwd: workspacePath, stdio: 'inherit' });
}
}
@@ -231,7 +198,6 @@ async function setup(): Promise<void> {
console.log('*** Test data:', testDataPath);
console.log('*** Preparing smoketest setup...');
await getKeybindings();
await createWorkspaceFile();
await setupRepository();
@@ -245,8 +211,11 @@ function createApp(quality: Quality): Application {
loggers.push(new ConsoleLogger());
}
let log: string | undefined = undefined;
if (opts.log) {
loggers.push(new FileLogger(opts.log));
log = 'trace';
}
return new Application({
@@ -257,7 +226,9 @@ function createApp(quality: Quality): Application {
extensionsPath,
workspaceFilePath,
waitTime: parseInt(opts['wait-time'] || '0') || 20,
logger: new MultiLogger(loggers)
logger: new MultiLogger(loggers),
verbose: opts.verbose,
log
});
}
@@ -269,6 +240,13 @@ before(async function () {
after(async function () {
await new Promise(c => setTimeout(c, 500)); // wait for shutdown
if (opts.log) {
const logsDir = path.join(userDataDir, 'logs');
const destLogsDir = path.join(path.dirname(opts.log), 'logs');
await new Promise((c, e) => ncp(logsDir, destLogsDir, err => err ? e(err) : c()));
}
await new Promise((c, e) => rimraf(testDataPath, { maxBusyTries: 10 }, err => err ? e(err) : c()));
});

18
test/smoke/src/utils.ts Normal file
View File

@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ISuiteCallbackContext, ITestCallbackContext } from 'mocha';
export function describeRepeat(n: number, description: string, callback: (this: ISuiteCallbackContext) => void): void {
for (let i = 0; i < n; i++) {
describe(`${description} (iteration ${i})`, callback);
}
}
export function itRepeat(n: number, description: string, callback: (this: ITestCallbackContext, done: MochaDone) => any): void {
for (let i = 0; i < n; i++) {
it(`${description} (iteration ${i})`, callback);
}
}

View File

@@ -87,7 +87,9 @@ export interface SpawnOptions {
userDataDir: string;
extensionsPath: string;
logger: Logger;
verbose?: boolean;
extraArgs?: string[];
log?: string;
}
async function createDriverHandle(): Promise<string> {
@@ -122,6 +124,14 @@ export async function spawn(options: SpawnOptions): Promise<Code> {
args.unshift(repoPath);
}
if (options.verbose) {
args.push('--driver-verbose');
}
if (options.log) {
args.push('--log', options.log);
}
if (options.extraArgs) {
args.push(...options.extraArgs);
}
@@ -144,9 +154,13 @@ async function poll<T>(
retryInterval: number = 100 // millis
): Promise<T> {
let trial = 1;
let lastError: string = '';
while (true) {
if (trial > retryCount) {
console.error('** Timeout!');
console.error(lastError);
throw new Error(`Timeout: ${timeoutMessage} after ${(retryCount * retryInterval) / 1000} seconds.`);
}
@@ -156,13 +170,11 @@ async function poll<T>(
if (acceptFn(result)) {
return result;
} else {
lastError = 'Did not pass accept function';
}
} catch (e) {
// console.warn(e);
if (/Method not implemented/.test(e.message)) {
throw e;
}
lastError = Array.isArray(e.stack) ? e.stack.join(os.EOL) : e.stack;
}
await new Promise(resolve => setTimeout(resolve, retryInterval));
@@ -183,6 +195,10 @@ export class Code {
) {
this.driver = new Proxy(driver, {
get(target, prop, receiver) {
if (typeof prop === 'symbol') {
throw new Error('Invalid usage');
}
if (typeof target[prop] !== 'function') {
return target[prop];
}
@@ -217,7 +233,12 @@ export class Code {
async waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean): Promise<string> {
const windowId = await this.getActiveWindowId();
accept = accept || (result => textContent !== void 0 ? textContent === result : !!result);
return await poll(() => this.driver.getElements(windowId, selector).then(els => els[0].textContent), s => accept!(typeof s === 'string' ? s : ''), `get text content '${selector}'`);
return await poll(
() => this.driver.getElements(windowId, selector).then(els => els.length > 0 ? Promise.resolve(els[0].textContent) : Promise.reject(new Error('Element not found for textContent'))),
s => accept!(typeof s === 'string' ? s : ''),
`get text content '${selector}'`
);
}
async waitAndClick(selector: string, xoffset?: number, yoffset?: number): Promise<void> {
@@ -230,21 +251,11 @@ export class Code {
await poll(() => this.driver.doubleClick(windowId, selector), () => true, `double click '${selector}'`);
}
async waitAndMove(selector: string): Promise<void> {
const windowId = await this.getActiveWindowId();
await poll(() => this.driver.move(windowId, selector), () => true, `move '${selector}'`);
}
async waitForSetValue(selector: string, value: string): Promise<void> {
const windowId = await this.getActiveWindowId();
await poll(() => this.driver.setValue(windowId, selector, value), () => true, `set value '${selector}'`);
}
async waitForPaste(selector: string, value: string): Promise<void> {
const windowId = await this.getActiveWindowId();
await poll(() => this.driver.paste(windowId, selector, value), () => true, `paste '${selector}'`);
}
async waitForElements(selector: string, recursive: boolean, accept: (result: IElement[]) => boolean = result => result.length > 0): Promise<IElement[]> {
const windowId = await this.getActiveWindowId();
return await poll(() => this.driver.getElements(windowId, selector, recursive), accept, `get elements '${selector}'`);
@@ -275,6 +286,11 @@ export class Code {
await poll(() => this.driver.getTerminalBuffer(windowId, selector), accept, `get terminal buffer '${selector}'`);
}
async writeInTerminal(selector: string, value: string): Promise<void> {
const windowId = await this.getActiveWindowId();
await poll(() => this.driver.writeInTerminal(windowId, selector, value), () => true, `writeInTerminal '${selector}'`);
}
private async getActiveWindowId(): Promise<number> {
if (typeof this._activeWindowId !== 'number') {
const windows = await this.driver.getWindowIds();

39
test/smoke/test/index.js Normal file
View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
const Mocha = require('mocha');
const minimist = require('minimist');
const suite = 'Smoke Tests';
const [, , ...args] = process.argv;
const opts = minimist(args, {
string: [
'f'
]
});
const options = {
useColors: true,
timeout: 60000,
slow: 30000,
grep: opts['f']
};
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
options.reporter = 'mocha-multi-reporters';
options.reporterOptions = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
testsuitesTitle: `${suite} ${process.platform}`,
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
}
};
}
const mocha = new Mocha(options);
mocha.addFile('out/main.js');
mocha.run(failures => process.exit(failures ? -1 : 0));

View File

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

View File

@@ -41,9 +41,9 @@
version "8.0.33"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd"
"@types/node@^7.0.18":
version "7.0.46"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.46.tgz#c3dedd25558c676b3d6303e51799abb9c3f8f314"
"@types/node@^8.0.24":
version "8.10.23"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.23.tgz#e5ccfdafff42af5397c29669b6d7d65f7d629a00"
"@types/rimraf@2.0.2":
version "2.0.2"
@@ -86,6 +86,10 @@ ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
ansi-styles@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de"
@@ -238,9 +242,9 @@ braces@^1.8.2:
preserve "^0.2.0"
repeat-element "^1.1.2"
browser-stdout@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
builtin-modules@^1.0.0:
version "1.1.1"
@@ -271,6 +275,10 @@ chalk@0.5.1:
strip-ansi "^0.3.0"
supports-color "^0.2.0"
charenc@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
chokidar@^1.6.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
@@ -300,16 +308,14 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
dependencies:
delayed-stream "~1.0.0"
commander@2.15.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
commander@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d"
commander@2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
dependencies:
graceful-readlink ">= 1.0.0"
commander@^2.8.1:
version "2.11.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
@@ -367,6 +373,10 @@ cpx@^1.5.0:
shell-quote "^1.6.1"
subarg "^1.0.0"
crypt@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
cryptiles@2.x.x:
version "2.0.5"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
@@ -395,18 +405,18 @@ date-fns@^1.23.0:
version "1.29.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6"
debug@2.6.8:
version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
ms "2.0.0"
debug@2.6.9, debug@^2.1.3, debug@^2.2.0:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
ms "2.0.0"
debug@3.1.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -427,9 +437,9 @@ detect-libc@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
diff@3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
diff@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
dom-serializer@0:
version "0.1.0"
@@ -483,11 +493,11 @@ electron-download@^3.0.1:
semver "^5.3.0"
sumchecker "^1.2.0"
electron@1.7.7:
version "1.7.7"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.7.tgz#cfd89ca9eba79d763ac0b0c6dcc583792097b9b6"
electron@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/electron/-/electron-2.0.6.tgz#8e5c1bd2ebc08fa7a6ee906de3753c1ece9d7300"
dependencies:
"@types/node" "^7.0.18"
"@types/node" "^8.0.24"
electron-download "^3.0.1"
extract-zip "^1.0.3"
@@ -699,18 +709,7 @@ glob2base@^0.0.12:
dependencies:
find-index "^0.1.1"
glob@7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.2"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.5:
glob@7.1.2, glob@^7.0.5:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
@@ -725,13 +724,9 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
"graceful-readlink@>= 1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
growl@1.9.2:
version "1.9.2"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
growl@1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
har-schema@^1.0.5:
version "1.0.5"
@@ -765,6 +760,10 @@ has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -865,7 +864,7 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
is-buffer@^1.1.5:
is-buffer@^1.1.5, is-buffer@~1.1.1:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@@ -979,10 +978,6 @@ json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
json3@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
jsonfile@^2.1.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
@@ -1030,52 +1025,9 @@ load-json-file@^1.0.0:
pinkie-promise "^2.0.0"
strip-bom "^2.0.0"
lodash._baseassign@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
dependencies:
lodash._basecopy "^3.0.0"
lodash.keys "^3.0.0"
lodash._basecopy@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
lodash._basecreate@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821"
lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
lodash._isiterateecall@^3.0.0:
version "3.0.9"
resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
lodash.create@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7"
dependencies:
lodash._baseassign "^3.0.0"
lodash._basecreate "^3.0.0"
lodash._isiterateecall "^3.0.0"
lodash.isarguments@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
lodash.isarray@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
lodash.keys@^3.0.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
dependencies:
lodash._getnative "^3.0.0"
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
lodash@^4.16.4:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
lodash@^4.5.1:
version "4.17.5"
@@ -1092,6 +1044,14 @@ map-obj@^1.0.0, map-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
md5@^2.1.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
dependencies:
charenc "~0.0.1"
crypt "~0.0.1"
is-buffer "~1.1.1"
meow@^3.1.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
@@ -1149,7 +1109,7 @@ mime-types@~2.1.7:
dependencies:
mime-db "~1.33.0"
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4:
minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
@@ -1169,28 +1129,44 @@ mkdirp@0.5.0:
dependencies:
minimist "0.0.8"
mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.1:
mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
mocha@^3.2.0:
version "3.5.3"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d"
mocha-junit-reporter@^1.17.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.17.0.tgz#2e5149ed40fc5d2e3ca71e42db5ab1fec9c6d85c"
dependencies:
browser-stdout "1.3.0"
commander "2.9.0"
debug "2.6.8"
diff "3.2.0"
debug "^2.2.0"
md5 "^2.1.0"
mkdirp "~0.5.1"
strip-ansi "^4.0.0"
xml "^1.0.0"
mocha-multi-reporters@^1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82"
dependencies:
debug "^3.1.0"
lodash "^4.16.4"
mocha@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
dependencies:
browser-stdout "1.3.1"
commander "2.15.1"
debug "3.1.0"
diff "3.5.0"
escape-string-regexp "1.0.5"
glob "7.1.1"
growl "1.9.2"
glob "7.1.2"
growl "1.10.5"
he "1.1.1"
json3 "3.3.2"
lodash.create "3.1.1"
minimatch "3.0.4"
mkdirp "0.5.1"
supports-color "3.1.2"
supports-color "5.4.0"
ms@2.0.0:
version "2.0.0"
@@ -1726,6 +1702,12 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
dependencies:
ansi-regex "^3.0.0"
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@@ -1755,11 +1737,11 @@ sumchecker@^1.2.0:
debug "^2.2.0"
es6-promise "^4.0.5"
supports-color@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
supports-color@5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
dependencies:
has-flag "^1.0.0"
has-flag "^3.0.0"
supports-color@^0.2.0:
version "0.2.0"
@@ -1895,6 +1877,10 @@ wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
xml@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
xtend@~2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b"

13
test/tree/package.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "tree",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"koa": "^2.5.1",
"koa-mount": "^3.0.0",
"koa-route": "^3.2.0",
"koa-static": "^5.0.0",
"mz": "^2.7.0"
}
}

View File

@@ -0,0 +1,62 @@
<html>
<head>
<meta charset="utf-8">
<title>Tree</title>
<style>
#container {
width: 400;
height: 600;
border: 1px solid black;
}
.monaco-scrollable-element>.scrollbar>.slider {
background: rgba(100, 100, 100, .4);
}
</style>
</head>
<body>
<div id="container"></div>
<script src="/static/vs/loader.js"></script>
<script>
require.config({ baseUrl: '/static' });
require(['vs/base/browser/ui/tree/tree', 'vs/base/common/iterator'], ({ Tree }, { iter }) => {
const delegate = {
getHeight() { return 22; },
getTemplateId() { return 'template'; }
};
const renderer = {
templateId: 'template',
renderTemplate(container) { return container; },
renderElement(element, index, container) {
container.textContent = element;
},
disposeTemplate() { }
};
const tree = new Tree(container, delegate, [renderer]);
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/ls?path=');
xhr.send();
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
const data = JSON.parse(this.responseText);
performance.mark('before splice');
const start = performance.now();
tree.splice([0], 0, [data]);
console.log('splice took', performance.now() - start);
performance.mark('after splice');
}
};
});
</script>
</body>
</html>

39
test/tree/server.js Normal file
View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const fs = require('mz/fs');
const path = require('path');
const Koa = require('koa');
const _ = require('koa-route');
const serve = require('koa-static');
const mount = require('koa-mount');
const app = new Koa();
const root = path.dirname(path.dirname(__dirname));
async function getTree(fsPath, level) {
const element = path.basename(fsPath);
const stat = await fs.stat(fsPath);
if (!stat.isDirectory() || element === '.git' || element === '.build' || level >= 3) {
return { element };
}
const childNames = await fs.readdir(fsPath);
const children = await Promise.all(childNames.map(async childName => await getTree(path.join(fsPath, childName), level + 1)));
return { element, collapsible: true, collapsed: false, children };
}
app.use(serve('public'));
app.use(mount('/static', serve('../../out')));
app.use(_.get('/api/ls', async ctx => {
const relativePath = ctx.query.path;
const absolutePath = path.join(root, relativePath);
ctx.body = await getTree(absolutePath, 0);
}))
app.listen(3000);
console.log('http://localhost:3000');

290
test/tree/yarn.lock Normal file
View File

@@ -0,0 +1,290 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@^1.2.2:
version "1.3.5"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
dependencies:
mime-types "~2.1.18"
negotiator "0.6.1"
any-promise@^1.0.0, any-promise@^1.1.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
content-disposition@~0.5.0:
version "0.5.2"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
content-type@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
cookies@~0.7.0:
version "0.7.1"
resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b"
dependencies:
depd "~1.1.1"
keygrip "~1.0.2"
debug@*, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
debug@^2.6.1:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
ms "2.0.0"
deep-equal@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
depd@^1.1.0, depd@~1.1.1, depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
destroy@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
error-inject@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37"
escape-html@~1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
fresh@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
http-assert@^1.1.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a"
dependencies:
deep-equal "~1.0.1"
http-errors "~1.6.1"
http-errors@^1.2.8, http-errors@^1.6.3, http-errors@~1.6.1, http-errors@~1.6.2:
version "1.6.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.0"
statuses ">= 1.4.0 < 2"
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
is-generator-function@^1.0.3:
version "1.0.7"
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
keygrip@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91"
koa-compose@^3.0.0, koa-compose@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7"
dependencies:
any-promise "^1.1.0"
koa-compose@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877"
koa-convert@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0"
dependencies:
co "^4.6.0"
koa-compose "^3.0.0"
koa-is-json@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14"
koa-mount@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/koa-mount/-/koa-mount-3.0.0.tgz#08cab3b83d31442ed8b7e75c54b1abeb922ec197"
dependencies:
debug "^2.6.1"
koa-compose "^3.2.1"
koa-route@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/koa-route/-/koa-route-3.2.0.tgz#76298b99a6bcfa9e38cab6fe5c79a8733e758bce"
dependencies:
debug "*"
methods "~1.1.0"
path-to-regexp "^1.2.0"
koa-send@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb"
dependencies:
debug "^3.1.0"
http-errors "^1.6.3"
mz "^2.7.0"
resolve-path "^1.4.0"
koa-static@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943"
dependencies:
debug "^3.1.0"
koa-send "^5.0.0"
koa@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/koa/-/koa-2.5.1.tgz#79f8b95f8d72d04fe9a58a8da5ebd6d341103f9c"
dependencies:
accepts "^1.2.2"
content-disposition "~0.5.0"
content-type "^1.0.0"
cookies "~0.7.0"
debug "*"
delegates "^1.0.0"
depd "^1.1.0"
destroy "^1.0.3"
error-inject "~1.0.0"
escape-html "~1.0.1"
fresh "^0.5.2"
http-assert "^1.1.0"
http-errors "^1.2.8"
is-generator-function "^1.0.3"
koa-compose "^4.0.0"
koa-convert "^1.2.0"
koa-is-json "^1.0.0"
mime-types "^2.0.7"
on-finished "^2.1.0"
only "0.0.2"
parseurl "^1.3.0"
statuses "^1.2.0"
type-is "^1.5.5"
vary "^1.0.0"
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
methods@~1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
mime-db@~1.33.0:
version "1.33.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
mime-types@^2.0.7, mime-types@~2.1.18:
version "2.1.18"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
dependencies:
mime-db "~1.33.0"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
mz@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
dependencies:
any-promise "^1.0.0"
object-assign "^4.0.1"
thenify-all "^1.0.0"
negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
on-finished@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
dependencies:
ee-first "1.1.1"
only@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4"
parseurl@^1.3.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
path-is-absolute@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
path-to-regexp@^1.2.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
dependencies:
isarray "0.0.1"
resolve-path@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7"
dependencies:
http-errors "~1.6.2"
path-is-absolute "1.0.1"
setprototypeof@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
"statuses@>= 1.4.0 < 2", statuses@^1.2.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
thenify-all@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
dependencies:
thenify ">= 3.1.0 < 4"
"thenify@>= 3.1.0 < 4":
version "3.3.0"
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839"
dependencies:
any-promise "^1.0.0"
type-is@^1.5.5:
version "1.6.16"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194"
dependencies:
media-typer "0.3.0"
mime-types "~2.1.18"
vary@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"