Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5

This commit is contained in:
ADS Merger
2020-02-08 04:50:58 +00:00
parent 8c61538a27
commit 2af13c18d2
752 changed files with 16458 additions and 10063 deletions

View File

@@ -1,32 +1,36 @@
# Tests
# VSCode Tests
## Run
## Contents
The best way to run the Code tests is from the terminal. To make development changes to unit tests you need to be running `yarn run watch`. See [Development Workflow](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#incremental-build) for more details. From the `azuredatastudio` folder run:
**OS X and Linux**
./scripts/test.sh
**Windows**
scripts\test
This folder contains the various test runners for VSCode. Please refer to the documentation within for how to run them:
* `unit`: our suite of unit tests
* `integration`: our suite of API tests
* `smoke`: our suite of automated UI tests
* `ui`: our suite of manual UI tests
## Debug
To debug tests use `--debug` when running the test script. Also, the set of tests can be reduced with the `--run` and `--runGlob` flags. Both require a file path/pattern. Like so:
### Unit Tests (Electron-runner)
./scripts/test.sh --debug --runGrep **/extHost*.test.js
```
./scripts/test.[sh|bat]
```
## Coverage
All unit tests are run inside a electron-browser environment which access to DOM and Nodejs api. This is the closest to the enviroment in which VS Code itself ships. Notes:
The following command will create a `coverage` folder at the root of the workspace:
- use the `--debug` to see an electron window with dev tools which allows for debugging
- to run only a subset of tests use the `--run` or `--glob` options
**OS X and Linux**
### Unit Tests (Browser-runner)
./scripts/test.sh --coverage
```
yarn test-browser --browser webkit --browser chromium
```
**Windows**
Unit tests from layers `common` and `browser` are run inside `chromium`, `webkit`, and (soonish) `firefox` (using playwright). This complements our electron-based unit test runner and adds more coverage of supported platforms. Notes:
- these tests are part of the continuous build, that means you might have test failures that only happen with webkit on _windows_ or _chromium_ on linux
- you can these tests locally via yarn `test-browser --browser chromium --browser webkit`
- to debug, open `<vscode>/test/unit/browser/renderer.html` inside a browser and use the `?m=<amd_module>`-query to specify what AMD module to load, e.g `file:///Users/jrieken/Code/vscode/test/unit/browser/renderer.html?m=vs/base/test/common/strings.test` runs all tests from `strings.test.ts`
- to run only a subset of tests use the `--run` or `--glob` options
scripts\test --coverage

View File

@@ -20,20 +20,19 @@
"prepublishOnly": "npm run copy-package-version"
},
"devDependencies": {
"@types/debug": "4.1.5",
"@types/mkdirp": "0.5.1",
"@types/ncp": "2.0.1",
"@types/node": "8.0.33",
"@types/puppeteer": "^1.19.0",
"@types/node": "^12.11.7",
"@types/tmp": "0.1.0",
"concurrently": "^3.5.1",
"cpx": "^1.5.0",
"typescript": "2.9.2",
"typescript": "3.7.5",
"watch": "^1.0.2"
},
"dependencies": {
"mkdirp": "^0.5.1",
"ncp": "^2.0.0",
"puppeteer": "^1.19.0",
"tmp": "0.1.0",
"vscode-uri": "^2.0.3"
}

View File

@@ -133,6 +133,7 @@ export class Application {
extraArgs,
remote: this.options.remote,
web: this.options.web,
browser: this.options.browser,
headless: this.options.headless
});

View File

@@ -10,7 +10,7 @@ import * as fs from 'fs';
import * as mkdirp from 'mkdirp';
import { tmpName } from 'tmp';
import { IDriver, connect as connectElectronDriver, IDisposable, IElement, Thenable } from './driver';
import { connect as connectPuppeteerDriver, launch } from './puppeteerDriver';
import { connect as connectPlaywrightDriver, launch } from './playwrightDriver';
import { Logger } from './logger';
import { ncp } from 'ncp';
import { URI } from 'vscode-uri';
@@ -101,6 +101,8 @@ export interface SpawnOptions {
remote?: boolean;
/** Run in the web */
web?: boolean;
/** A specific browser to use (requires web: true) */
browser?: 'chromium' | 'webkit' | 'firefox';
/** Run in headless mode (only applies when web is true) */
headless?: boolean;
}
@@ -120,68 +122,69 @@ export async function spawn(options: SpawnOptions): Promise<Code> {
const outPath = codePath ? getBuildOutPath(codePath) : getDevOutPath();
const handle = await createDriverHandle();
const args = [
options.workspacePath,
'--skip-getting-started',
'--skip-release-notes',
'--sticky-quickopen',
'--disable-telemetry',
'--disable-updates',
'--disable-crash-reporter',
`--extensions-dir=${options.extensionsPath}`,
`--user-data-dir=${options.userDataDir}`,
'--driver', handle
];
const env = process.env;
if (options.remote) {
// Replace workspace path with URI
args[0] = `--${options.workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri=vscode-remote://test+test/${URI.file(options.workspacePath).path}`;
if (codePath) {
// running against a build: copy the test resolver extension
const testResolverExtPath = path.join(options.extensionsPath, 'vscode-test-resolver');
if (!fs.existsSync(testResolverExtPath)) {
const orig = path.join(repoPath, 'extensions', 'vscode-test-resolver');
await new Promise((c, e) => ncp(orig, testResolverExtPath, err => err ? e(err) : c()));
}
}
args.push('--enable-proposed-api=vscode.vscode-test-resolver');
const remoteDataDir = `${options.userDataDir}-server`;
mkdirp.sync(remoteDataDir);
env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir;
}
if (!codePath) {
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);
}
let child: cp.ChildProcess | undefined;
let connectDriver: typeof connectElectronDriver;
if (options.web) {
await launch(args);
connectDriver = connectPuppeteerDriver.bind(connectPuppeteerDriver, !!options.headless);
await launch(options.userDataDir, options.workspacePath, options.codePath);
connectDriver = connectPlaywrightDriver.bind(connectPlaywrightDriver, !!options.headless, options.browser);
} else {
const env = process.env;
const args = [
options.workspacePath,
'--skip-getting-started',
'--skip-release-notes',
'--sticky-quickopen',
'--disable-telemetry',
'--disable-updates',
'--disable-crash-reporter',
`--extensions-dir=${options.extensionsPath}`,
`--user-data-dir=${options.userDataDir}`,
'--driver', handle
];
if (options.remote) {
// Replace workspace path with URI
args[0] = `--${options.workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri=vscode-remote://test+test/${URI.file(options.workspacePath).path}`;
if (codePath) {
// running against a build: copy the test resolver extension
const testResolverExtPath = path.join(options.extensionsPath, 'vscode-test-resolver');
if (!fs.existsSync(testResolverExtPath)) {
const orig = path.join(repoPath, 'extensions', 'vscode-test-resolver');
await new Promise((c, e) => ncp(orig, testResolverExtPath, err => err ? e(err) : c()));
}
}
args.push('--enable-proposed-api=vscode.vscode-test-resolver');
const remoteDataDir = `${options.userDataDir}-server`;
mkdirp.sync(remoteDataDir);
env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir;
}
if (!codePath) {
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);
}
const spawnOptions: cp.SpawnOptions = { env };
child = cp.spawn(electronPath, args, spawnOptions);
instances.add(child);
child.once('exit', () => instances.delete(child!));
connectDriver = connectElectronDriver;
}
return connect(connectDriver, child, outPath, handle, options.logger);
}

View File

@@ -89,7 +89,7 @@ export class Editor {
const line = `${editor} .view-lines > .view-line:nth-child(${lineNumber})`;
const textarea = `${editor} textarea`;
await this.code.waitAndClick(line, 0, 0);
await this.code.waitAndClick(line, 1, 1);
await this.code.waitForActiveElement(textarea);
}

View File

@@ -17,27 +17,27 @@ export class Editors {
}
}
async selectTab(tabName: string, untitled: boolean = false): Promise<void> {
await this.code.waitAndClick(`.tabs-container div.tab[aria-label="${tabName}, tab"]`);
await this.waitForEditorFocus(tabName, untitled);
async selectTab(fileName: string): Promise<void> {
await this.code.waitAndClick(`.tabs-container div.tab[data-resource-name$="${fileName}"]`);
await this.waitForEditorFocus(fileName);
}
async waitForActiveEditor(filename: string): Promise<any> {
const selector = `.editor-instance .monaco-editor[data-uri$="${filename}"] textarea`;
async waitForActiveEditor(fileName: string): Promise<any> {
const selector = `.editor-instance .monaco-editor[data-uri$="${fileName}"] textarea`;
return this.code.waitForActiveElement(selector);
}
async waitForEditorFocus(fileName: string, untitled: boolean = false): Promise<void> {
async waitForEditorFocus(fileName: string): Promise<void> {
await this.waitForActiveTab(fileName);
await this.waitForActiveEditor(fileName);
}
async waitForActiveTab(fileName: string, isDirty: boolean = false): Promise<void> {
await this.code.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][aria-label="${fileName}, tab"]`);
await this.code.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][data-resource-name$="${fileName}"]`);
}
async waitForTab(fileName: string, isDirty: boolean = false): Promise<void> {
await this.code.waitForElement(`.tabs-container div.tab${isDirty ? '.dirty' : ''}[aria-label="${fileName}, tab"]`);
await this.code.waitForElement(`.tabs-container div.tab${isDirty ? '.dirty' : ''}[data-resource-name$="${fileName}"]`);
}
async newUntitledFile(): Promise<void> {
@@ -47,6 +47,6 @@ export class Editors {
await this.code.dispatchKeybinding('ctrl+n');
}
await this.waitForEditorFocus('Untitled-1', true);
await this.waitForEditorFocus('Untitled-1');
}
}

View File

@@ -3,17 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as puppeteer from 'puppeteer';
import * as playwright from 'playwright';
import { ChildProcess, spawn } from 'child_process';
import { join } from 'path';
import { mkdir } from 'fs';
import { promisify } from 'util';
import { IDriver, IDisposable } from './driver';
import { URI } from 'vscode-uri';
const width = 1200;
const height = 800;
const vscodeToPuppeteerKey: { [key: string]: string } = {
const vscodeToPlaywrightKey: { [key: string]: string } = {
cmd: 'Meta',
ctrl: 'Control',
shift: 'Shift',
@@ -26,7 +27,7 @@ const vscodeToPuppeteerKey: { [key: string]: string } = {
home: 'Home'
};
function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver {
function buildDriver(browser: playwright.Browser, page: playwright.Page): IDriver {
const driver: IDriver = {
_serviceBrand: undefined,
getWindowIds: () => {
@@ -45,8 +46,8 @@ function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver
const keys = chord.split('+');
const keysDown: string[] = [];
for (let i = 0; i < keys.length; i++) {
if (keys[i] in vscodeToPuppeteerKey) {
keys[i] = vscodeToPuppeteerKey[keys[i]];
if (keys[i] in vscodeToPlaywrightKey) {
keys[i] = vscodeToPlaywrightKey[keys[i]];
}
await page.keyboard.down(keys[i]);
keysDown.push(keys[i]);
@@ -68,7 +69,7 @@ function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver
await driver.click(windowId, selector, 0, 0);
await timeout(100);
},
setValue: async (windowId, selector, text) => page.evaluate(`window.driver.setValue('${selector}', '${text}')`),
setValue: async (windowId, selector, text) => page.evaluate(`window.driver.setValue('${selector}', '${text}')`).then(undefined),
getTitle: (windowId) => page.evaluate(`window.driver.getTitle()`),
isActiveElement: (windowId, selector) => page.evaluate(`window.driver.isActiveElement('${selector}')`),
getElements: (windowId, selector, recursive) => page.evaluate(`window.driver.getElements('${selector}', ${recursive})`),
@@ -86,25 +87,32 @@ function timeout(ms: number): Promise<void> {
// function runInDriver(call: string, args: (string | boolean)[]): Promise<any> {}
let args: string[] | undefined;
let server: ChildProcess | undefined;
let endpoint: string | undefined;
let workspacePath: string | undefined;
export async function launch(_args: string[]): Promise<void> {
args = _args;
const agentFolder = args.filter(e => e.includes('--user-data-dir='))[0].replace('--user-data-dir=', '');
export async function launch(userDataDir: string, _workspacePath: string, codeServerPath = process.env.VSCODE_REMOTE_SERVER_PATH): Promise<void> {
workspacePath = _workspacePath;
const agentFolder = userDataDir;
await promisify(mkdir)(agentFolder);
const env = {
VSCODE_AGENT_FOLDER: agentFolder,
VSCODE_REMOTE_SERVER_PATH: codeServerPath,
...process.env
};
let serverLocation: string | undefined;
if (codeServerPath) {
serverLocation = join(codeServerPath, `server.${process.platform === 'win32' ? 'cmd' : 'sh'}`);
} else {
serverLocation = join(__dirname, '..', '..', '..', `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`);
}
server = spawn(
join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`),
serverLocation,
['--browser', 'none', '--driver', 'web'],
{ env }
);
server.stderr.on('data', e => console.log('Server stderr: ' + e));
server.stdout.on('data', e => console.log('Server stdout: ' + e));
server.stderr?.on('data', error => console.log(`Server stderr: ${error}`));
server.stdout?.on('data', data => console.log(`Server stdout: ${data}`));
process.on('exit', teardown);
process.on('SIGINT', teardown);
process.on('SIGTERM', teardown);
@@ -120,7 +128,7 @@ function teardown(): void {
function waitForEndpoint(): Promise<string> {
return new Promise<string>(r => {
server!.stdout.on('data', (d: Buffer) => {
server!.stdout?.on('data', (d: Buffer) => {
const matches = d.toString('ascii').match(/Web UI available at (.+)/);
if (matches !== null) {
r(matches[1]);
@@ -129,20 +137,18 @@ function waitForEndpoint(): Promise<string> {
});
}
export function connect(headless: boolean, outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> {
export function connect(headless: boolean, engine: 'chromium' | 'webkit' | 'firefox' = 'chromium'): Promise<{ client: IDisposable, driver: IDriver }> {
return new Promise(async (c) => {
const browser = await puppeteer.launch({
const browser = await playwright[engine].launch({
// Run in Edge dev on macOS
// executablePath: '/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev',
headless,
slowMo: 80,
args: [`--window-size=${width},${height}`]
headless
});
const page = (await browser.pages())[0];
const page = (await browser.defaultContext().pages())[0];
await page.setViewport({ width, height });
await page.goto(`${endpoint}&folder=${args![1]}`);
await page.goto(`${endpoint}&folder=vscode-remote://localhost:9888${URI.file(workspacePath!).path}`);
const result = {
client: { dispose: () => teardown },
client: { dispose: () => browser.close() && teardown() },
driver: buildDriver(browser, page)
};
c(result);

View File

@@ -12,7 +12,7 @@ export const enum ProblemSeverity {
export class Problems {
static PROBLEMS_VIEW_SELECTOR = '.panel.markers-panel';
static PROBLEMS_VIEW_SELECTOR = '.panel .markers-panel';
constructor(private code: Code) { }

View File

@@ -109,11 +109,12 @@ export class Search extends Viewlet {
}
async waitForResultText(text: string): Promise<void> {
await this.code.waitForTextContent(`${VIEWLET} .messages .message>p`, text);
// The label can end with " - " depending on whether the search editor is enabled
await this.code.waitForTextContent(`${VIEWLET} .messages .message>span`, undefined, result => result.startsWith(text));
}
async waitForNoResultText(): Promise<void> {
await this.code.waitForElement(`${VIEWLET} .messages[aria-hidden="true"] .message>p`);
await this.code.waitForElement(`${VIEWLET} .messages[aria-hidden="true"] .message>span`);
}
private async waitForInputFocus(selector: string): Promise<void> {

View File

@@ -20,8 +20,6 @@ export const enum StatusBarElement {
export class StatusBar {
private readonly mainSelector = 'div[id="workbench.parts.statusbar"]';
private readonly leftSelector = '.statusbar-item.left';
private readonly rightSelector = '.statusbar-item.right';
constructor(private code: Code) { }
@@ -44,23 +42,23 @@ export class StatusBar {
private getSelector(element: StatusBarElement): string {
switch (element) {
case StatusBarElement.BRANCH_STATUS:
return `${this.mainSelector} ${this.leftSelector} .codicon.codicon-git-branch`;
return `.statusbar-item[id="status.scm"] .codicon.codicon-git-branch`;
case StatusBarElement.SYNC_STATUS:
return `${this.mainSelector} ${this.leftSelector} .codicon.codicon-sync`;
return `.statusbar-item[id="status.scm"] .codicon.codicon-sync`;
case StatusBarElement.PROBLEMS_STATUS:
return `${this.mainSelector} ${this.leftSelector} .codicon.codicon-error`;
return `.statusbar-item[id="status.problems"]`;
case StatusBarElement.SELECTION_STATUS:
return `${this.mainSelector} ${this.rightSelector}[title="Go to Line"]`;
return `.statusbar-item[id="status.editor.selection"]`;
case StatusBarElement.INDENTATION_STATUS:
return `${this.mainSelector} ${this.rightSelector}[title="Select Indentation"]`;
return `.statusbar-item[id="status.editor.indentation"]`;
case StatusBarElement.ENCODING_STATUS:
return `${this.mainSelector} ${this.rightSelector}[title="Select Encoding"]`;
return `.statusbar-item[id="status.editor.encoding"]`;
case StatusBarElement.EOL_STATUS:
return `${this.mainSelector} ${this.rightSelector}[title="Select End of Line Sequence"]`;
return `.statusbar-item[id="status.editor.eol"]`;
case StatusBarElement.LANGUAGE_STATUS:
return `${this.mainSelector} ${this.rightSelector}[title="Select Language Mode"]`;
return `.statusbar-item[id="status.editor.mode"]`;
case StatusBarElement.FEEDBACK_ICON:
return `${this.mainSelector} .statusbar-item.right[id="status.feedback"]`;
return `.statusbar-item[id="status.feedback"]`;
default:
throw new Error(element);
}

View File

@@ -16,6 +16,6 @@
"exclude": [
"node_modules",
"out",
"tools",
"tools"
]
}

View File

@@ -2,6 +2,11 @@
# yarn lockfile v1
"@types/debug@4.1.5":
version "4.1.5"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==
"@types/mkdirp@0.5.1":
version "0.5.1"
resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.1.tgz#ea887cd024f691c1ca67cce20b7606b053e43b0f"
@@ -21,17 +26,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.1.tgz#3b5c3a26393c19b400844ac422bd0f631a94d69d"
integrity sha512-aK9jxMypeSrhiYofWWBf/T7O+KwaiAHzM4sveCdWPn71lzUSMimRnKzhXDKfKwV1kWoBo2P1aGgaIYGLf9/ljw==
"@types/node@8.0.33":
version "8.0.33"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd"
integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A==
"@types/puppeteer@^1.19.0":
version "1.19.1"
resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.19.1.tgz#942ca62288953a0f5fbbc25c103b5f2ba28b60ab"
integrity sha512-ReWZvoEfMiJIA3AG+eM+nCx5GKrU2ANVYY5TC0nbpeiTCtnJbcqnmBbR8TkXMBTvLBYcuTOAELbTcuX73siDNQ==
dependencies:
"@types/node" "*"
"@types/node@^12.11.7":
version "12.12.26"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.26.tgz#213e153babac0ed169d44a6d919501e68f59dea9"
integrity sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA==
"@types/tmp@0.1.0":
version "0.1.0"
@@ -43,13 +41,6 @@ abbrev@1:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
agent-base@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies:
es6-promisify "^5.0.0"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@@ -130,11 +121,6 @@ async-each@^1.0.0:
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
async-limiter@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
atob@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
@@ -204,11 +190,6 @@ braces@^2.3.1:
split-string "^3.0.2"
to-regex "^3.0.1"
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
cache-base@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@@ -304,16 +285,6 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
concat-stream@1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
concurrently@^3.5.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.6.1.tgz#2f95baec5c4051294dfbb55b57a3b98a3e2b45ec"
@@ -371,27 +342,20 @@ date-fns@^1.23.0:
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
debug@^2.2.0, debug@^2.3.3:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^3.1.0, debug@^3.2.6:
debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
debug@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@@ -446,18 +410,6 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
es6-promise@^4.0.3:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
dependencies:
es6-promise "^4.0.3"
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -533,23 +485,6 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extract-zip@^1.6.6:
version "1.6.7"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=
dependencies:
concat-stream "1.6.2"
debug "2.6.9"
mkdirp "0.5.1"
yauzl "2.4.1"
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=
dependencies:
pend "~1.2.0"
filename-regex@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
@@ -729,14 +664,6 @@ hosted-git-info@^2.1.4:
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546"
integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==
https-proxy-agent@^2.2.1:
version "2.2.3"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.3.tgz#fb6cd98ed5b9c35056b5a73cd01a8a721d7193d1"
integrity sha512-Ytgnz23gm2DVftnzqRRz2dOXZbGd2uiajSw/95bPp6v53zPRspQjLm/AfBgqbJ2qfeRXWIOMVLpp86+/5yX39Q==
dependencies:
agent-base "^4.3.0"
debug "^3.1.0"
iconv-lite@^0.4.4:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -759,7 +686,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
inherits@2, inherits@^2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -1042,11 +969,6 @@ micromatch@^3.1.10:
snapdragon "^0.8.1"
to-regex "^3.0.2"
mime@^2.0.3:
version "2.4.4"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5"
integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==
minimatch@^3.0.2, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
@@ -1087,7 +1009,7 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1:
mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
@@ -1310,11 +1232,6 @@ path-type@^3.0.0:
dependencies:
pify "^3.0.0"
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
@@ -1335,30 +1252,6 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
progress@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
proxy-from-env@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=
puppeteer@^1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.19.0.tgz#e3b7b448c2c97933517078d7a2c53687361bebea"
integrity sha512-2S6E6ygpoqcECaagDbBopoSOPDv0pAZvTbnBgUY+6hq0/XDFDOLEMNlHF/SKJlzcaZ9ckiKjKDuueWI3FN/WXw==
dependencies:
debug "^4.1.0"
extract-zip "^1.6.6"
https-proxy-agent "^2.2.1"
mime "^2.0.3"
progress "^2.0.1"
proxy-from-env "^1.0.0"
rimraf "^2.6.1"
ws "^6.1.0"
randomatic@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
@@ -1387,7 +1280,7 @@ read-pkg@^3.0.0:
normalize-package-data "^2.3.2"
path-type "^3.0.0"
readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2:
readable-stream@^2.0.2, readable-stream@^2.0.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
@@ -1746,15 +1639,10 @@ tree-kill@^1.1.0:
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.1.tgz#5398f374e2f292b9dcc7b2e71e30a5c3bb6c743a"
integrity sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@2.9.2:
version "2.9.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
typescript@3.7.5:
version "3.7.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
union-value@^1.0.0:
version "1.0.1"
@@ -1822,21 +1710,7 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
dependencies:
async-limiter "~1.0.0"
yallist@^3.0.0, yallist@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
yauzl@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=
dependencies:
fd-slicer "~1.0.1"

View File

@@ -1,48 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var express = require('express');
var glob = require('glob');
var path = require('path');
var fs = require('fs');
var port = 8887;
var root = path.dirname(__dirname);
function template(str, env) {
return str.replace(/{{\s*([\w_\-]+)\s*}}/g, function (all, part) {
return env[part];
});
}
var app = express();
app.use('/out', express.static(path.join(root, 'out')));
app.use('/test', express.static(path.join(root, 'test')));
app.use('/node_modules', express.static(path.join(root, 'node_modules')));
app.get('/', function (req, res) {
glob('**/vs/{base,platform,editor}/**/test/{common,browser}/**/*.test.js', {
cwd: path.join(root, 'out'),
// ignore: ['**/test/{node,electron*}/**/*.js']
}, function (err, files) {
if (err) { return res.sendStatus(500); }
var modules = files
.map(function (file) { return file.replace(/\.js$/, ''); });
fs.readFile(path.join(__dirname, 'index.html'), 'utf8', function (err, templateString) {
if (err) { return res.sendStatus(500); }
res.send(template(templateString, {
modules: JSON.stringify(modules)
}));
});
});
});
app.listen(port, function () {
console.log('http://localhost:8887/');
});

5
test/integration/browser/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.DS_Store
npm-debug.log
Thumbs.db
node_modules/
out/

View File

@@ -0,0 +1,13 @@
# VS Code Integration test
### Run
```bash
# Dev (Electron)
scripts/test-integration.sh
# Dev (Web)
node test/integration/browser/out/index.js
```

View File

@@ -0,0 +1,18 @@
{
"name": "code-oss-dev-integration-test",
"version": "0.1.0",
"main": "./index.js",
"scripts": {
"postinstall": "npm run compile",
"compile": "yarn tsc"
},
"devDependencies": {
"@types/mkdirp": "0.5.1",
"@types/node": "^12.11.7",
"@types/rimraf": "2.0.2",
"@types/tmp": "^0.1.0",
"rimraf": "^2.6.1",
"tmp": "0.0.33",
"typescript": "3.7.5"
}
}

View File

@@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as cp from 'child_process';
import * as playwright from 'playwright';
import * as url from 'url';
import * as tmp from 'tmp';
import * as rimraf from 'rimraf';
const optimist = require('optimist')
.describe('workspacePath', 'path to the workspace to open in the test').string()
.describe('extensionDevelopmentPath', 'path to the extension to test').string()
.describe('extensionTestsPath', 'path to the extension tests').string()
.describe('debug', 'do not run browsers headless').boolean('debug')
.describe('browser', 'browser in which integration tests should run').string('browser').default('browser', 'chromium')
.describe('help', 'show the help').alias('help', 'h');
if (optimist.argv.help) {
optimist.showHelp();
process.exit(0);
}
const width = 1200;
const height = 800;
async function runTestsInBrowser(browserType: string, endpoint: string): Promise<void> {
const browser = await playwright[browserType].launch({ headless: !Boolean(optimist.argv.debug) });
const page = (await browser.defaultContext().pages())[0];
await page.setViewport({ width, height });
const host = url.parse(endpoint).host;
const protocol = 'vscode-remote';
const testWorkspaceUri = url.format({ pathname: path.resolve(optimist.argv.workspacePath), protocol, host, slashes: true });
const testExtensionUri = url.format({ pathname: path.resolve(optimist.argv.extensionDevelopmentPath), protocol, host, slashes: true });
const testFilesUri = url.format({ pathname: path.resolve(optimist.argv.extensionTestsPath), protocol, host, slashes: true });
const folderParam = testWorkspaceUri;
const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"]]`;
await page.goto(`${endpoint}&folder=${folderParam}&payload=${payloadParam}`);
await page.exposeFunction('codeAutomationLog', (type: string, args: any[]) => {
console[type](...args);
});
page.on('console', async (msg: playwright.ConsoleMessage) => {
const msgText = msg.text();
if (msgText.indexOf('vscode:exit') >= 0) {
await browser.close();
process.exit(msgText === 'vscode:exit 0' ? 0 : 1);
}
});
}
async function launchServer(): Promise<string> {
// Ensure a tmp user-data-dir is used for the tests
const tmpDir = tmp.dirSync({ prefix: 't' });
const testDataPath = tmpDir.name;
process.once('exit', () => rimraf.sync(testDataPath));
const userDataDir = path.join(testDataPath, 'd');
const env = {
VSCODE_AGENT_FOLDER: userDataDir,
...process.env
};
let serverLocation: string;
if (process.env.VSCODE_REMOTE_SERVER_PATH) {
serverLocation = path.join(process.env.VSCODE_REMOTE_SERVER_PATH, `server.${process.platform === 'win32' ? 'cmd' : 'sh'}`);
} else {
serverLocation = path.join(__dirname, '..', '..', '..', '..', `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`);
process.env.VSCODE_DEV = '1';
}
let serverProcess = cp.spawn(
serverLocation,
['--browser', 'none', '--driver', 'web'],
{ env }
);
serverProcess?.stderr?.on('data', error => console.log(`Server stderr: ${error}`));
if (optimist.argv.debug) {
serverProcess?.stdout?.on('data', data => console.log(`Server stdout: ${data}`));
}
function teardownServer() {
if (serverProcess) {
serverProcess.kill();
}
}
process.on('exit', teardownServer);
process.on('SIGINT', teardownServer);
process.on('SIGTERM', teardownServer);
return new Promise(c => {
serverProcess?.stdout?.on('data', data => {
const matches = data.toString('ascii').match(/Web UI available at (.+)/);
if (matches !== null) {
c(matches[1]);
}
});
});
}
launchServer().then(async endpoint => {
return runTestsInBrowser(optimist.argv.browser, endpoint);
}, console.error);

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": false,
"removeComments": false,
"preserveConstEnums": true,
"target": "es2017",
"strictNullChecks": true,
"noUnusedParameters": false,
"noUnusedLocals": true,
"outDir": "out",
"sourceMap": true,
"lib": [
"es2016",
"dom"
]
},
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,148 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/events@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
"@types/glob@*":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
dependencies:
"@types/events" "*"
"@types/minimatch" "*"
"@types/node" "*"
"@types/minimatch@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/mkdirp@0.5.1":
version "0.5.1"
resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.1.tgz#ea887cd024f691c1ca67cce20b7606b053e43b0f"
integrity sha512-XA4vNO6GCBz8Smq0hqSRo4yRWMqr4FPQrWjhJt6nKskzly4/p87SfuJMFYGRyYb6jo2WNIQU2FDBsY5r1BibUA==
dependencies:
"@types/node" "*"
"@types/node@*":
version "13.7.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.0.tgz#b417deda18cf8400f278733499ad5547ed1abec4"
integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==
"@types/node@^12.11.7":
version "12.12.26"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.26.tgz#213e153babac0ed169d44a6d919501e68f59dea9"
integrity sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA==
"@types/rimraf@2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e"
integrity sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ==
dependencies:
"@types/glob" "*"
"@types/node" "*"
"@types/tmp@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.1.0.tgz#19cf73a7bcf641965485119726397a096f0049bd"
integrity sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA==
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
glob@^7.1.3:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
rimraf@^2.6.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
tmp@0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
dependencies:
os-tmpdir "~1.0.2"
typescript@3.7.5:
version "3.7.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=

View File

@@ -1,21 +1,28 @@
# VS Code Smoke Test
Make sure you are on **Node v10.x**.
Make sure you are on **Node v12.x**.
### Run
```bash
# Install Dependencies and Compile
yarn --cwd test/smoke
yarn --cwd test/automation
# Dev
# Dev (Electron)
yarn smoketest
# Build
yarn smoketest --build PATH_TO_NEW_BUILD_PARENT_FOLDER --stable-build PATH_TO_LAST_STABLE_BUILD_PARENT_FOLDER
# Dev (Web)
yarn smoketest --web --browser <chromium|firefox|webkit>
# Remote
yarn smoketest --build PATH_TO_NEW_BUILD_PARENT_FOLDER --remote
# Build (Electron)
yarn smoketest --build <path latest built version> --stable-build <path to previous stable version>
# Build (Web - read instructions below)
yarn smoketest --build <path to web server folder> --web --browser <chromium|firefox|webkit>
# Remote (Electron)
yarn smoketest --build <path latest built version> --remote
```
### Run for a release
@@ -27,18 +34,33 @@ git checkout release/1.22
yarn --cwd test/smoke
```
#### Electron
In addition to the new build to be released you will need the previous stable build so that the smoketest can test the data migration.
The recommended way to make these builds available for the smoketest is by downloading their archive version (\*.zip) and extracting
them into two folders. Pass the folder paths to the smoketest as follows:
```bash
yarn smoketest --build PATH_TO_NEW_RELEASE_PARENT_FOLDER --stable-build PATH_TO_LAST_STABLE_RELEASE_PARENT_FOLDER
yarn smoketest --build <path latest built version> --stable-build <path to previous stable version>
```
#### Web
**macOS**: if you have downloaded the server with web bits, make sure to run the following command before unzipping it to avoid security issues on startup:
```bash
xattr -d com.apple.quarantine <path to server with web folder zip>
```
There is no support for testing an old version to a new one yet, so simply configure the `--build` command line argument to point to
the web server folder which includes the web client bits (e.g. `vscode-server-darwin-web` for macOS).
**Note**: make sure to point to the server that includes the client bits!
### 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;
- `-f PATTERN` (alias `-g 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

View File

@@ -27,7 +27,7 @@
"rimraf": "^2.6.1",
"strip-json-comments": "^2.0.1",
"tmp": "0.0.33",
"typescript": "2.9.2",
"typescript": "3.7.5",
"watch": "^1.0.2"
}
}

View File

@@ -28,9 +28,6 @@ export function setup() {
});
it('verifies that warning becomes an error once setting changed', async function () {
// settings might take a while to update?
this.timeout(40000);
const app = this.app as Application;
await app.workbench.settingsEditor.addUserSetting('css.lint.emptyRules', '"error"');
await app.workbench.quickopen.openFile('style.css');

View File

@@ -1,122 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as http from 'http';
import * as path from 'path';
import * as fs from 'fs';
import * as stripJsonComments from 'strip-json-comments';
import { Application } from '../../../../automation';
export function setup() {
describe('Debug', () => {
it('configure launch json', async function () {
const app = this.app as Application;
await app.workbench.debug.openDebugViewlet();
await app.workbench.quickopen.openFile('app.js');
await app.workbench.quickopen.runCommand('Debug: Open launch.json');
const launchJsonPath = path.join(app.workspacePathOrFolder, '.vscode', 'launch.json');
const content = fs.readFileSync(launchJsonPath, 'utf8');
const config = JSON.parse(stripJsonComments(content));
config.configurations[0].protocol = 'inspector';
fs.writeFileSync(launchJsonPath, JSON.stringify(config, undefined, 4), 'utf8');
// 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');
assert.equal(config.configurations[0].type, 'node');
if (process.platform === 'win32') {
assert.equal(config.configurations[0].program, '${workspaceFolder}\\bin\\www');
} else {
assert.equal(config.configurations[0].program, '${workspaceFolder}/bin/www');
}
});
it('breakpoints', async function () {
const app = this.app as Application;
await app.workbench.quickopen.openFile('index.js');
await app.workbench.debug.setBreakpointOnLine(6);
});
let port: number;
it('start debugging', async function () {
const app = this.app as Application;
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 => /index\.js$/.test(sf.name) && sf.lineNumber === 6, 'looking for index.js and line 6').then(c, e);
});
});
it('focus stack frames and variables', async function () {
const app = this.app as Application;
await app.workbench.debug.waitForVariableCount(4, 5);
await app.workbench.debug.focusStackFrame('layer.js', 'looking for layer.js');
await app.workbench.debug.waitForVariableCount(5, 6);
await app.workbench.debug.focusStackFrame('route.js', 'looking for route.js');
await app.workbench.debug.waitForVariableCount(3, 4);
await app.workbench.debug.focusStackFrame('index.js', 'looking for index.js');
await app.workbench.debug.waitForVariableCount(4, 5);
});
it('stepOver, stepIn, stepOut', async function () {
const app = this.app as Application;
await app.workbench.debug.stepIn();
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 => /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 => /index\.js$/.test(sf.name) && sf.lineNumber === 7, `looking for index.js and line 7`);
});
it('continue', async function () {
const app = this.app as Application;
await app.workbench.debug.continue();
await new Promise((c, e) => {
const request = http.get(`http://localhost:${port}`);
request.on('error', e);
app.workbench.debug.waitForStackFrame(sf => /index\.js$/.test(sf.name) && sf.lineNumber === 6, `looking for index.js and line 6`).then(c, e);
});
});
it('debug console', async function () {
const app = this.app as Application;
await app.workbench.debug.waitForReplCommand('2 + 2', r => r === '4');
});
it('debug console link', async function () {
const app = this.app as Application;
await app.workbench.debug.waitForReplCommand('"./app.js:5:1"', r => r.includes('app.js'));
await app.workbench.debug.waitForLink();
});
it('stop debugging', async function () {
const app = this.app as Application;
await app.workbench.debug.stopDebugging();
});
});
}

View File

@@ -1,80 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import { Application } from '../../../../automation';
const DIFF_EDITOR_LINE_INSERT = '.monaco-diff-editor .editor.modified .line-insert';
const SYNC_STATUSBAR = 'div[id="workbench.parts.statusbar"] .statusbar-item[title$="Synchronize Changes"]';
export function setup() {
describe('Git', () => {
before(async function () {
const app = this.app as Application;
cp.execSync('git config user.name testuser', { cwd: app.workspacePathOrFolder });
cp.execSync('git config user.email monacotools@microsoft.com', { cwd: app.workspacePathOrFolder });
});
it('reflects working tree changes', async function () {
const app = this.app as Application;
await app.workbench.scm.openSCMViewlet();
await app.workbench.quickopen.openFile('app.js');
await app.workbench.editor.waitForTypeInEditor('app.js', '.foo{}');
await app.workbench.editors.saveOpenedFile();
await app.workbench.quickopen.openFile('index.pug');
await app.workbench.editor.waitForTypeInEditor('index.pug', 'hello world');
await app.workbench.editors.saveOpenedFile();
await app.workbench.scm.refreshSCMViewlet();
await app.workbench.scm.waitForChange('app.js', 'Modified');
await app.workbench.scm.waitForChange('index.pug', 'Modified');
});
it('opens diff editor', async function () {
const app = this.app as Application;
await app.workbench.scm.openSCMViewlet();
await app.workbench.scm.openChange('app.js');
await app.code.waitForElement(DIFF_EDITOR_LINE_INSERT);
});
it('stages correctly', 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.openChange('app.js');
await new Promise(c => setTimeout(c, 1000));
await app.workbench.scm.unstage('app.js');
});
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.openChange('app.js');
await app.workbench.scm.stage('app.js');
await app.workbench.scm.commit('first commit');
await app.code.waitForTextContent(SYNC_STATUSBAR, ' 0↓ 1↑');
await app.workbench.quickopen.runCommand('Git: Stage All Changes');
await app.workbench.scm.waitForChange('index.pug', 'Index Modified');
await app.workbench.scm.commit('second commit');
await app.code.waitForTextContent(SYNC_STATUSBAR, ' 0↓ 2↑');
cp.execSync('git reset --hard origin/master', { cwd: app.workspacePathOrFolder });
});
});
}

View File

@@ -12,7 +12,7 @@ export function setup() {
await app.workbench.editors.newUntitledFile();
const untitled = 'Untitled-1';
const textToTypeInUntitled = 'Hello, Untitled Code';
const textToTypeInUntitled = 'Hello from Untitled';
await app.workbench.editor.waitForTypeInEditor(untitled, textToTypeInUntitled);
const readmeMd = 'readme.md';
@@ -25,8 +25,8 @@ export function setup() {
await app.workbench.editors.waitForActiveTab(readmeMd, true);
await app.workbench.editor.waitForEditorContents(readmeMd, c => c.indexOf(textToType) > -1);
await app.workbench.editors.waitForTab(untitled, true);
await app.workbench.editors.selectTab(untitled, true);
await app.workbench.editors.waitForTab(untitled);
await app.workbench.editors.selectTab(untitled);
await app.workbench.editor.waitForEditorContents(untitled, c => c.indexOf(textToTypeInUntitled) > -1);
});
});

View File

@@ -8,7 +8,6 @@ import { join } from 'path';
export function setup(stableCodePath: string, testDataPath: string) {
describe('Data Migration: This test MUST run before releasing by providing the --stable-build command line argument', () => {
it(`verifies opened editors are restored`, async function () {
if (!stableCodePath) {
@@ -66,7 +65,7 @@ export function setup(stableCodePath: string, testDataPath: string) {
await stableApp.workbench.editors.newUntitledFile();
const untitled = 'Untitled-1';
const textToTypeInUntitled = 'Hello, Untitled Code';
const textToTypeInUntitled = 'Hello from Untitled';
await stableApp.workbench.editor.waitForTypeInEditor(untitled, textToTypeInUntitled);
const readmeMd = 'readme.md';
@@ -86,7 +85,7 @@ export function setup(stableCodePath: string, testDataPath: string) {
await insidersApp.workbench.editor.waitForEditorContents(readmeMd, c => c.indexOf(textToType) > -1);
await insidersApp.workbench.editors.waitForTab(untitled, true);
await insidersApp.workbench.editors.selectTab(untitled, true);
await insidersApp.workbench.editors.selectTab(untitled);
await insidersApp.workbench.editor.waitForEditorContents(untitled, c => c.indexOf(textToTypeInUntitled) > -1);
await insidersApp.stop();

View File

@@ -34,8 +34,6 @@ import { setup as setupDataPreferencesTests } from './areas/preferences/preferen
import { setup as setupDataSearchTests } from './areas/search/search.test';
import { setup as setupDataCSSTests } from './areas/css/css.test';
import { setup as setupDataEditorTests } from './areas/editor/editor.test';
import { setup as setupDataDebugTests } from './areas/debug/debug.test';
import { setup as setupDataGitTests } from './areas/git/git.test';
import { setup as setupDataStatusbarTests } from './areas/statusbar/statusbar.test';
import { setup as setupDataExtensionTests } from './areas/extensions/extensions.test';
import { setup as setupTerminalTests } from './areas/terminal/terminal.test';
@@ -43,8 +41,8 @@ import { setup as setupDataMultirootTests } from './areas/multiroot/multiroot.te
import { setup as setupDataLocalizationTests } from './areas/workbench/localization.test';
import { setup as setupLaunchTests } from './areas/workbench/launch.test';*///{{END}}
if (!/^v10/.test(process.version)) {
console.error('Error: Smoketest must be run using Node 10. Currently running', process.version);
if (!/^v10/.test(process.version) && !/^v12/.test(process.version)) {
console.error('Error: Smoketest must be run using Node 10/12. Currently running', process.version);
process.exit(1);
}
@@ -55,6 +53,7 @@ process.once('exit', () => rimraf.sync(testDataPath));
const [, , ...args] = process.argv;
const opts = minimist(args, {
string: [
'browser',
'build',
'stable-build',
'wait-time',
@@ -66,7 +65,8 @@ const opts = minimist(args, {
'verbose',
'remote',
'web',
'headless'
'headless',
'ci'
],
default: {
verbose: false
@@ -79,7 +79,6 @@ const extensionsPath = path.join(testDataPath, 'extensions-dir');
mkdirp.sync(extensionsPath);
const screenshotsPath = opts.screenshots ? path.resolve(opts.screenshots) : null;
if (screenshotsPath) {
mkdirp.sync(screenshotsPath);
}
@@ -89,83 +88,109 @@ function fail(errorMessage): void {
process.exit(1);
}
if (parseInt(process.version.substr(1)) < 6) {
fail('Please update your Node version to greater than 6 to run the smoke test.');
}
const repoPath = path.join(__dirname, '..', '..', '..');
function getDevElectronPath(): string {
const buildPath = path.join(repoPath, '.build');
const product = require(path.join(repoPath, 'product.json'));
let quality: Quality;
switch (process.platform) {
case 'darwin':
return path.join(buildPath, 'electron', `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron');
case 'linux':
return path.join(buildPath, 'electron', `${product.applicationName}`);
case 'win32':
return path.join(buildPath, 'electron', `${product.nameShort}.exe`);
default:
throw new Error('Unsupported platform.');
}
}
//
// #### Electron Smoke Tests ####
//
if (!opts.web) {
function getBuildElectronPath(root: string): string {
switch (process.platform) {
case 'darwin':
return path.join(root, 'Contents', 'MacOS', 'Electron');
case 'linux': {
const product = require(path.join(root, 'resources', 'app', 'product.json'));
return path.join(root, product.applicationName);
function getDevElectronPath(): string {
const buildPath = path.join(repoPath, '.build');
const product = require(path.join(repoPath, 'product.json'));
switch (process.platform) {
case 'darwin':
return path.join(buildPath, 'electron', `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron');
case 'linux':
return path.join(buildPath, 'electron', `${product.applicationName}`);
case 'win32':
return path.join(buildPath, 'electron', `${product.nameShort}.exe`);
default:
throw new Error('Unsupported platform.');
}
case 'win32': {
const product = require(path.join(root, 'resources', 'app', 'product.json'));
return path.join(root, `${product.nameShort}.exe`);
}
function getBuildElectronPath(root: string): string {
switch (process.platform) {
case 'darwin':
return path.join(root, 'Contents', 'MacOS', 'Electron');
case 'linux': {
const product = require(path.join(root, 'resources', 'app', 'product.json'));
return path.join(root, product.applicationName);
}
case 'win32': {
const product = require(path.join(root, 'resources', 'app', 'product.json'));
return path.join(root, `${product.nameShort}.exe`);
}
default:
throw new Error('Unsupported platform.');
}
default:
throw new Error('Unsupported platform.');
}
let testCodePath = opts.build;
let stableCodePath = opts['stable-build'];
let electronPath: string;
let stablePath: string | undefined = undefined;
if (testCodePath) {
electronPath = getBuildElectronPath(testCodePath);
if (stableCodePath) {
stablePath = getBuildElectronPath(stableCodePath);
}
} else {
testCodePath = getDevElectronPath();
electronPath = testCodePath;
process.env.VSCODE_REPOSITORY = repoPath;
process.env.VSCODE_DEV = '1';
process.env.VSCODE_CLI = '1';
}
if (!fs.existsSync(electronPath || '')) {
fail(`Can't find VSCode at ${electronPath}.`);
}
if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) {
fail(`Can't find Stable VSCode at ${stablePath}.`);
}
if (process.env.VSCODE_DEV === '1') {
quality = Quality.Dev;
} else if (electronPath.indexOf('Code - Insiders') >= 0 /* macOS/Windows */ || electronPath.indexOf('code-insiders') /* Linux */ >= 0) {
quality = Quality.Insiders;
} else {
quality = Quality.Stable;
}
}
let testCodePath = opts.build;
let stableCodePath = opts['stable-build'];
let electronPath: string;
let stablePath: string | undefined = undefined;
//
// #### Web Smoke Tests ####
//
else {
const testCodeServerPath = opts.build || process.env.VSCODE_REMOTE_SERVER_PATH;
if (testCodePath) {
electronPath = getBuildElectronPath(testCodePath);
if (stableCodePath) {
stablePath = getBuildElectronPath(stableCodePath);
if (typeof testCodeServerPath === 'string' && !fs.existsSync(testCodeServerPath)) {
fail(`Can't find Code server at ${testCodeServerPath}.`);
}
} else {
testCodePath = getDevElectronPath();
electronPath = testCodePath;
process.env.VSCODE_REPOSITORY = repoPath;
process.env.VSCODE_DEV = '1';
process.env.VSCODE_CLI = '1';
}
if (!opts.web && !fs.existsSync(electronPath || '')) {
fail(`Can't find Code at ${electronPath}.`);
}
if (!testCodeServerPath) {
process.env.VSCODE_REPOSITORY = repoPath;
process.env.VSCODE_DEV = '1';
process.env.VSCODE_CLI = '1';
}
if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) {
fail(`Can't find Stable Code at ${stablePath}.`);
if (process.env.VSCODE_DEV === '1') {
quality = Quality.Dev;
} else {
quality = Quality.Insiders;
}
}
const userDataDir = path.join(testDataPath, 'd');
let quality: Quality;
if (process.env.VSCODE_DEV === '1') {
quality = Quality.Dev;
} else if (electronPath.indexOf('Code - Insiders') >= 0 /* macOS/Windows */ || electronPath.indexOf('code-insiders') /* Linux */ >= 0) {
quality = Quality.Insiders;
} else {
quality = Quality.Stable;
}
async function setupRepository(): Promise<void> {
if (opts['test-repo']) {
console.log('*** Copying test project repository:', opts['test-repo']);
@@ -228,13 +253,13 @@ function createOptions(): ApplicationOptions {
screenshotsPath,
remote: opts.remote,
web: opts.web,
browser: opts.browser,
headless: opts.headless
};
}
before(async function () {
// allow two minutes for setup
this.timeout(2 * 60 * 1000);
this.timeout(2 * 60 * 1000); // allow two minutes for setup
await setup();
this.defaultOptions = createOptions();
});
@@ -250,12 +275,8 @@ after(async function () {
await new Promise((c, e) => rimraf(testDataPath, { maxBusyTries: 10 }, err => err ? e(err) : c()));
});
/*//{{SQL CARBON EDIT}}
if (!opts.web) {
setupDataMigrationTests(stableCodePath, testDataPath);
}*/
describe('Running Code', () => {
describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => {
before(async function () {
const app = new Application(this.defaultOptions);
await app!.start(opts.web ? false : undefined);
@@ -300,27 +321,29 @@ describe('Running Code', () => {
app.logger.log('*** Test start:', title);
});
}
//{{SQL CARBON EDIT}}
runProfilerTests();
runQueryEditorTests();
/*
if (!opts.web) { setupDataLossTests(); }
setupDataExplorerTests();
if (!opts.web) { setupDataPreferencesTests(); }
setupDataSearchTests();
setupDataCSSTests();
setupDataEditorTests();
if (!opts.web) { setupDataDebugTests(); }
setupDataGitTests();
setupDataStatusbarTests(!!opts.web);
setupDataExtensionTests();
setupTerminalTests();
if (!opts.web) { setupDataMultirootTests(); }
setupDataLocalizationTests();
*/
//{{END}}
// CI only tests (must be reliable)
if (opts.ci) {
// TODO@Ben figure out tests that can run continously and reliably
}
// Non-CI execution (all tests)
else {
/*if (!opts.web) { setupDataMigrationTests(opts['stable-build'], testDataPath); } {{SQL CARBON EDIT}} comment out tests
if (!opts.web) { setupDataLossTests(); }
setupDataExplorerTests();
if (!opts.web) { setupDataPreferencesTests(); }
setupDataSearchTests();
setupDataCSSTests();
setupDataEditorTests();
setupDataStatusbarTests(!!opts.web);
if (!opts.web) { setupDataExtensionTests(); }
setupTerminalTests();
if (!opts.web) { setupDataMultirootTests(); }
if (!opts.web) { setupDataLocalizationTests(); }
if (!opts.web) { setupLaunchTests(); }*/
runProfilerTests(); // {{SQL CARBON EDIT}} add our tests
runQueryEditorTests(); // {{SQL CARBON EDIT}} add our tests
}
});
/*//{{SQL CARBON EDIT}}
if (!opts.web) {
setupLaunchTests();
}*/

View File

@@ -11,18 +11,14 @@ const suite = 'Smoke Tests';
const [, , ...args] = process.argv;
const opts = minimist(args, {
string: [
'f'
]
string: ['f', 'g']
});
const options = {
useColors: true,
//{{SQL CARBON EDIT}}
timeout: 60000 * 2,
//{{END}}
color: true,
timeout: 60000,
slow: 30000,
grep: opts['f']
grep: opts['f'] || opts['g']
};
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
@@ -38,4 +34,4 @@ if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
const mocha = new Mocha(options);
mocha.addFile('out/main.js');
mocha.run(failures => process.exit(failures ? -1 : 0));
mocha.run(failures => process.exit(failures ? -1 : 0));

View File

@@ -2122,10 +2122,10 @@ tree-kill@^1.1.0:
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36"
integrity sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==
typescript@2.9.2:
version "2.9.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
typescript@3.7.5:
version "3.7.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
union-value@^1.0.0:
version "1.0.1"

49
test/unit/README.md Normal file
View File

@@ -0,0 +1,49 @@
# Unit Tests
## Run (inside Electron)
The best way to run the unit tests is from the terminal. To make development changes to unit tests you need to be running `yarn watch`. See [Development Workflow](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#incremental-build) for more details. From the `vscode` folder run:
**OS X and Linux**
./scripts/test.sh
**Windows**
scripts\test
## Run (inside browser)
You can run tests inside a browser instance too:
**OS X and Linux**
node test/unit/browser/index.js
**Windows**
node test\unit\browser\index.js
## Run (with node)
yarn run mocha --run src/vs/editor/test/browser/controller/cursor.test.ts
## Debug
To debug tests use `--debug` when running the test script. Also, the set of tests can be reduced with the `--run` and `--runGlob` flags. Both require a file path/pattern. Like so:
./scripts/test.sh --debug --runGrep **/extHost*.test.js
## Coverage
The following command will create a `coverage` folder at the root of the workspace:
**OS X and Linux**
./scripts/test.sh --coverage
**Windows**
scripts\test --coverage

View File

@@ -273,25 +273,24 @@ assert.notEqual = function notEqual(actual, expected, message) {
// assert.deepEqual(actual, expected, message_opt);
assert.deepEqual = function deepEqual(actual, expected, message) {
if (!_deepEqual(actual, expected)) {
if (!_deepEqual(actual, expected, false)) {
fail(actual, expected, message, 'deepEqual', assert.deepEqual);
}
};
function _deepEqual(actual, expected) {
assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
if (!_deepEqual(actual, expected, true)) {
fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual);
}
};
function _deepEqual(actual, expected, strict) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
// } else if (actual instanceof Buffer && expected instanceof Buffer) {
// return compare(actual, expected) === 0;
// } else if (util.isBuffer(actual) && util.isBuffer(expected)) {
// if (actual.length != expected.length) return false;
//
// for (var i = 0; i < actual.length; i++) {
// if (actual[i] !== expected[i]) return false;
// }
//
// return true;
//
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (util.isDate(actual) && util.isDate(expected)) {
@@ -309,8 +308,9 @@ function _deepEqual(actual, expected) {
// 7.4. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (!util.isObject(actual) && !util.isObject(expected)) {
return actual == expected;
} else if ((actual === null || typeof actual !== 'object') &&
(expected === null || typeof expected !== 'object')) {
return strict ? actual === expected : actual == expected;
// 7.5 For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
@@ -319,32 +319,22 @@ function _deepEqual(actual, expected) {
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected);
return objEquiv(actual, expected, strict);
}
}
var isArguments = function(object) {
function isArguments(object) {
return Object.prototype.toString.call(object) == '[object Arguments]';
};
}
(function() {
if (!isArguments(arguments)) {
isArguments = function(object) {
return object != null &&
typeof object === 'object' &&
typeof object.callee === 'function' &&
typeof object.length === 'number' || false;
};
}
})();
function objEquiv(a, b) {
if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b))
function objEquiv(a, b, strict) {
if (a === null || a === undefined || b === null || b === undefined)
return false;
// if one is a primitive, the other must be same
if (util.isPrimitive(a) || util.isPrimitive(b))
return a === b;
if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
return false;
// an identical 'prototype' property.
if (a.prototype !== b.prototype) return false;
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
var aIsArgs = isArguments(a),
bIsArgs = isArguments(b);
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
@@ -352,32 +342,28 @@ function objEquiv(a, b) {
if (aIsArgs) {
a = pSlice.call(a);
b = pSlice.call(b);
return _deepEqual(a, b);
}
try {
var ka = Object_keys(a),
kb = Object_keys(b),
key, i;
} catch (e) {//happens when one is a string literal and the other isn't
return false;
return _deepEqual(a, b, strict);
}
var ka = Object.keys(a),
kb = Object.keys(b),
key, i;
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length != kb.length)
if (ka.length !== kb.length)
return false;
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i])
if (ka[i] !== kb[i])
return false;
}
//equivalent values for every corresponding key, and
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!_deepEqual(a[key], b[key])) return false;
if (!_deepEqual(a[key], b[key], strict)) return false;
}
return true;
}
@@ -386,11 +372,19 @@ function objEquiv(a, b) {
// assert.notDeepEqual(actual, expected, message_opt);
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
if (_deepEqual(actual, expected)) {
if (_deepEqual(actual, expected, false)) {
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
}
};
assert.notDeepStrictEqual = notDeepStrictEqual;
function notDeepStrictEqual(actual, expected, message) {
if (_deepEqual(actual, expected, true)) {
fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual);
}
}
// 9. The strict equality assertion tests strict equality, as determined by ===.
// assert.strictEqual(actual, expected, message_opt);
@@ -428,7 +422,11 @@ function expectedException(actual, expected) {
function _throws(shouldThrow, block, expected, message) {
var actual;
if (util.isString(expected)) {
if (typeof block !== 'function') {
throw new TypeError('block must be a function');
}
if (typeof expected === 'string') {
message = expected;
expected = null;
}
@@ -469,6 +467,5 @@ assert.doesNotThrow = function(block, /*optional*/message) {
};
assert.ifError = function(err) { if (err) {throw err;}};
return assert;
});

244
test/unit/browser/index.js Normal file
View File

@@ -0,0 +1,244 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
const path = require('path');
const glob = require('glob');
const fs = require('fs');
const events = require('events');
const mocha = require('mocha');
const url = require('url');
const minimatch = require('minimatch');
const playwright = require('playwright');
// opts
const defaultReporterName = process.platform === 'win32' ? 'list' : 'spec';
const optimist = require('optimist')
// .describe('grep', 'only run tests matching <pattern>').alias('grep', 'g').alias('grep', 'f').string('grep')
// .describe('build', 'run with build output (out-build)').boolean('build')
.describe('run', 'only run tests matching <relative_file_path>').string('run')
.describe('glob', 'only run tests matching <glob_pattern>').string('glob')
.describe('debug', 'do not run browsers headless').boolean('debug')
.describe('browser', 'browsers in which tests should run').string('browser').default('browser', ['chromium'])
.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', 'tfs').string('tfs')
.describe('help', 'show the help').alias('help', 'h');
// logic
const argv = optimist.argv;
if (argv.help) {
optimist.showHelp();
process.exit(0);
}
const withReporter = (function () {
const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter);
let ctor;
try {
ctor = require(reporterPath);
} catch (err) {
try {
ctor = require(argv.reporter);
} catch (err) {
ctor = process.platform === 'win32' ? mocha.reporters.List : mocha.reporters.Spec;
console.warn(`could not load reporter: ${argv.reporter}, using ${ctor.name}`);
}
}
function parseReporterOption(value) {
let r = /^([^=]+)=(.*)$/.exec(value);
return r ? { [r[1]]: r[2] } : {};
}
let reporterOptions = argv['reporter-options'];
reporterOptions = typeof reporterOptions === 'string' ? [reporterOptions] : reporterOptions;
reporterOptions = reporterOptions.reduce((r, o) => Object.assign(r, parseReporterOption(o)), {});
return (runner) => new ctor(runner, { reporterOptions })
})()
const outdir = argv.build ? 'out-build' : 'out';
const out = path.join(__dirname, `../../../${outdir}`);
function ensureIsArray(a) {
return Array.isArray(a) ? a : [a];
}
const testModules = (async function () {
const excludeGlob = '**/{node,electron-browser,electron-main}/**/*.test.js';
let isDefaultModules = true;
let promise;
if (argv.run) {
// use file list (--run)
isDefaultModules = false;
promise = Promise.resolve(ensureIsArray(argv.run).map(file => {
file = file.replace(/^src/, 'out');
file = file.replace(/\.ts$/, '.js');
return path.relative(out, file);
}));
} else {
// glob patterns (--glob)
const defaultGlob = '**/*.test.js';
const pattern = argv.glob || defaultGlob
isDefaultModules = pattern === defaultGlob;
promise = new Promise((resolve, reject) => {
glob(pattern, { cwd: out }, (err, files) => {
if (err) {
reject(err);
} else {
resolve(files)
}
});
});
}
return promise.then(files => {
const modules = [];
for (let file of files) {
if (!minimatch(file, excludeGlob)) {
modules.push(file.replace(/\.js$/, ''));
} else if (!isDefaultModules) {
console.warn(`DROPPONG ${file} because it cannot be run inside a browser`);
}
}
return modules;
})
})();
async function runTestsInBrowser(testModules, browserType) {
const browser = await playwright[browserType].launch({ headless: !Boolean(argv.debug) });
const page = (await browser.defaultContext().pages())[0]
const target = url.pathToFileURL(path.join(__dirname, 'renderer.html'));
await page.goto(target.href);
const emitter = new events.EventEmitter();
await page.exposeFunction('mocha_report', (type, data1, data2) => {
emitter.emit(type, data1, data2)
});
page.on('console', async msg => {
console[msg.type()](msg.text(), await Promise.all(msg.args().map(async arg => await arg.jsonValue())));
});
withReporter(new EchoRunner(emitter, browserType.toUpperCase()));
// collection failures for console printing
const fails = [];
emitter.on('fail', (test, err) => {
if (err.stack) {
const regex = /(vs\/.*\.test)\.js/;
for (let line of String(err.stack).split('\n')) {
const match = regex.exec(line);
if (match) {
fails.push(match[1]);
break;
}
}
}
});
try {
// @ts-ignore
await page.evaluate(modules => loadAndRun(modules), testModules);
} catch (err) {
console.error(err);
}
await browser.close();
if (fails.length > 0) {
return `to DEBUG, open ${browserType.toUpperCase()} and navigate to ${target.href}?${fails.map(module => `m=${module}`).join('&')}`;
}
}
class EchoRunner extends events.EventEmitter {
constructor(event, title = '') {
super();
event.on('start', () => this.emit('start'));
event.on('end', () => this.emit('end'));
event.on('suite', (suite) => this.emit('suite', EchoRunner.deserializeSuite(suite, title)));
event.on('suite end', (suite) => this.emit('suite end', EchoRunner.deserializeSuite(suite, title)));
event.on('test', (test) => this.emit('test', EchoRunner.deserializeRunnable(test)));
event.on('test end', (test) => this.emit('test end', EchoRunner.deserializeRunnable(test)));
event.on('hook', (hook) => this.emit('hook', EchoRunner.deserializeRunnable(hook)));
event.on('hook end', (hook) => this.emit('hook end', EchoRunner.deserializeRunnable(hook)));
event.on('pass', (test) => this.emit('pass', EchoRunner.deserializeRunnable(test)));
event.on('fail', (test, err) => this.emit('fail', EchoRunner.deserializeRunnable(test, title), EchoRunner.deserializeError(err)));
event.on('pending', (test) => this.emit('pending', EchoRunner.deserializeRunnable(test)));
}
static deserializeSuite(suite, titleExtra) {
return {
root: suite.root,
suites: suite.suites,
tests: suite.tests,
title: titleExtra && suite.title ? `${suite.title} - /${titleExtra}/` : suite.title,
fullTitle: () => suite.fullTitle,
timeout: () => suite.timeout,
retries: () => suite.retries,
enableTimeouts: () => suite.enableTimeouts,
slow: () => suite.slow,
bail: () => suite.bail
};
}
static deserializeRunnable(runnable, titleExtra) {
return {
title: runnable.title,
fullTitle: () => titleExtra && runnable.fullTitle ? `${runnable.fullTitle} - /${titleExtra}/` : runnable.fullTitle,
async: runnable.async,
slow: () => runnable.slow,
speed: runnable.speed,
duration: runnable.duration
};
}
static deserializeError(err) {
const inspect = err.inspect;
err.inspect = () => inspect;
return err;
}
}
testModules.then(async modules => {
// run tests in selected browsers
const browserTypes = Array.isArray(argv.browser)
? argv.browser : [argv.browser];
const promises = browserTypes.map(async browserType => {
try {
return await runTestsInBrowser(modules, browserType);
} catch (err) {
console.error(err);
process.exit(1);
}
});
// aftermath
let didFail = false;
const messages = await Promise.all(promises);
for (let msg of messages) {
if (msg) {
didFail = true;
console.log(msg);
}
}
process.exit(didFail ? 1 : 0);
}).catch(err => {
console.error(err);
});

View File

@@ -0,0 +1,140 @@
<html>
<head>
<meta charset="utf-8">
<title>VSCode Tests</title>
<link href="../../../node_modules/mocha/mocha.css" rel="stylesheet" />
</head>
<body>
<div id="mocha"></div>
<script src="../../../node_modules/mocha/mocha.js"></script>
<script>
// !!! DO NOT CHANGE !!!
// Our unit tests may run in environments without
// display (e.g. from builds) and tests may by
// accident bring up native dialogs or even open
// windows. This we cannot allow as it may crash
// the test run.
// !!! DO NOT CHANGE !!!
window.open = function () { throw new Error('window.open() is not supported in tests!'); };
window.alert = function () { throw new Error('window.alert() is not supported in tests!'); }
window.confirm = function () { throw new Error('window.confirm() is not supported in tests!'); }
// Ignore uncaught cancelled promise errors
window.addEventListener('unhandledrejection', e => {
const name = e && e.reason && e.reason.name;
if (name === 'Canceled') {
e.preventDefault();
e.stopPropagation();
}
});
mocha.setup({
ui: 'tdd',
timeout: 5000
});
</script>
<script src="../../../out/vs/loader.js"></script>
<script>
// configure loader
const baseUrl = window.location.href;
require.config({
catchError: true,
baseUrl: new URL('../../../src', baseUrl).href,
paths: {
'vs': new URL('../../../out/vs', baseUrl).href,
assert: new URL('../assert.js', baseUrl).href,
sinon: new URL('../../../node_modules/sinon/pkg/sinon-1.17.7.js', baseUrl).href
}
});
</script>
<script>
function serializeSuite(suite) {
return {
root: suite.root,
suites: suite.suites.map(serializeSuite),
tests: suite.tests.map(serializeRunnable),
title: suite.title,
fullTitle: suite.fullTitle(),
timeout: suite.timeout(),
retries: suite.retries(),
enableTimeouts: suite.enableTimeouts(),
slow: suite.slow(),
bail: suite.bail()
};
}
function serializeRunnable(runnable) {
return {
title: runnable.title,
fullTitle: runnable.fullTitle(),
async: runnable.async,
slow: runnable.slow(),
speed: runnable.speed,
duration: runnable.duration
};
}
function serializeError(err) {
return {
message: err.message,
stack: err.stack,
actual: err.actual,
expected: err.expected,
uncaught: err.uncaught,
showDiff: err.showDiff,
inspect: typeof err.inspect === 'function' ? err.inspect() : ''
};
}
function PlaywrightReporter(runner) {
runner.on('start', () => window.mocha_report('start'));
runner.on('end', () => window.mocha_report('end'));
runner.on('suite', suite => window.mocha_report('suite', serializeSuite(suite)));
runner.on('suite end', suite => window.mocha_report('suite end', serializeSuite(suite)));
runner.on('test', test => window.mocha_report('test', serializeRunnable(test)));
runner.on('test end', test => window.mocha_report('test end', serializeRunnable(test)));
runner.on('hook', hook => window.mocha_report('hook', serializeRunnable(hook)));
runner.on('hook end', hook => window.mocha_report('hook end', serializeRunnable(hook)));
runner.on('pass', test => window.mocha_report('pass', serializeRunnable(test)));
runner.on('fail', (test, err) => window.mocha_report('fail', serializeRunnable(test), serializeError(err)));
runner.on('pending', test => window.mocha_report('pending', serializeRunnable(test)));
};
window.loadAndRun = async function loadAndRun(modules, manual = false) {
// load
// await Promise.all(modules.map(module => new Promise((resolve, reject) =>{
// require([module], resolve, err => {
// console.log("BAD " + module + JSON.stringify(err, undefined, '\t'));
// // console.log(module);
// resolve({});
// });
// })));
await new Promise((resolve, reject) => {
require(modules, resolve, err => {
console.log(err);
reject(err);
});
});
// run
return new Promise((resolve, reject) => {
if (!manual) {
mocha.reporter(PlaywrightReporter);
}
mocha.run(failCount => resolve(failCount === 0));
});
}
const modules = new URL(window.location.href).searchParams.getAll('m');
if (Array.isArray(modules) && modules.length > 0) {
console.log('MANUALLY running tests', modules);
loadAndRun(modules, true).then(() => console.log('done'), err => console.log(err));
}
</script>
</body>
</html>

View File

@@ -12,7 +12,7 @@ const iLibSourceMaps = require('istanbul-lib-source-maps');
const iLibReport = require('istanbul-lib-report');
const iReports = require('istanbul-reports');
const REPO_PATH = toUpperDriveLetter(path.join(__dirname, '..'));
const REPO_PATH = toUpperDriveLetter(path.join(__dirname, '../../'));
exports.initialize = function (loaderConfig) {
const instrumenter = iLibInstrument.createInstrumenter();
@@ -48,7 +48,7 @@ exports.createReport = function (isSingle) {
transformed.data = newData;
const context = iLibReport.createContext({
dir: path.join(__dirname, `../.build/coverage${isSingle ? '-single' : ''}`),
dir: path.join(REPO_PATH, `.build/coverage${isSingle ? '-single' : ''}`),
coverageMap: transformed
});
const tree = context.getTree('flat');

View File

@@ -18,7 +18,7 @@ const optimist = require('optimist')
.describe('grep', 'only run tests matching <pattern>').alias('grep', 'g').alias('grep', 'f').string('grep')
.describe('invert', 'uses the inverse of the match specified by grep').alias('invert', 'i').string('invert')
.describe('run', 'only run tests from <file>').string('run')
.describe('runGlob', 'only run tests matching <file_pattern>').alias('runGlob', 'runGrep').string('runGlob')
.describe('runGlob', 'only run tests matching <file_pattern>').alias('runGlob', 'glob').alias('runGlob', 'runGrep').string('runGlob')
.describe('build', 'run with build output (out-build)').boolean('build')
.describe('coverage', 'generate coverage report').boolean('coverage')
.describe('debug', 'open dev tools, keep window open, reuse app data').string('debug')

View File

@@ -3,12 +3,12 @@
<head>
<meta charset="utf-8">
<title>VSCode Tests</title>
<link href="../../node_modules/mocha/mocha.css" rel="stylesheet" />
<link href="../../../node_modules/mocha/mocha.css" rel="stylesheet" />
</head>
<body>
<div id="mocha"></div>
<script src="../../node_modules/mocha/mocha.js"></script>
<script src="../../../node_modules/mocha/mocha.js"></script>
<script>

View File

@@ -10,7 +10,7 @@ const assert = require('assert');
const path = require('path');
const glob = require('glob');
const util = require('util');
const bootstrap = require('../../src/bootstrap');
const bootstrap = require('../../../src/bootstrap');
const coverage = require('../coverage');
require('reflect-metadata'); // {{SQL CARBON EDIT}}
@@ -26,7 +26,7 @@ let _out;
function initLoader(opts) {
let outdir = opts.build ? 'out-build' : 'out';
_out = path.join(__dirname, `../../${outdir}`);
_out = path.join(__dirname, `../../../${outdir}`);
// setup loader
loader = require(`${_out}/vs/loader`);
@@ -34,7 +34,7 @@ function initLoader(opts) {
nodeRequire: require,
nodeMain: __filename,
catchError: true,
baseUrl: bootstrap.uriFromPath(path.join(__dirname, '../../src')),
baseUrl: bootstrap.uriFromPath(path.join(__dirname, '../../../src')),
paths: {
'vs': `../${outdir}/vs`,
'sql': `../${outdir}/sql`, // {{SQL CARBON EDIT}}

View File

@@ -11,42 +11,40 @@ const path = require('path');
const glob = require('glob');
const jsdom = require('jsdom-no-contextify');
const TEST_GLOB = '**/test/**/*.test.js';
const coverage = require('./coverage');
const coverage = require('../coverage');
require('reflect-metadata'); // {{SQL CARBON EDIT}}
var optimist = require('optimist')
const optimist = require('optimist')
.usage('Run the Code tests. All mocha options apply.')
.describe('build', 'Run from out-build').boolean('build')
.describe('run', 'Run a single file').string('run')
.describe('coverage', 'Generate a coverage report').boolean('coverage')
.describe('only-monaco-editor', 'Run only monaco editor tests').boolean('only-monaco-editor')
.describe('browser', 'Run tests in a browser').boolean('browser')
.alias('h', 'help').boolean('h')
.describe('h', 'Show help');
var argv = optimist.argv;
const argv = optimist.argv;
if (argv.help) {
optimist.showHelp();
process.exit(1);
}
var out = argv.build ? 'out-build' : 'out';
var loader = require('../' + out + '/vs/loader');
var src = path.join(path.dirname(__dirname), out);
const REPO_ROOT = path.join(__dirname, '../../../');
const out = argv.build ? 'out-build' : 'out';
const loader = require(`../../../${out}/vs/loader`);
const src = path.join(REPO_ROOT, out);
function main() {
process.on('uncaughtException', function (e) {
console.error(e.stack || e);
});
var loaderConfig = {
const loaderConfig = {
nodeRequire: require,
nodeMain: __filename,
baseUrl: path.join(path.dirname(__dirname), 'src'),
baseUrl: path.join(REPO_ROOT, 'src'),
paths: {
'vs/css': '../test/css.mock',
'vs/css': '../test/unit/node/css.mock',
'vs': `../${out}/vs`,
'sql': `../${out}/sql`, // {{SQL CARBON EDIT}}
'lib': `../${out}/lib`,
@@ -93,17 +91,17 @@ function main() {
global.navigator = global.window.navigator;
global.XMLHttpRequest = global.window.XMLHttpRequest;
var didErr = false;
var write = process.stderr.write;
let didErr = false;
const write = process.stderr.write;
process.stderr.write = function (data) {
didErr = didErr || !!data;
write.apply(process.stderr, arguments);
};
var loadFunc = null;
let loadFunc = null;
if (argv.runGlob) {
loadFunc = cb => {
loadFunc = (cb) => {
const doRun = tests => {
const modulesToLoad = tests.map(test => {
if (path.isAbsolute(test)) {
@@ -118,41 +116,19 @@ function main() {
glob(argv.runGlob, { cwd: src }, function (err, files) { doRun(files); });
};
} else if (argv.run) {
var tests = (typeof argv.run === 'string') ? [argv.run] : argv.run;
var modulesToLoad = tests.map(function (test) {
const tests = (typeof argv.run === 'string') ? [argv.run] : argv.run;
const modulesToLoad = tests.map(function (test) {
test = test.replace(/^src/, 'out');
test = test.replace(/\.ts$/, '.js');
return path.relative(src, path.resolve(test)).replace(/(\.js)|(\.js\.map)$/, '').replace(/\\/g, '/');
});
loadFunc = cb => {
loadFunc = (cb) => {
define(modulesToLoad, () => cb(null), cb);
};
} else if (argv['only-monaco-editor']) {
loadFunc = function (cb) {
glob(TEST_GLOB, { cwd: src }, function (err, files) {
var modulesToLoad = files.map(function (file) {
return file.replace(/\.js$/, '');
});
modulesToLoad = modulesToLoad.filter(function (module) {
if (/^vs\/workbench\//.test(module)) {
return false;
}
// platform tests drag in the workbench.
// see https://github.com/Microsoft/vscode/commit/12eaba2f64c69247de105c3d9c47308ac6e44bc9
// and cry a little
if (/^vs\/platform\//.test(module)) {
return false;
}
return !/(\/|\\)node(\/|\\)/.test(module);
});
console.log(JSON.stringify(modulesToLoad, null, '\t'));
define(modulesToLoad, function () { cb(null); }, cb);
});
};
} else {
loadFunc = function (cb) {
loadFunc = (cb) => {
glob(TEST_GLOB, { cwd: src }, function (err, files) {
var modulesToLoad = files.map(function (file) {
const modulesToLoad = files.map(function (file) {
return file.replace(/\.js$/, '');
});
define(modulesToLoad, function () { cb(null); }, cb);
@@ -178,7 +154,7 @@ function main() {
}
// report failing test for every unexpected error during any of the tests
var unexpectedErrors = [];
let unexpectedErrors = [];
suite('Errors', function () {
test('should not have unexpected errors in tests', function () {
if (unexpectedErrors.length) {
@@ -199,7 +175,7 @@ function main() {
event.preventDefault();
});
errors.setUnexpectedErrorHandler(function (err) {
let stack = (err && err.stack) || (new Error().stack);
const stack = (err && err.stack) || (new Error().stack);
unexpectedErrors.push((err && err.message ? err.message : err) + '\n' + stack);
});

49
test/unit/node/browser.js Normal file
View File

@@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const yaserver = require('yaserver');
const http = require('http');
const glob = require('glob');
const path = require('path');
const fs = require('fs');
const REPO_ROOT = path.join(__dirname, '../../../');
const PORT = 8887;
function template(str, env) {
return str.replace(/{{\s*([\w_\-]+)\s*}}/g, function (all, part) {
return env[part];
});
}
yaserver.createServer({ rootDir: REPO_ROOT }).then((staticServer) => {
const server = http.createServer((req, res) => {
if (req.url === '' || req.url === '/') {
glob('**/vs/{base,platform,editor}/**/test/{common,browser}/**/*.test.js', {
cwd: path.join(REPO_ROOT, 'out'),
// ignore: ['**/test/{node,electron*}/**/*.js']
}, function (err, files) {
if (err) { console.log(err); process.exit(0); }
var modules = files
.map(function (file) { return file.replace(/\.js$/, ''); });
fs.readFile(path.join(__dirname, 'index.html'), 'utf8', function (err, templateString) {
if (err) { console.log(err); process.exit(0); }
res.end(template(templateString, {
modules: JSON.stringify(modules)
}));
});
});
} else {
return staticServer.handle(req, res);
}
});
server.listen(PORT, () => {
console.log(`http://localhost:${PORT}/`);
});
});

View File

@@ -16,7 +16,7 @@
require.config({
baseUrl: '/out',
paths: {
assert: '/test/assert.js',
assert: '/test/unit/assert.js',
sinon: '/node_modules/sinon/pkg/sinon-1.17.7.js'
}
});
@@ -26,4 +26,4 @@
});
</script>
</body>
</html>
</html>