Merge vscode source through release 1.79.2 (#23482)

* log when an editor action doesn't run because of enablement

* notebooks create/dispose editors. this means controllers must be created eagerly (😢) and that notebooks need a custom way of plugging comparision keys for session. works unless creating another session for the same cell of a duplicated editor

* Set offSide to sql lang configuration to true (#183461)

* Fixes #181764 (#183550)

* fix typo

* Always scroll down and focus the input (#183557)

* Fixes #180386 (#183561)

* cli: ensure ordering of rpc server messages (#183558)

* cli: ensure ordering of rpc server messages

Sending lots of messages to a stream would block them around the async
tokio mutex, which is "fair" so doesn't preserve ordering. Instead, use
the write_loop approach I introduced to the server_multiplexer for the
same reason some time ago.

* fix clippy

* update for May endgame

* testing: allow invalidateTestResults to take an array (#183569)

* Document `ShareProvider` API proposal (#183568)

* Document `ShareProvider` API proposal

* Remove mention of VS Code from JSDoc

* Add support for rendering svg and md in welcome message (#183580)

* Remove toggle setting more eagerly (#183584)

* rm message abt macOS

* Change text (#183589)

* Change text

* Accidentally changed the wrong file

* cli: improve output for code tunnel status (#183571)

* testing: allow invalidateTestResults to take an array

* cli: improve output for code tunnel status

Fixes #183570

* [json/css/html] update services (#183595)

* Add experimental setting to enable this dialog

* Fix exporting chat model to JSON before it is initialized (#183597)

* minimum scrolling to reveal the next cell on shift+enter (#183600)

do minimum scrolling to reveal the next cell on Execute cell and select next

* Fixing Jupyter notebook issue 13263 (#183527)

fix for the issue, still need to understand why there is strange focusing

* Tweak proposed API JSDoc (#183590)

* Tweak proposed API JSDoc

* workbench -> workspace

* fix ? operator

* Use active editor and show progress when sharing (#183603)

Use active editor and show progress

* use scroll setting variable correctly

* Schedule welcome widget to show once between typing. (#183606)

* Schedule dialog to show once between typing

* Don't re-render if already displayed once

* Add F10 keybinding for debugger step, even on Web. (#183510)

Fixes #181792.
Previously, for Web the keyboard shortcut was Alt-F10, because it was
believed that F10 could not be bound on browsers. This turned out to be
incorrect, so we make the shortcut consistent (F10) with desktop VSCode
which is also what many other debuggers use.
We keep Alt-F10 on web as a secondary keybinding to keep the experience
some web users may have gotten used to by now.

* Also pass process.env

* Restore missing chat clear commands (#183651)

* chore: update electron@22.5.4 (#183716)

* Show remote indicator in web when remoteAuthority is set (#183728)

* feat: .vuerc as json file (#153017)

Co-authored-by: Martin Aeschlimann <martinae@microsoft.com>

* Delete --compatibility=1.63 code from the server (#183738)

* Copy vscode.dev link to tunnel generates an invalid link when an untitled workspace is open (#183739)

* Recent devcontainer display string corrupted on Get Started page (#183740)

* Improve "next codeblock" navigation (#183744)

* Improve "next codeblock" navigation
Operate on the current focused response, or the last one, and scroll to the selected item

* Normalize command title

* Git - run git status if similarityThreshold changes (#183762)

* fix aria-label issue in kb editor

fixes A11y_GradeB_VSCode_Keyboard shortcut reads words together - Blind: Arrow key navigation to row Find the binding keys and  "when" cell data are read together resulting in a word " CTRL + FeditorFocus  instead of CTRL + F editorFocus" #182490

* Status - fix compact padding (#183768)

* Remove angle brackets from VB brackets (#183782)

Fixes #183359

* Update language config schema with more details about brackets. (#183779)

* fix comment (#183812)

* Support for `Notebook` CodeAction Kind (#183457)

* nb kind support -- wip

* allow notebook codeactions around single cell edit check

* move notebook code action type out of editor

---------

Co-authored-by: rebornix <penn.lv@gmail.com>

* cli: fix connection default being applied (#183827)

* cli: bump to openssl 1.1.1u (#183828)

* Implement "delete" action for chat history (#183609)

* Use desired file name when generating new md pasted file paths (#183861)

Fixes #183851

* Default to filename for markdown new file if empty (#183864)

Fixes #183848

* Fix small typo (#183865)

Fixes #183819

* Noop when moving a symbol into the file it is already in (#183866)

Fixes #183793

* Adjust codeAction validation to account for notebook kind (#183859)

* Make JS/TS `go to configuration` commands work on non-`file:` file systems (#183688)

Make `go to project` commands work on non-`file:` file systems

Fixes #183685

* Can't do regex search after opening notebook (#183884)

Fixes #183858

* Default to current dir for `move to file` select (#183875)

Fixes #183870

`showOpenDialog` seems to ignore `defaultUri` if the file doesn't exist

* Use `<...>` style markdown links when needed (#183876)

Fixes #183849

* Remove check for context keys

* Update xterm package

* Enable updating a chat model without triggering incremental typing (#183894)

* Enable chat "move" commands on empty sessions (#183895)

* Enable chat "move" commands on empty sessions
and also imported sessions

* Fix command name

* Fix some chat keybindings on windows (#183896)

* "Revert File" on inactive editors are ignored (fix #177557) (#183903)

* Empty reason while switching profile (fix #183775) (#183904)

* fix https://github.com/microsoft/vscode-internalbacklog/issues/4278 (#183910)

* fix https://github.com/microsoft/vscode/issues/183770 (#183914)

* code --status displays a lot of errors before actual status output (fix #183787) (#183915)

* joh/icy manatee (#183917)

* Use idle value for widget of interactive editor controller

https://github.com/microsoft/vscode/issues/183820

* also make preview editors idle values

https://github.com/microsoft/vscode/issues/183820

* Fix #183777 (#183929)

* Fix #182309 (#183925)

* Tree checkbox item -> items (#183931)

Fixes #183826

* Fixes #183909 (#183940)

* Fix #183837 (#183943)

fix #183837

* Git - fix #183941 (#183944)

* Update xterm.css

Fixes #181242

* chore: add @ulugbekna and @aiday-mar to my-endgame notebook (#183946)

* Revert "When snippet mode is active, make `Tab` not accept suggestion but advance placeholder"

This reverts commit 50a80cdb61511343996ff1d41d0b676c3d329f48.

* revert not focusing completion list when quick suggest happens during snippet

* change `snippetsPreventQuickSuggestions` default to false

* Fix #181446 (#183956)

* fix https://github.com/microsoft/vscode-internalbacklog/issues/4298 (#183957)

* fix: remove extraneous incorrect context keys (#183959)

These were actually getting added in getTestItemContextOverlay, and the test ID was using the extended ID which extensions do not know about.

Fixes #183612

* Fixes https://github.com/microsoft/monaco-editor/issues/3920 (#183960)

* fix https://github.com/microsoft/vscode-internalbacklog/issues/4324 (#183961)

* fix #183030

* fix #180826 (#183962)

* make message more generic for interactive editor help

* .

* fix #183968

* Keep codeblock toolbar visible when focused

* Fix when clause on "Run in terminal" command

* add important info to help menu

* fix #183970

* Set `isRefactoring` for all TS refactoring edits (#183982)

* consolidate

* Disable move to file in TS versions < 5.2 (#183992)

There are still a few key bugs with refactoring. We will  ship this as a preview for TS 5.2+ instead of for 5.1

* Polish query accepting (#183995)

We shouldn't send the same request to Copilot if the query hasn't changed. So if the query is the same, we short circut.

Fixes https://github.com/microsoft/vscode-internalbacklog/issues/4286

Also, when we open in chat, we should use the last accepted query, not what's in the input box.

Fixes https://github.com/microsoft/vscode-internalbacklog/issues/4280

* Allow widget to have focus (#184000)

So that selecting non-code text works.

Fixes https://github.com/microsoft/vscode-internalbacklog/issues/4294

* Fix microsoft/vscode-internalbacklog#4257. Mitigate zindex for zone widgets. (#184001)

* Change welcome dialog contribution to Eventually

* Misc fixes

* Workspace folder picker entry descriptions are suboptimal for some filesystems (fix #183418) (#184018)

* cli - ignore std error unless verbose (#183787) (#184031)

* joh/inquisitive meerkat (#184034)

* only stash sessions that are none empty

https://github.com/microsoft/vscode-internalbacklog/issues/4281

* only unstash a session once - unless new exchanges are made,

https://github.com/microsoft/vscode-internalbacklog/issues/4281

* account for all exchange types

* Improve declared components (#184039)

* make sure to read setting (#184040)

d'oh, related to https://github.com/microsoft/vscode/issues/173387#issuecomment-1571696644

* [html] update service (#184049)

[html] update service. FIxes #181176

* reset context keys on reset/hide (#184042)

fixes https://github.com/microsoft/vscode-internalbacklog/issues/4330

* use `Lazy`, not `IdleValue` for the IE widget held by the eager controller (#184048)

https://github.com/microsoft/vscode/issues/183820

* fix https://github.com/microsoft/vscode-internalbacklog/issues/4333 (#184067)

* use undo-loop instead of undo-edit when discarding chat session (#184063)

* use undo-loop instead of undo-edit when discarding chat session

fixes https://github.com/microsoft/vscode-internalbacklog/issues/4118

* fix tests, wait for correct state

* Add logging to node download (#184070)

Add logging to node download. For #182951

* re-enable default zone widget revealing when showing (#184072)

fixes https://github.com/microsoft/vscode-internalbacklog/issues/4332, also fixes https://github.com/microsoft/vscode-internalbacklog/issues/3784

* fix #178202

* Allow APIs in stable (#184062)

* Fix microsoft/vscode-internalbacklog#4206. Override List view whitespace css for monaco editor (#184087)

* Fix JSDoc grammatical error (#184090)

* Pick up TS 5.1.3 (#184091)

Fixes #182931

* Misc fixes

* update distro (#184097)

* chore: update electron@22.5.5 (#184116)

* Extension host veto is registered multiple times on restart (fix #183778) (#184127)

Extension host veto is registered multiple times on restart (#183778)

* Do not auto start the local web worker extension host (#184137)

* Allow embedders to intercept trustedTypes.createPolicy calls (#184136)

Allow embedders to intercept trustedTypes.createPolicy calls (#184100)

* fix: reading from console output for --status on windows and linux (#184138)

fix: reading from console output for --status on windows and linux (#184118)

* Misc fixes

* code --status displays a lot of errors before actual status output (fix #183787) (#184200)

fix 183787

* (cherry-pick to 1.79 from main) Handle galleryExtension failure in featuredExtensionService (#184205)

Handle galleryExtension failure in featuredExtensionService (#184198)

Handle galleryExtension failure

* Fix #184183. Multiple output height updates are skipped. (#184188)

* Post merge init fixes

* Misc build issues

* disable toggle inline diff of `alt` down

https://github.com/microsoft/vscode-internalbacklog/issues/4342

* Take into account already activated extensions when computing running locations (#184303)

Take into account already activated extensions when computing running locations (fixes #184180)

* Avoid `extensionService.getExtension` and use `ActivationKind.Immediate` to allow that URI handling works while resolving (#184310)

Avoid `extensionService.getExtension` and use `ActivationKind.Immediate` to allow that URI handling works while resolving (fixes #182217)

* WIP

* rm fish auto injection

* More breaks

* Fix Port Attributes constructor (#184412)

* WIP

* WIP

* Allow extensions to get at the exports of other extensions during resolving (#184487)

Allow extensions to get at the exports of other extensions during resolving (fixes #184472)

* do not auto finish session when inline chat widgets have focus

re https://github.com/microsoft/vscode-internalbacklog/issues/4354

* fix compile errors caused by new base method

* WIP

* WIP

* WIP

* WIP

* Build errors

* unc - fix path traversal bypass

* Bump version

* cherry-pick prod changes from main

* Disable sandbox

* Build break from merge

* bump version

* Merge pull request #184739 from max06/max06/issue184659

Restore ShellIntegration for fish (#184659)

* Git - only add --find-renames if the value is not the default one (#185053)

Git - only add --find-renames if the value is not the default one (#184992)

* Cherry-pick: Revert changes to render featured extensions when available (#184747)

Revert changes to render featured extensions when available.  (#184573)

* Lower timeouts for experimentation and gallery service

* Revert changes to render extensions when available

* Add audio cues

* fix: disable app sandbox when --no-sandbox is present (#184913)

* fix: disable app sandbox when --no-sandbox is present (#184897)

* fix: loading minimist in packaged builds

* Runtime errors

* UNC allow list checks cannot be disabled in extension host (fix #184989) (#185085)

* UNC allow list checks cannot be disabled in extension host (#184989)

* Update src/vs/base/node/unc.js

Co-authored-by: Robo <hop2deep@gmail.com>

---------

Co-authored-by: Robo <hop2deep@gmail.com>

* Add notebook extension

* Fix mangling issues

* Fix mangling issues

* npm install

* npm install

* Issues blocking bundle

* Fix build folder compile errors

* Fix windows bundle build

* Linting fixes

* Fix sqllint issues

* Update yarn.lock files

* Fix unit tests

* Fix a couple breaks from test fixes

* Bump distro

* redo the checkbox style

* Update linux build container dockerfile

* Bump build image tag

* Bump native watch dog package

* Bump node-pty

* Bump distro

* Fix documnetation error

* Update distro

* redo the button styles

* Update datasource TS

* Add missing yarn.lock files

* Windows setup fix

* Turn off extension unit tests while investigating

* color box style

* Remove appx

* Turn off test log upload

* update dropdownlist style

* fix universal app build error (#23488)

* Skip flaky bufferContext vscode test

---------

Co-authored-by: Johannes <johannes.rieken@gmail.com>
Co-authored-by: Henning Dieterichs <hdieterichs@microsoft.com>
Co-authored-by: Julien Richard <jairbubbles@hotmail.com>
Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
Co-authored-by: Megan Rogge <merogge@microsoft.com>
Co-authored-by: meganrogge <megan.rogge@microsoft.com>
Co-authored-by: Rob Lourens <roblourens@gmail.com>
Co-authored-by: Connor Peet <connor@peet.io>
Co-authored-by: Joyce Er <joyce.er@microsoft.com>
Co-authored-by: Bhavya U <bhavyau@microsoft.com>
Co-authored-by: Raymond Zhao <7199958+rzhao271@users.noreply.github.com>
Co-authored-by: Martin Aeschlimann <martinae@microsoft.com>
Co-authored-by: Aaron Munger <aamunger@microsoft.com>
Co-authored-by: Aiday Marlen Kyzy <amarlenkyzy@microsoft.com>
Co-authored-by: rebornix <penn.lv@gmail.com>
Co-authored-by: Ole <oler@google.com>
Co-authored-by: Jean Pierre <jeanp413@hotmail.com>
Co-authored-by: Robo <hop2deep@gmail.com>
Co-authored-by: Yash Singh <saiansh2525@gmail.com>
Co-authored-by: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com>
Co-authored-by: Ulugbek Abdullaev <ulugbekna@gmail.com>
Co-authored-by: Alex Ross <alros@microsoft.com>
Co-authored-by: Michael Lively <milively@microsoft.com>
Co-authored-by: Matt Bierner <matb@microsoft.com>
Co-authored-by: Andrea Mah <31675041+andreamah@users.noreply.github.com>
Co-authored-by: Benjamin Pasero <benjamin.pasero@microsoft.com>
Co-authored-by: Sandeep Somavarapu <sasomava@microsoft.com>
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Co-authored-by: Tyler James Leonhardt <me@tylerleonhardt.com>
Co-authored-by: Alexandru Dima <alexdima@microsoft.com>
Co-authored-by: Joao Moreno <Joao.Moreno@microsoft.com>
Co-authored-by: Alan Ren <alanren@microsoft.com>
This commit is contained in:
Karl Burtram
2023-06-27 15:26:51 -07:00
committed by GitHub
parent 7975fda6dd
commit 01e66ab3e6
4335 changed files with 252586 additions and 164604 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "vscode-automation",
"version": "1.54.0",
"version": "1.71.0",
"description": "VS Code UI automation driver",
"author": {
"name": "Microsoft Corporation"

View File

@@ -69,7 +69,7 @@ export class Application {
}
async restart(options?: { workspaceOrFolder?: string; extraArgs?: string[] }): Promise<void> {
await measureAndLog((async () => {
await measureAndLog(() => (async () => {
await this.stop();
await this._start(options?.workspaceOrFolder, options?.extraArgs);
})(), 'Application#restart()', this.logger);
@@ -82,7 +82,7 @@ export class Application {
const code = await this.startApplication(extraArgs);
// ...and make sure the window is ready to interact
await measureAndLog(this.checkWindowReady(code), 'Application#checkWindowReady()', this.logger);
await measureAndLog(() => this.checkWindowReady(code), 'Application#checkWindowReady()', this.logger);
}
async stop(): Promise<void> {
@@ -116,7 +116,8 @@ export class Application {
private async checkWindowReady(code: Code): Promise<any> {
// We need a rendered workbench
await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger);
await measureAndLog(() => code.didFinishLoad(), 'Application#checkWindowReady: wait for navigation to be committed', this.logger);
await measureAndLog(() => code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger);
// {{SQL CARBON EDIT}} Wait for specified status bar items before considering the app ready - we wait for them together to avoid timing
@@ -124,7 +125,7 @@ export class Application {
const statusbarPromises: Promise<string>[] = [];
if (this.remote) {
await measureAndLog(code.waitForTextContent('.monaco-workbench .statusbar-item[id="status.host"]', undefined, statusHostLabel => {
await measureAndLog(() => code.waitForTextContent('.monaco-workbench .statusbar-item[id="status.host"]', undefined, statusHostLabel => {
this.logger.log(`checkWindowReady: remote indicator text is ${statusHostLabel}`);
// The absence of "Opening Remote" is not a strict

View File

@@ -3,15 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join } from 'path';
import * as os from 'os';
import * as cp from 'child_process';
import { IElement, ILocalizedStrings, ILocaleInfo } from './driver';
import { launch as launchPlaywrightBrowser } from './playwrightBrowser';
import { launch as launchPlaywrightElectron } from './playwrightElectron';
import { Logger, measureAndLog } from './logger';
import * as treekill from 'tree-kill';
import { teardown } from './processes';
import { IElement, ILocaleInfo, ILocalizedStrings, ILogFile } from './driver';
import { Logger, measureAndLog } from './logger';
import { launch as launchPlaywrightBrowser } from './playwrightBrowser';
import { PlaywrightDriver } from './playwrightDriver';
import { launch as launchPlaywrightElectron } from './playwrightElectron';
import { teardown } from './processes';
export interface LaunchOptions {
codePath?: string;
@@ -71,10 +71,9 @@ export async function launch(options: LaunchOptions): Promise<Code> {
if (stopped) {
throw new Error('Smoke test process has terminated, refusing to spawn Code');
}
// Browser smoke tests
if (options.web) {
const { serverProcess, driver } = await measureAndLog(launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger);
const { serverProcess, driver } = await measureAndLog(() => launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger);
registerInstance(serverProcess, options.logger, 'server');
return new Code(driver, options.logger, serverProcess);
@@ -82,7 +81,7 @@ export async function launch(options: LaunchOptions): Promise<Code> {
// Electron smoke tests (playwright)
else {
const { electronProcess, driver } = await measureAndLog(launchPlaywrightElectron(options), 'launch playwright (electron)', options.logger);
const { electronProcess, driver } = await measureAndLog(() => launchPlaywrightElectron(options), 'launch playwright (electron)', options.logger);
registerInstance(electronProcess, options.logger, 'electron');
return new Code(driver, options.logger, electronProcess);
@@ -129,8 +128,12 @@ export class Code {
await this.driver.dispatchKeybinding(keybinding);
}
async didFinishLoad(): Promise<void> {
return this.driver.didFinishLoad();
}
async exit(): Promise<void> {
return measureAndLog(new Promise<void>((resolve, reject) => {
return measureAndLog(() => new Promise<void>(resolve => {
const pid = this.mainProcess.pid!;
let done = false;
@@ -144,18 +147,39 @@ export class Code {
while (!done) {
retries++;
if (retries === 20) {
this.logger.log('Smoke test exit call did not terminate process after 10s, forcefully exiting the application...');
switch (retries) {
// no need to await since we're polling for the process to die anyways
treekill(pid, err => {
try {
process.kill(pid, 0); // throws an exception if the process doesn't exist anymore
this.logger.log('Failed to kill Electron process tree:', err?.message);
} catch (error) {
// Expected when process is gone
}
});
// after 5 / 10 seconds: try to exit gracefully again
case 10:
case 20: {
this.logger.log('Smoke test exit call did not terminate process after 5-10s, gracefully trying to exit the application again...');
this.driver.exitApplication();
break;
}
// after 20 seconds: forcefully kill
case 40: {
this.logger.log('Smoke test exit call did not terminate process after 20s, forcefully exiting the application...');
// no need to await since we're polling for the process to die anyways
treekill(pid, err => {
try {
process.kill(pid, 0); // throws an exception if the process doesn't exist anymore
this.logger.log('Failed to kill Electron process tree:', err?.message);
} catch (error) {
// Expected when process is gone
}
});
break;
}
// after 30 seconds: give up
case 60: {
done = true;
this.logger.log('Smoke test exit call did not terminate process after 30s, giving up');
resolve();
}
}
try {
@@ -165,12 +189,6 @@ export class Code {
done = true;
resolve();
}
if (retries === 60) {
done = true;
this.logger.log('Smoke test exit call did not terminate process after 30s, giving up');
resolve();
}
}
})();
}), 'Code#exit()', this.logger);
@@ -232,14 +250,18 @@ export class Code {
await this.poll(() => this.driver.writeInTerminal(selector, value), () => true, `writeInTerminal '${selector}'`);
}
async getLocaleInfo(): Promise<ILocaleInfo> {
getLocaleInfo(): Promise<ILocaleInfo> {
return this.driver.getLocaleInfo();
}
async getLocalizedStrings(): Promise<ILocalizedStrings> {
getLocalizedStrings(): Promise<ILocalizedStrings> {
return this.driver.getLocalizedStrings();
}
getLogs(): Promise<ILogFile[]> {
return this.driver.getLogs();
}
private async poll<T>(
fn: () => Promise<T>,
acceptFn: (result: T) => boolean,

View File

@@ -49,13 +49,22 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom
args.push('--disable-dev-shm-usage');
}
if (process.platform === 'darwin') {
// On macOS force software based rendering since we are seeing GPU process
// hangs when initializing GL context. This is very likely possible
// that there are new displays available in the CI hardware and
// the relevant drivers couldn't be loaded via the GPU sandbox.
// TODO(deepak1556): remove this switch with Electron update.
args.push('--use-gl=swiftshader');
}
if (remote) {
// Replace workspace path with URI
args[0] = `--${workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri=vscode-remote://test+test/${URI.file(workspacePath).path}`;
if (codePath) {
// running against a build: copy the test resolver extension
await measureAndLog(copyExtension(root, extensionsPath, 'vscode-test-resolver'), 'copyExtension(vscode-test-resolver)', logger);
await measureAndLog(() => copyExtension(root, extensionsPath, 'vscode-test-resolver'), 'copyExtension(vscode-test-resolver)', logger);
}
args.push('--enable-proposed-api=vscode.vscode-test-resolver');
const remoteDataDir = `${userDataDir}-server`;

View File

@@ -41,15 +41,15 @@ export class MultiLogger implements Logger {
}
}
export async function measureAndLog<T>(promise: Promise<T>, name: string, logger: Logger): Promise<T> {
export async function measureAndLog<T>(promiseFactory: () => Promise<T>, name: string, logger: Logger): Promise<T> {
const now = Date.now();
logger.log(`Starting operation '${name}...`);
logger.log(`Starting operation '${name}'...`);
let res: T | undefined = undefined;
let e: unknown;
try {
res = await promise;
res = await promiseFactory();
} catch (error) {
e = error;
}

View File

@@ -20,6 +20,7 @@ export class Notebook {
async openNotebook() {
await this.quickAccess.openFileQuickAccessAndWait('notebook.ipynb', 1);
await this.quickInput.selectQuickInputElement(0);
await this.code.waitForElement(activeRowSelector);
await this.focusFirstCell();
await this.waitForActiveCellEditorContents('code()');

View File

@@ -22,19 +22,20 @@ export async function launch(options: LaunchOptions): Promise<{ serverProcess: C
const { serverProcess, endpoint } = await launchServer(options);
// Launch browser
const { browser, context, page } = await launchBrowser(options, endpoint);
const { browser, context, page, pageLoadedPromise } = await launchBrowser(options, endpoint);
return {
serverProcess,
driver: new PlaywrightDriver(browser, context, page, serverProcess, options)
driver: new PlaywrightDriver(browser, context, page, serverProcess, pageLoadedPromise, options)
};
}
async function launchServer(options: LaunchOptions) {
const { userDataDir, codePath, extensionsPath, logger, logsPath } = options;
const serverLogsPath = join(logsPath, 'server');
const codeServerPath = codePath ?? process.env.VSCODE_REMOTE_SERVER_PATH;
const agentFolder = userDataDir;
await measureAndLog(mkdirp(agentFolder), `mkdirp(${agentFolder})`, logger);
await measureAndLog(() => mkdirp(agentFolder), `mkdirp(${agentFolder})`, logger);
const env = {
VSCODE_REMOTE_SERVER_PATH: codeServerPath,
@@ -49,7 +50,7 @@ async function launchServer(options: LaunchOptions) {
`--extensions-dir=${extensionsPath}`,
`--server-data-dir=${agentFolder}`,
'--accept-server-license-terms',
`--logsPath=${logsPath}`
`--logsPath=${serverLogsPath}`
];
if (options.verbose) {
@@ -68,7 +69,7 @@ async function launchServer(options: LaunchOptions) {
logger.log(`Starting server out of sources from '${serverLocation}'`);
}
logger.log(`Storing log files into '${logsPath}'`);
logger.log(`Storing log files into '${serverLogsPath}'`);
logger.log(`Command line: '${serverLocation}' ${args.join(' ')}`);
const serverProcess = spawn(
@@ -81,28 +82,32 @@ async function launchServer(options: LaunchOptions) {
return {
serverProcess,
endpoint: await measureAndLog(waitForEndpoint(serverProcess, logger), 'waitForEndpoint(serverProcess)', logger)
endpoint: await measureAndLog(() => waitForEndpoint(serverProcess, logger), 'waitForEndpoint(serverProcess)', logger)
};
}
async function launchBrowser(options: LaunchOptions, endpoint: string) {
const { logger, workspacePath, tracing, headless } = options;
const browser = await measureAndLog(playwright[options.browser ?? 'chromium'].launch({ headless: headless ?? false }), 'playwright#launch', logger);
const browser = await measureAndLog(() => playwright[options.browser ?? 'chromium'].launch({
headless: headless ?? false,
timeout: 0
}), 'playwright#launch', logger);
browser.on('disconnected', () => logger.log(`Playwright: browser disconnected`));
const context = await measureAndLog(browser.newContext(), 'browser.newContext', logger);
const context = await measureAndLog(() => browser.newContext(), 'browser.newContext', logger);
if (tracing) {
try {
await measureAndLog(context.tracing.start({ screenshots: true, /* remaining options are off for perf reasons */ }), 'context.tracing.start()', logger);
await measureAndLog(() => context.tracing.start({ screenshots: true, /* remaining options are off for perf reasons */ }), 'context.tracing.start()', logger);
} catch (error) {
logger.log(`Playwright (Browser): Failed to start playwright tracing (${error})`); // do not fail the build when this fails
}
}
const page = await measureAndLog(context.newPage(), 'context.newPage()', logger);
await measureAndLog(page.setViewportSize({ width: 1200, height: 800 }), 'page.setViewportSize', logger);
const page = await measureAndLog(() => context.newPage(), 'context.newPage()', logger);
await measureAndLog(() => page.setViewportSize({ width: 1200, height: 800 }), 'page.setViewportSize', logger);
if (options.verbose) {
context.on('page', () => logger.log(`Playwright (Browser): context.on('page')`));
@@ -133,9 +138,12 @@ async function launchBrowser(options: LaunchOptions, endpoint: string) {
`["logLevel","${options.verbose ? 'trace' : 'info'}"]`
].join(',')}]`;
await measureAndLog(page.goto(`${endpoint}&${workspacePath.endsWith('.code-workspace') ? 'workspace' : 'folder'}=${URI.file(workspacePath!).path}&payload=${payloadParam}`), 'page.goto()', logger);
const gotoPromise = measureAndLog(() => page.goto(`${endpoint}&${workspacePath.endsWith('.code-workspace') ? 'workspace' : 'folder'}=${URI.file(workspacePath!).path}&payload=${payloadParam}`), 'page.goto()', logger);
const pageLoadedPromise = page.waitForLoadState('load');
return { browser, context, page };
await gotoPromise;
return { browser, context, page, pageLoadedPromise };
}
function waitForEndpoint(server: ChildProcess, logger: Logger): Promise<string> {

View File

@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as playwright from '@playwright/test';
import { join } from 'path';
import { dirname, join } from 'path';
import { promises } from 'fs';
import { IWindowDriver } from './driver';
import { PageFunction } from 'playwright-core/types/structs';
import { measureAndLog } from './logger';
@@ -36,6 +37,7 @@ export class PlaywrightDriver {
private readonly context: playwright.BrowserContext,
private readonly page: playwright.Page,
private readonly serverProcess: ChildProcess | undefined,
private readonly whenLoaded: Promise<unknown>,
private readonly options: LaunchOptions
) {
}
@@ -46,7 +48,7 @@ export class PlaywrightDriver {
}
try {
await measureAndLog(this.context.tracing.startChunk({ title: name }), `startTracing for ${name}`, this.options.logger);
await measureAndLog(() => this.context.tracing.startChunk({ title: name }), `startTracing for ${name}`, this.options.logger);
} catch (error) {
// Ignore
}
@@ -63,7 +65,7 @@ export class PlaywrightDriver {
persistPath = join(this.options.logsPath, `playwright-trace-${PlaywrightDriver.traceCounter++}-${name.replace(/\s+/g, '-')}.zip`);
}
await measureAndLog(this.context.tracing.stopChunk({ path: persistPath }), `stopTracing for ${name}`, this.options.logger);
await measureAndLog(() => this.context.tracing.stopChunk({ path: persistPath }), `stopTracing for ${name}`, this.options.logger);
// To ensure we have a screenshot at the end where
// it failed, also trigger one explicitly. Tracing
@@ -77,11 +79,15 @@ export class PlaywrightDriver {
}
}
async didFinishLoad(): Promise<void> {
await this.whenLoaded;
}
private async takeScreenshot(name: string): Promise<void> {
try {
const persistPath = join(this.options.logsPath, `playwright-screenshot-${PlaywrightDriver.screenShotCounter++}-${name.replace(/\s+/g, '-')}.png`);
await measureAndLog(this.page.screenshot({ path: persistPath, type: 'png' }), 'takeScreenshot', this.options.logger);
await measureAndLog(() => this.page.screenshot({ path: persistPath, type: 'png' }), 'takeScreenshot', this.options.logger);
} catch (error) {
// Ignore
}
@@ -96,16 +102,25 @@ export class PlaywrightDriver {
// Stop tracing
try {
if (this.options.tracing) {
await measureAndLog(this.context.tracing.stop(), 'stop tracing', this.options.logger);
await measureAndLog(() => this.context.tracing.stop(), 'stop tracing', this.options.logger);
}
} catch (error) {
// Ignore
}
// Web: Extract client logs
if (this.options.web) {
try {
await measureAndLog(() => this.saveWebClientLogs(), 'saveWebClientLogs()', this.options.logger);
} catch (error) {
this.options.logger.log(`Error saving web client logs (${error})`);
}
}
// Web: exit via `close` method
if (this.options.web) {
try {
await measureAndLog(this.application.close(), 'playwright.close()', this.options.logger);
await measureAndLog(() => this.application.close(), 'playwright.close()', this.options.logger);
} catch (error) {
this.options.logger.log(`Error closing appliction (${error})`);
}
@@ -114,7 +129,7 @@ export class PlaywrightDriver {
// Desktop: exit via `driver.exitApplication`
else {
try {
await measureAndLog(this.evaluateWithDriver(([driver]) => driver.exitApplication()), 'driver.exitApplication()', this.options.logger);
await measureAndLog(() => this.evaluateWithDriver(([driver]) => driver.exitApplication()), 'driver.exitApplication()', this.options.logger);
} catch (error) {
this.options.logger.log(`Error exiting appliction (${error})`);
}
@@ -122,7 +137,18 @@ export class PlaywrightDriver {
// Server: via `teardown`
if (this.serverProcess) {
await measureAndLog(teardown(this.serverProcess, this.options.logger), 'teardown server process', this.options.logger);
await measureAndLog(() => teardown(this.serverProcess!, this.options.logger), 'teardown server process', this.options.logger);
}
}
private async saveWebClientLogs(): Promise<void> {
const logs = await this.getLogs();
for (const log of logs) {
const absoluteLogsPath = join(this.options.logsPath, log.relativePath);
await promises.mkdir(dirname(absoluteLogsPath), { recursive: true });
await promises.writeFile(absoluteLogsPath, log.contents);
}
}
@@ -166,7 +192,7 @@ export class PlaywrightDriver {
}
async getTitle() {
return this.evaluateWithDriver(([driver]) => driver.getTitle());
return this.page.title();
}
async isActiveElement(selector: string) {
@@ -201,6 +227,10 @@ export class PlaywrightDriver {
return this.evaluateWithDriver(([driver]) => driver.getLocalizedStrings());
}
async getLogs() {
return this.page.evaluate(([driver]) => driver.getLogs(), [await this.getDriverHandle()] as const);
}
private async evaluateWithDriver<T>(pageFunction: PageFunction<playwright.JSHandle<IWindowDriver>[], T>) {
return this.page.evaluate(pageFunction, [await this.getDriverHandle()]);
}

View File

@@ -22,26 +22,30 @@ export async function launch(options: LaunchOptions): Promise<{ electronProcess:
return {
electronProcess,
driver: new PlaywrightDriver(electron, context, page, undefined /* no server process */, options)
driver: new PlaywrightDriver(electron, context, page, undefined /* no server process */, Promise.resolve() /* Window is open already */, options)
};
}
async function launchElectron(configuration: IElectronConfiguration, options: LaunchOptions) {
const { logger, tracing } = options;
const electron = await measureAndLog(playwright._electron.launch({
const electron = await measureAndLog(() => playwright._electron.launch({
executablePath: configuration.electronPath,
args: configuration.args,
env: configuration.env as { [key: string]: string }
env: configuration.env as { [key: string]: string },
timeout: 0
}), 'playwright-electron#launch', logger);
const window = await measureAndLog(electron.firstWindow(), 'playwright-electron#firstWindow', logger);
let window = electron.windows()[0];
if (!window) {
window = await measureAndLog(() => electron.waitForEvent('window', { timeout: 0 }), 'playwright-electron#firstWindow', logger);
}
const context = window.context();
if (tracing) {
try {
await measureAndLog(context.tracing.start({ screenshots: true, /* remaining options are off for perf reasons */ }), 'context.tracing.start()', logger);
await measureAndLog(() => context.tracing.start({ screenshots: true, /* remaining options are off for perf reasons */ }), 'context.tracing.start()', logger);
} catch (error) {
logger.log(`Playwright (Electron): Failed to start playwright tracing (${error})`); // do not fail the build when this fails
}

View File

@@ -33,7 +33,7 @@ export class Problems {
static getSelectorInProblemsView(problemType: ProblemSeverity): string {
const selector = problemType === ProblemSeverity.WARNING ? 'codicon-warning' : 'codicon-error';
return `div[id="workbench.panel.markers"] .monaco-tl-contents .marker-icon.${selector}`;
return `div[id="workbench.panel.markers"] .monaco-tl-contents .marker-icon .${selector}`;
}
static getSelectorInEditor(problemType: ProblemSeverity): string {

View File

@@ -11,8 +11,9 @@ export class QuickInput {
private static QUICK_INPUT_INPUT = `${QuickInput.QUICK_INPUT} .quick-input-box input`;
private static QUICK_INPUT_ROW = `${QuickInput.QUICK_INPUT} .quick-input-list .monaco-list-row`;
private static QUICK_INPUT_FOCUSED_ELEMENT = `${QuickInput.QUICK_INPUT_ROW}.focused .monaco-highlighted-label`;
private static QUICK_INPUT_ENTRY_LABEL = `${QuickInput.QUICK_INPUT_ROW} .label-name`;
private static QUICK_INPUT_ENTRY_LABEL_SPAN = `${QuickInput.QUICK_INPUT_ROW} .monaco-highlighted-label span`;
// Note: this only grabs the label and not the description or detail
private static QUICK_INPUT_ENTRY_LABEL = `${QuickInput.QUICK_INPUT_ROW} .quick-input-list-row > .monaco-icon-label .label-name`;
private static QUICK_INPUT_ENTRY_LABEL_SPAN = `${QuickInput.QUICK_INPUT_ROW} .monaco-highlighted-label`;
constructor(private code: Code) { }

View File

@@ -35,7 +35,7 @@ export class Search extends Viewlet {
async clearSearchResults(): Promise<void> {
await retry(
() => this.code.waitAndClick(`.sidebar .codicon-search-clear-results`),
() => this.code.waitAndClick(`.sidebar .title-actions .codicon-search-clear-results`),
() => this.waitForNoResultText(10));
}

View File

@@ -10,9 +10,9 @@ import { IElement } from './driver';
export enum Selector {
TerminalView = `#terminal`,
CommandDecorationPlaceholder = `.terminal-command-decoration.codicon-circle-outline`,
CommandDecorationSuccess = `.terminal-command-decoration.codicon-primitive-dot`,
CommandDecorationError = `.terminal-command-decoration.codicon-error-small`,
CommandDecorationPlaceholder = `.terminal-command-decoration.codicon-terminal-decoration-incomplete`,
CommandDecorationSuccess = `.terminal-command-decoration.codicon-terminal-decoration-success`,
CommandDecorationError = `.terminal-command-decoration.codicon-terminal-decoration-error`,
Xterm = `#terminal .terminal-wrapper`,
XtermEditor = `.editor-instance .terminal-wrapper`,
TabsEntry = '.terminal-tabs-entry',

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"target": "es2020",
"strict": true,
"noUnusedParameters": false,
"noUnusedLocals": true,
@@ -9,7 +9,7 @@
"sourceMap": true,
"declaration": true,
"lib": [
"es2016",
"es2020",
"dom"
]
},

View File

@@ -22,9 +22,9 @@
integrity sha512-aK9jxMypeSrhiYofWWBf/T7O+KwaiAHzM4sveCdWPn71lzUSMimRnKzhXDKfKwV1kWoBo2P1aGgaIYGLf9/ljw==
"@types/node@16.x":
version "16.11.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
version "16.18.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc"
integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==
"@types/tmp@0.2.2":
version "0.2.2"

View File

@@ -13,6 +13,10 @@ import { URI } from 'vscode-uri';
import * as kill from 'tree-kill';
import * as optimistLib from 'optimist';
import { promisify } from 'util';
import { promises } from 'fs';
const root = path.join(__dirname, '..', '..', '..', '..');
const logsPath = path.join(root, '.build', 'logs', 'integration-tests-browser');
const optimist = optimistLib
.describe('workspacePath', 'path to the workspace (folder or *.code-workspace file) to open in the test').string('workspacePath')
@@ -35,6 +39,7 @@ type BrowserType = 'chromium' | 'firefox' | 'webkit';
async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWithStringQuery, server: cp.ChildProcess): Promise<void> {
const browser = await playwright[browserType].launch({ headless: !Boolean(optimist.argv.debug) });
const context = await browser.newContext();
const page = await context.newPage();
await page.setViewportSize({ width, height });
@@ -58,26 +63,22 @@ async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWith
console.error('Request Failed', e.url(), e.failure()?.errorText);
});
const host = endpoint.host;
const protocol = 'vscode-remote';
const testWorkspacePath = URI.file(path.resolve(optimist.argv.workspacePath)).path;
const testExtensionUri = url.format({ pathname: URI.file(path.resolve(optimist.argv.extensionDevelopmentPath)).path, protocol, host, slashes: true });
const testFilesUri = url.format({ pathname: URI.file(path.resolve(optimist.argv.extensionTestsPath)).path, protocol, host, slashes: true });
const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"],["enableProposedApi",""],["webviewExternalEndpointCommit","3c8520fab514b9f56070214496b26ff68d1b1cb5"],["skipWelcome","true"]]`;
if (path.extname(testWorkspacePath) === '.code-workspace') {
await page.goto(`${endpoint.href}&workspace=${testWorkspacePath}&payload=${payloadParam}`);
} else {
await page.goto(`${endpoint.href}&folder=${testWorkspacePath}&payload=${payloadParam}`);
}
await page.exposeFunction('codeAutomationLog', (type: string, args: any[]) => {
console[type](...args);
});
await page.exposeFunction('codeAutomationExit', async (code: number) => {
await page.exposeFunction('codeAutomationExit', async (code: number, logs: Array<{ readonly relativePath: string; readonly contents: string }>) => {
try {
for (const log of logs) {
const absoluteLogsPath = path.join(logsPath, log.relativePath);
await promises.mkdir(path.dirname(absoluteLogsPath), { recursive: true });
await promises.writeFile(absoluteLogsPath, log.contents);
}
} catch (error) {
console.error(`Error saving web client logs (${error})`);
}
try {
await browser.close();
} catch (error) {
@@ -92,6 +93,21 @@ async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWith
process.exit(code);
});
const host = endpoint.host;
const protocol = 'vscode-remote';
const testWorkspacePath = URI.file(path.resolve(optimist.argv.workspacePath)).path;
const testExtensionUri = url.format({ pathname: URI.file(path.resolve(optimist.argv.extensionDevelopmentPath)).path, protocol, host, slashes: true });
const testFilesUri = url.format({ pathname: URI.file(path.resolve(optimist.argv.extensionTestsPath)).path, protocol, host, slashes: true });
const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"],["enableProposedApi",""],["webviewExternalEndpointCommit","ef65ac1ba57f57f2a3961bfe94aa20481caca4c6"],["skipWelcome","true"]]`;
if (path.extname(testWorkspacePath) === '.code-workspace') {
await page.goto(`${endpoint.href}&workspace=${testWorkspacePath}&payload=${payloadParam}`);
} else {
await page.goto(`${endpoint.href}&folder=${testWorkspacePath}&payload=${payloadParam}`);
}
}
function consoleLogFn(msg: playwright.ConsoleMessage) {
@@ -122,9 +138,6 @@ async function launchServer(browserType: BrowserType): Promise<{ endpoint: url.U
...process.env
};
const root = path.join(__dirname, '..', '..', '..', '..');
const logsPath = path.join(root, '.build', 'logs', 'integration-tests-browser');
const serverArgs = ['--enable-proposed-api', '--disable-telemetry', '--server-data-dir', userDataDir, '--accept-server-license-terms', '--disable-workspace-trust'];
let serverLocation: string;
@@ -144,8 +157,9 @@ async function launchServer(browserType: BrowserType): Promise<{ endpoint: url.U
}
}
console.log(`Storing log files into '${logsPath}'`);
serverArgs.push('--logsPath', logsPath);
const serverLogsPath = path.join(logsPath, 'server');
console.log(`Storing log files into '${serverLogsPath}'`);
serverArgs.push('--logsPath', serverLogsPath);
const stdio: cp.StdioOptions = optimist.argv.debug ? 'pipe' : ['ignore', 'pipe', 'ignore'];

View File

@@ -4,14 +4,14 @@
"noImplicitAny": false,
"removeComments": false,
"preserveConstEnums": true,
"target": "es2017",
"strictNullChecks": true,
"target": "es2020",
"strict": true,
"noUnusedParameters": false,
"noUnusedLocals": true,
"outDir": "out",
"sourceMap": true,
"lib": [
"es2016",
"es2020",
"dom"
]
},

View File

@@ -34,9 +34,9 @@
integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==
"@types/node@16.x":
version "16.11.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
version "16.18.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc"
integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==
"@types/optimist@0.0.29":
version "0.0.29"

View File

@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MochaOptions } from 'mocha';
export function configure(opts: MochaOptions): void;
export function run(testsRoot: string[], clb: (error: Error | undefined, failures: number | undefined) => void): void;

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// eslint-disable-next-line code-no-standalone-editor
// eslint-disable-next-line local/code-no-standalone-editor
import * as monaco from './out/vs/editor/editor.main.js';
monaco.editor.create(document.getElementById('container'), {

View File

@@ -11,7 +11,7 @@
"mocha": "node ../node_modules/mocha/bin/mocha"
},
"dependencies": {
"@vscode/test-electron": "2.1.4",
"@vscode/test-electron": "^2.2.1",
"mkdirp": "^1.0.4",
"ncp": "^2.0.0",
"node-fetch": "^2.6.7",

View File

@@ -8,7 +8,7 @@ import { Application, Logger } from '../../../../automation';
import { installAllHandlers } from '../../utils';
export function setup(logger: Logger) {
describe.skip('Notebooks', () => { // TODO@rebornix https://github.com/microsoft/vscode/issues/140575
describe.skip('Notebooks', () => { // https://github.com/microsoft/vscode/issues/140575
// Shared before/after handling
installAllHandlers(logger);
@@ -25,7 +25,7 @@ export function setup(logger: Logger) {
cp.execSync('git reset --hard HEAD --quiet', { cwd: app.workspacePathOrFolder });
});
it.skip('inserts/edits code cell', async function () { // TODO@rebornix https://github.com/microsoft/vscode/issues/139672
it('inserts/edits code cell', async function () {
const app = this.app as Application;
await app.workbench.notebook.openNotebook();
await app.workbench.notebook.focusNextCell();
@@ -34,7 +34,7 @@ export function setup(logger: Logger) {
await app.workbench.notebook.stopEditingCell();
});
it.skip('inserts/edits markdown cell', async function () {
it('inserts/edits markdown cell', async function () {
const app = this.app as Application;
await app.workbench.notebook.openNotebook();
await app.workbench.notebook.focusNextCell();

View File

@@ -29,14 +29,16 @@ export function setup(logger: Logger) {
it('searches only for *.js files & checks for correct result number', async function () {
const app = this.app as Application;
await app.workbench.search.searchFor('body');
await app.workbench.search.showQueryDetails();
await app.workbench.search.setFilesToIncludeText('*.js');
await app.workbench.search.submitSearch();
try {
await app.workbench.search.setFilesToIncludeText('*.js');
await app.workbench.search.searchFor('body');
await app.workbench.search.showQueryDetails();
await app.workbench.search.waitForResultText('4 results in 1 file');
await app.workbench.search.setFilesToIncludeText('');
await app.workbench.search.hideQueryDetails();
await app.workbench.search.waitForResultText('4 results in 1 file');
} finally {
await app.workbench.search.setFilesToIncludeText('');
await app.workbench.search.hideQueryDetails();
}
});
it('dismisses result & checks for correct result number', async function () {

View File

@@ -51,18 +51,6 @@ export function setup() {
await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 });
});
});
describe('Custom configuration', function () {
it('Should update and show custom icons', async () => {
await createShellIntegrationProfile();
await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 });
await terminal.runCommandInTerminal(`echo "foo"`);
await terminal.runCommandInTerminal(`bar`);
await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIcon', '"zap"');
await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconSuccess', '"zap"');
await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconError', '"zap"');
await terminal.assertCommandDecorations(undefined, { updatedIcon: "zap", count: 3 });
});
});
describe('terminal.integrated.shellIntegration.decorationsEnabled should determine gutter and overview ruler decoration visibility', function () {
beforeEach(async () => {
await settingsEditor.clearUserSettings();

View File

@@ -8,7 +8,10 @@ import { Application, ApplicationOptions, Logger, Quality } from '../../../../au
import { createApp, timeout, installDiagnosticsHandler, installAppAfterHandler, getRandomUserDataDir, suiteLogsPath, suiteCrashPath } from '../../utils';
export function setup(ensureStableCode: () => string | undefined, logger: Logger) {
describe('Data Loss (insiders -> insiders)', () => {
describe('Data Loss (insiders -> insiders)', function () {
// Double the timeout since these tests involve 2 startups
this.timeout(4 * 60 * 1000);
let app: Application | undefined = undefined;
@@ -72,18 +75,18 @@ export function setup(ensureStableCode: () => string | undefined, logger: Logger
});
it('verifies that "hot exit" works for dirty files (without delay)', function () {
return testHotExit.call(this, 'test_verifies_that_hot_exit_works_for_dirty_files_without_delay', undefined);
return testHotExit.call(this, 'test_verifies_that_hot_exit_works_for_dirty_files_without_delay', undefined, undefined);
});
it('verifies that "hot exit" works for dirty files (with delay)', function () {
return testHotExit.call(this, 'test_verifies_that_hot_exit_works_for_dirty_files_with_delay', 2000);
return testHotExit.call(this, 'test_verifies_that_hot_exit_works_for_dirty_files_with_delay', 2000, undefined);
});
it('verifies that auto save triggers on shutdown', function () {
return testHotExit.call(this, 'test_verifies_that_auto_save_triggers_on_shutdown', undefined, true);
});
async function testHotExit(title: string, restartDelay: number | undefined, autoSave: boolean | undefined) {
async function testHotExit(this: import('mocha').Context, title: string, restartDelay: number | undefined, autoSave: boolean | undefined) {
app = createApp({
...this.defaultOptions,
logsPath: suiteLogsPath(this.defaultOptions, title),
@@ -130,7 +133,10 @@ export function setup(ensureStableCode: () => string | undefined, logger: Logger
}
});
describe('Data Loss (stable -> insiders)', () => {
describe('Data Loss (stable -> insiders)', function () {
// Double the timeout since these tests involve 2 startups
this.timeout(4 * 60 * 1000);
let insidersApp: Application | undefined = undefined;
let stableApp: Application | undefined = undefined;
@@ -203,7 +209,7 @@ export function setup(ensureStableCode: () => string | undefined, logger: Logger
return testHotExit.call(this, `test_verifies_that_hot_exit_works_for_dirty_files_with_delay_from_stable`, 2000);
});
async function testHotExit(title: string, restartDelay: number | undefined) {
async function testHotExit(this: import('mocha').Context, title: string, restartDelay: number | undefined) {
const stableCodePath = ensureStableCode();
if (!stableCodePath) {
this.skip();

View File

@@ -329,28 +329,58 @@ async function ensureStableCode(): Promise<void> {
let stableCodePath = opts['stable-build'];
if (!stableCodePath) {
const { major, minor } = parseVersion(version!);
const majorMinorVersion = `${major}.${minor - 1}`;
const versionsReq = await fetch('https://update.code.visualstudio.com/api/releases/stable', { headers: { 'x-api-version': '2' } });
const current = parseVersion(version!);
const versionsReq = await retry(() => measureAndLog(() => fetch('https://update.code.visualstudio.com/api/releases/stable'), 'versionReq', logger), 1000, 20);
if (!versionsReq.ok) {
throw new Error('Could not fetch releases from update server');
}
const versions: { version: string }[] = await versionsReq.json();
const prefix = `${majorMinorVersion}.`;
const previousVersion = versions.find(v => v.version.startsWith(prefix));
const versions: string[] = await measureAndLog(() => versionsReq.json(), 'versionReq.json()', logger);
const stableVersion = versions.find(raw => {
const version = parseVersion(raw);
return version.major < current.major || (version.major === current.major && version.minor < current.minor);
});
if (!previousVersion) {
throw new Error(`Could not find suitable stable version ${majorMinorVersion}`);
if (!stableVersion) {
throw new Error(`Could not find suitable stable version for ${version}`);
}
console.log(`*** Found VS Code v${version}, downloading previous VS Code version ${previousVersion.version}...`);
logger.log(`Found VS Code v${version}, downloading previous VS Code version ${stableVersion}...`);
const stableCodeExecutable = await vscodetest.download({
cachePath: path.join(os.tmpdir(), 'vscode-test'),
version: previousVersion.version
});
let lastProgressMessage: string | undefined = undefined;
let lastProgressReportedAt = 0;
const stableCodeDestination = path.join(testDataPath, 's');
const stableCodeExecutable = await retry(() => measureAndLog(() => vscodetest.download({
cachePath: stableCodeDestination,
version: stableVersion,
extractSync: true,
reporter: {
report: report => {
let progressMessage = `download stable code progress: ${report.stage}`;
const now = Date.now();
if (progressMessage !== lastProgressMessage || now - lastProgressReportedAt > 10000) {
lastProgressMessage = progressMessage;
lastProgressReportedAt = now;
if (report.stage === 'downloading') {
progressMessage += ` (${report.bytesSoFar}/${report.totalBytes})`;
}
logger.log(progressMessage);
}
},
error: error => logger.log(`download stable code error: ${error}`)
}
}), 'download stable code', logger), 1000, 3, () => new Promise<void>((resolve, reject) => {
rimraf(stableCodeDestination, { maxBusyTries: 10 }, error => {
if (error) {
reject(error);
} else {
resolve();
}
});
}));
if (process.platform === 'darwin') {
// Visual Studio Code.app/Contents/MacOS/Electron
@@ -365,7 +395,7 @@ async function ensureStableCode(): Promise<void> {
throw new Error(`Can't find Stable VSCode at ${stableCodePath}.`);
}
console.log(`*** Using stable build ${stableCodePath} for migration tests`);
logger.log(`Using stable build ${stableCodePath} for migration tests`);
opts['stable-build'] = stableCodePath;
}

View File

@@ -19,25 +19,6 @@ export function itRepeat(n: number, description: string, callback: (this: Contex
}
}
/**
* Defines a test-case that will run but will be skips it if it throws an exception. This is useful
* to get some runs in CI when trying to stabilize a flaky test, without failing the build. Note
* that this only works if something inside the test throws, so a test's overall timeout won't work
* but throwing due to a polling timeout will.
* @param title The test-case title.
* @param callback The test-case callback.
*/
export function itSkipOnFail(title: string, callback: (this: Context) => any): void {
it(title, function () {
return Promise.resolve().then(() => {
return callback.apply(this, arguments);
}).catch(e => {
console.warn(`Test "${title}" failed but was marked as skip on fail:`, e);
this.skip();
});
});
}
export function installAllHandlers(logger: Logger, optionsTransform?: (opts: ApplicationOptions) => ApplicationOptions) {
installDiagnosticsHandler(logger);
installAppBeforeHandler(optionsTransform);
@@ -226,7 +207,7 @@ export async function retry<T>(task: ITask<Promise<T>>, delay: number, retries:
return await task();
} catch (error) {
lastError = error;
lastError = error as Error;
await timeout(delay);
}

View File

@@ -4,14 +4,14 @@
"noImplicitAny": false,
"removeComments": false,
"preserveConstEnums": true,
"target": "es2017",
"strictNullChecks": true,
"target": "es2020",
"strict": true,
"noUnusedParameters": false,
"noUnusedLocals": true,
"outDir": "out",
"sourceMap": true,
"lib": [
"es2016",
"es2020",
"dom"
]
},

View File

@@ -59,9 +59,9 @@
integrity sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ==
"@types/node@16.x":
version "16.11.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
version "16.18.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc"
integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==
"@types/rimraf@3.0.2":
version "3.0.2"
@@ -71,10 +71,10 @@
"@types/glob" "*"
"@types/node" "*"
"@vscode/test-electron@2.1.4":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.1.4.tgz#fa1b8915246d0102e81d4fd664bb6c337ca7092f"
integrity sha512-tHHAWNVwl8C7nyezHAHdNPWkksdXWvmae6bt4k1tJ9hvMm6QIIk95Mkutl82XHcD60mdP46EHDGU+xFsAvygOQ==
"@vscode/test-electron@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.2.1.tgz#6d1ac128e27c18e1d20bcb299e830b50587f74ca"
integrity sha512-DUdwSYVc9p/PbGveaq20dbAAXHfvdq4zQ24ILp6PKizOBxrOfMsOq8Vts5nMzeIo0CxtA/RxZLFyDv001PiUSg==
dependencies:
http-proxy-agent "^4.0.1"
https-proxy-agent "^5.0.0"
@@ -483,9 +483,9 @@ mime-types@^2.1.12:
mime-db "1.49.0"
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==
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"

View File

@@ -4,7 +4,7 @@
./scripts/test.[sh|bat]
All unit tests are run inside a electron-browser environment which access to DOM and Nodejs api. This is the closest to the environment in which VS Code itself ships. Notes:
All unit tests are run inside a Electron renderer environment which access to DOM and Nodejs api. This is the closest to the environment in which VS Code itself ships. Notes:
- 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

View File

@@ -467,5 +467,52 @@ assert.doesNotThrow = function(block, /*optional*/message) {
};
assert.ifError = function(err) { if (err) {throw err;}};
function checkIsPromise(obj) {
return (obj !== null && typeof obj === 'object' &&
typeof obj.then === 'function' &&
typeof obj.catch === 'function');
}
const NO_EXCEPTION_SENTINEL = {};
async function waitForActual(promiseFn) {
let resultPromise;
if (typeof promiseFn === 'function') {
// Return a rejected promise if `promiseFn` throws synchronously.
resultPromise = promiseFn();
// Fail in case no promise is returned.
if (!checkIsPromise(resultPromise)) {
throw new Error('ERR_INVALID_RETURN_VALUE: promiseFn did not return Promise. ' + resultPromise);
}
} else if (checkIsPromise(promiseFn)) {
resultPromise = promiseFn;
} else {
throw new Error('ERR_INVALID_ARG_TYPE: promiseFn is not Function or Promise. ' + promiseFn);
}
try {
await resultPromise;
} catch (e) {
return e;
}
return NO_EXCEPTION_SENTINEL;
}
function expectsError(shouldHaveError, actual, message) {
if (shouldHaveError && actual === NO_EXCEPTION_SENTINEL) {
fail(undefined, 'Error', `Missing expected rejection${message ? ': ' + message : ''}`)
} else if (!shouldHaveError && actual !== NO_EXCEPTION_SENTINEL) {
fail(actual, undefined, `Got unexpected rejection (${actual.message})${message ? ': ' + message : ''}`)
}
}
assert.rejects = async function rejects(promiseFn, message) {
expectsError(true, await waitForActual(promiseFn), message);
};
assert.doesNotReject = async function doesNotReject(fn, message) {
expectsError(false, await waitForActual(fn), message);
};
return assert;
});

View File

@@ -67,7 +67,7 @@ function ensureIsArray(a) {
const testModules = (async function () {
const excludeGlob = '**/{node,electron-sandbox,electron-browser,electron-main}/**/*.test.js';
const excludeGlob = '**/{node,electron-sandbox,electron-main}/**/*.test.js';
let isDefaultModules = true;
let promise;
@@ -131,15 +131,22 @@ async function runTestsInBrowser(testModules, browserType) {
const page = await context.newPage();
const target = url.pathToFileURL(path.join(__dirname, 'renderer.html'));
if (argv.build) {
target.search = `?build=true`;
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
target.search = `?build=true&ci=true`;
} else {
target.search = `?build=true`;
}
} else if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
target.search = `?ci=true`;
}
await page.goto(target.href);
const emitter = new events.EventEmitter();
await page.exposeFunction('mocha_report', (type, data1, data2) => {
emitter.emit(type, data1, data2);
});
await page.goto(target.href);
page.on('console', async msg => {
consoleLogFn(msg)(msg.text(), await Promise.all(msg.args().map(async arg => await arg.jsonValue())));
});
@@ -176,7 +183,7 @@ async function runTestsInBrowser(testModules, browserType) {
await browser.close();
if (failingTests.length > 0) {
let res = `The followings tests are failing:\n - ${failingTests.map(({ title, message }) => `${title} (reason: ${message})`).join('\n - ')}`;
let res = `The followings tests are failing:\n - ${failingTests.map(({ title, message }) => `${title} (reason: ${message})`).join('\n - ')}`;
if (failingModuleIds.length > 0) {
res += `\n\nTo DEBUG, open ${browserType.toUpperCase()} and navigate to ${target.href}?${failingModuleIds.map(module => `m=${module}`).join('&')}`;

View File

@@ -31,9 +31,12 @@
}
});
const urlParams = new URLSearchParams(window.location.search);
const isCI = urlParams.get('ci');
mocha.setup({
ui: 'tdd',
timeout: 5000
timeout: isCI ? 30000 : 5000
});
</script>
@@ -42,7 +45,6 @@
<script src="../../../out-build/vs/loader.js"></script>
<script>
const urlParams = new URLSearchParams(window.location.search);
const isBuild = urlParams.get('build');
// configure loader

View File

@@ -28,7 +28,12 @@ exports.initialize = function (loaderConfig) {
} catch (err) {
// missing source map...
}
return instrumenter.instrumentSync(contents, source, map);
try {
return instrumenter.instrumentSync(contents, source, map);
} catch (e) {
console.error(`Error instrumenting ${source}: ${e}`);
throw e;
}
};
};

View File

@@ -27,7 +27,7 @@ const optimist = require('optimist')
.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')
.describe('dev', 'open dev tools, keep window open, reuse app data').alias('dev', ['dev-tools', 'devTools']).string('dev')
.describe('reporter', 'the mocha reporter').string('reporter').default('reporter', 'spec')
.describe('reporter-options', 'the mocha reporter options').string('reporter-options').default('reporter-options', '')
.describe('wait-server', 'port to connect to and wait before running tests')
@@ -82,7 +82,7 @@ if (crashReporterDirectory) {
});
}
if (!argv.debug) {
if (!argv.dev) {
app.setPath('userData', path.join(tmpdir(), `vscode-tests-${Date.now()}`));
}
@@ -161,7 +161,7 @@ class IPCRunner extends events.EventEmitter {
app.on('ready', () => {
ipcMain.on('error', (_, err) => {
if (!argv.debug) {
if (!argv.dev) {
console.error(err);
app.exit(1);
}
@@ -191,7 +191,7 @@ app.on('ready', () => {
width: 800,
show: false,
webPreferences: {
preload: path.join(__dirname, '..', '..', '..', 'src', 'vs', 'base', 'parts', 'sandbox', 'electron-browser', 'preload.js'), // ensure similar environment as VSCode as tests may depend on this
preload: path.join(__dirname, '..', '..', '..', 'src', 'vs', 'base', 'parts', 'sandbox', 'electron-sandbox', 'preload.js'), // ensure similar environment as VSCode as tests may depend on this
additionalArguments: [`--vscode-window-config=vscode:test-vscode-window-config`],
nodeIntegration: true,
contextIsolation: false,
@@ -201,7 +201,7 @@ app.on('ready', () => {
});
win.webContents.on('did-finish-load', () => {
if (argv.debug) {
if (argv.dev) {
win.show();
win.webContents.openDevTools();
}
@@ -279,7 +279,7 @@ app.on('ready', () => {
applyReporter(runner, argv);
}
if (!argv.debug) {
if (!argv.dev) {
ipcMain.on('all done', () => app.exit(runner.didFail ? 1 : 0));
}
});

View File

@@ -35,7 +35,7 @@
mocha.setup({
ui: 'tdd',
timeout: 5000,
timeout: typeof process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] === 'string' ? 30000 : 5000,
forbidOnly: typeof process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] === 'string' // disallow .only() when running on build machine
});
require('./renderer');

View File

@@ -74,6 +74,13 @@ if (util.inspect && util.inspect['defaultOptions']) {
util.inspect['defaultOptions'].customInspect = false;
}
// VSCODE_GLOBALS: node_modules
globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target, mod) => (require.__$__nodeRequire ?? require)(String(mod)) });
// VSCODE_GLOBALS: package/product.json
globalThis._VSCODE_PRODUCT_JSON = (require.__$__nodeRequire ?? require)('../../../product.json');
globalThis._VSCODE_PACKAGE_JSON = (require.__$__nodeRequire ?? require)('../../../package.json');
const _tests_glob = '**/test/**/*.test.js';
let loader;
let _out;
@@ -86,7 +93,6 @@ function initLoader(opts) {
loader = require(`${_out}/vs/loader`);
const loaderConfig = {
nodeRequire: require,
nodeMain: __filename,
catchError: true,
baseUrl: bootstrap.fileUriFromPath(path.join(__dirname, '../../../src'), { isWindows: process.platform === 'win32' }),
paths: {
@@ -382,7 +388,7 @@ function runTests(opts) {
}
}
if (!opts.debug) {
if (!opts.dev) {
mocha.reporter(IPCReporter);
}
@@ -392,7 +398,7 @@ function runTests(opts) {
});
});
if (opts.debug) {
if (opts.dev) {
runner.on('fail', (test, err) => {
console.error(test.fullTitle());

View File

@@ -27,7 +27,7 @@ const optimist = require('optimist')
const TEST_GLOB = '**/test/**/*.test.js';
const excludeGlobs = [
'**/{browser,electron-sandbox,electron-browser,electron-main}/**/*.test.js',
'**/{browser,electron-sandbox,electron-main}/**/*.test.js',
'**/vs/platform/environment/test/node/nativeModules.test.js', // native modules are compiled against Electron and this test would fail with node.js
'**/vs/base/parts/storage/test/node/storage.test.js', // same as above, due to direct dependency to sqlite native module
'**/vs/workbench/contrib/testing/test/**' // flaky (https://github.com/microsoft/vscode/issues/137853)
@@ -56,6 +56,15 @@ if (majorRequiredNodeVersion !== currentMajorNodeVersion) {
}
function main() {
// VSCODE_GLOBALS: node_modules
globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target, mod) => require(String(mod)) });
// VSCODE_GLOBALS: package/product.json
globalThis._VSCODE_PRODUCT_JSON = require(`${REPO_ROOT}/product.json`);
globalThis._VSCODE_PACKAGE_JSON = require(`${REPO_ROOT}/package.json`);
process.on('uncaughtException', function (e) {
console.error(e.stack || e);
});
@@ -94,7 +103,6 @@ function main() {
const loaderConfig = {
nodeRequire: require,
nodeMain: __filename,
baseUrl: fileUriFromPath(src, { isWindows: process.platform === 'win32' }),
'sql': `../${out}/sql`, // {{SQL CARBON EDIT}}
catchError: true,