diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index b59d91ff380c7075ec99ed60977434e07d88d352..86c139ca2073f2cb8f648c065faf0e77e152c668 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -14,7 +14,7 @@ const webviewId = 'myWebview'; const testDocument = join(vscode.workspace.rootPath || '', './bower.json'); // TODO: Re-enable after https://github.com/microsoft/vscode/issues/88415 -suite.skip('Webview tests', () => { +('electron' in process.versions ? suite.skip : suite)('Webview tests', () => { const disposables: vscode.Disposable[] = []; function _register(disposable: T) { diff --git a/src/vs/platform/log/browser/log.ts b/src/vs/platform/log/browser/log.ts new file mode 100644 index 0000000000000000000000000000000000000000..466ba9dbabaed976903a5db295bed0c62d58df64 --- /dev/null +++ b/src/vs/platform/log/browser/log.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILogService, DEFAULT_LOG_LEVEL, LogLevel, LogServiceAdapter } from 'vs/platform/log/common/log'; + +interface IAutomatedWindow { + codeAutomationLog(type: string, args: any[]): void; +} + +/** + * A logger that is used when VSCode is running in the web with + * an automation such as playwright. We expect a global codeAutomationLog + * to be defined that we can use to log to. + */ +export class ConsoleLogInAutomationService extends LogServiceAdapter implements ILogService { + + declare codeAutomationLog: any; + + _serviceBrand: undefined; + + constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) { + super({ consoleLog: (type, args) => this.consoleLog(type, args) }, logLevel); + } + + private consoleLog(type: string, args: any[]): void { + const automatedWindow = window as unknown as IAutomatedWindow; + if (typeof automatedWindow.codeAutomationLog === 'function') { + automatedWindow.codeAutomationLog(type, args); + } + } +} diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index cc548cfafc35cd6bb3ebaa06c7a7b6c890c8dea1..04cd3646418ae193b98050b16ad0808afd4c2c28 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -213,48 +213,48 @@ export class ConsoleLogService extends AbstractLogService implements ILogService } } -export class ConsoleLogInMainService extends AbstractLogService implements ILogService { +export class LogServiceAdapter extends AbstractLogService implements ILogService { _serviceBrand: undefined; - constructor(private readonly client: LoggerChannelClient, logLevel: LogLevel = DEFAULT_LOG_LEVEL) { + constructor(private readonly adapter: { consoleLog: (type: string, args: any[]) => void }, logLevel: LogLevel = DEFAULT_LOG_LEVEL) { super(); this.setLevel(logLevel); } trace(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Trace) { - this.client.consoleLog('trace', [this.extractMessage(message), ...args]); + this.adapter.consoleLog('trace', [this.extractMessage(message), ...args]); } } debug(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Debug) { - this.client.consoleLog('debug', [this.extractMessage(message), ...args]); + this.adapter.consoleLog('debug', [this.extractMessage(message), ...args]); } } info(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Info) { - this.client.consoleLog('info', [this.extractMessage(message), ...args]); + this.adapter.consoleLog('info', [this.extractMessage(message), ...args]); } } warn(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Warning) { - this.client.consoleLog('warn', [this.extractMessage(message), ...args]); + this.adapter.consoleLog('warn', [this.extractMessage(message), ...args]); } } error(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Error) { - this.client.consoleLog('error', [this.extractMessage(message), ...args]); + this.adapter.consoleLog('error', [this.extractMessage(message), ...args]); } } critical(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Critical) { - this.client.consoleLog('critical', [this.extractMessage(message), ...args]); + this.adapter.consoleLog('critical', [this.extractMessage(message), ...args]); } } @@ -275,6 +275,15 @@ export class ConsoleLogInMainService extends AbstractLogService implements ILogS } } +export class ConsoleLogInMainService extends LogServiceAdapter implements ILogService { + + _serviceBrand: undefined; + + constructor(client: LoggerChannelClient, logLevel: LogLevel = DEFAULT_LOG_LEVEL) { + super({ consoleLog: (type, args) => client.consoleLog(type, args) }, logLevel); + } +} + export class MultiplexLogService extends AbstractLogService implements ILogService { _serviceBrand: undefined; diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 94e7052574f1d74789e1b2084e8062673a9a5a95..96de0d2a04df8d511ac8a3d7472a4dbf2a7bb926 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -7,6 +7,7 @@ import { mark } from 'vs/base/common/performance'; import { domContentLoaded, addDisposableListener, EventType, addClass, EventHelper } from 'vs/base/browser/dom'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILogService, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log'; +import { ConsoleLogInAutomationService } from 'vs/platform/log/browser/log'; import { Disposable } from 'vs/base/common/lifecycle'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { Workbench } from 'vs/workbench/browser/workbench'; @@ -49,6 +50,7 @@ import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedD import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; +import { coalesce } from 'vs/base/common/arrays'; class BrowserMain extends Disposable { @@ -234,9 +236,12 @@ class BrowserMain extends Disposable { } } - const consoleLogService = new ConsoleLogService(logService.getLevel()); - const fileLogService = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService); - logService.logger = new MultiplexLogService([consoleLogService, fileLogService]); + logService.logger = new MultiplexLogService(coalesce([ + new ConsoleLogService(logService.getLevel()), + new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService), + // Extension development test CLI: forward everything to test runner + environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI ? new ConsoleLogInAutomationService(logService.getLevel()) : undefined + ])); })(); const connection = remoteAgentService.getConnection(); diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index d1832f0c2e54e5cef0f0bcb2549fad4611e4a9ba..410acd256cd54cc2d0d94072fc8f9c0a72db82eb 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -111,8 +111,8 @@ export async function launch(userDataDir: string, _workspacePath: string, codeSe ['--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); @@ -148,7 +148,7 @@ export function connect(headless: boolean, engine: 'chromium' | 'webkit' | 'fire await page.setViewport({ width, height }); 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); diff --git a/test/integration/browser/package.json b/test/integration/browser/package.json index ed4f447f681e2b91eaf3492dbce431e250a53045..2b9c7cd2c4b89758b57a2f3aa5677b1d5c23bdfe 100644 --- a/test/integration/browser/package.json +++ b/test/integration/browser/package.json @@ -10,6 +10,7 @@ "@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" diff --git a/test/integration/browser/src/index.ts b/test/integration/browser/src/index.ts index 11b1e49e73d4e37ba8b50ce0ed72c1e113a07ba6..559bbafc162ade3b83c845ae5485c597e5860aa4 100644 --- a/test/integration/browser/src/index.ts +++ b/test/integration/browser/src/index.ts @@ -15,18 +15,13 @@ const optimist = require('optimist') .describe('browser', 'browser in which integration tests should run').string('browser').default('browser', 'chromium') .describe('help', 'show the help').alias('help', 'h'); -let serverProcess: cp.ChildProcess | undefined = undefined; - -function teardownServer() { - if (serverProcess) { - serverProcess.kill(); - serverProcess = undefined; - } -} +const width = 1200; +const height = 800; async function runTestsInBrowser(browserType: string, endpoint: string): Promise { 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'; @@ -41,24 +36,22 @@ async function runTestsInBrowser(browserType: string, endpoint: string): Promise await page.goto(`${endpoint}&folder=${folderParam}&payload=${payloadParam}`); - // const emitter = new events.EventEmitter(); - // await page.exposeFunction('mocha_report', (type, data1, data2) => { - // emitter.emit(type, data1, data2) - // }); + await page.exposeFunction('codeAutomationLog', (type: string, args: any[]) => { + console[type](...args); + }); page.on('console', async (msg: playwright.ConsoleMessage) => { const msgText = msg.text(); - console[msg.type()](msgText, await Promise.all(msg.args().map(async arg => await arg.jsonValue()))); - if (msgText.indexOf('vscode:exit') >= 0) { - browser.close(); - teardownServer(); - setTimeout(() => process.exit(msgText === 'vscode:exit 0' ? 0 : 1), 10); + await browser.close(); + process.exit(msgText === 'vscode:exit 0' ? 0 : 1); } }); } async function launchServer(): Promise { + + // 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)); @@ -70,7 +63,7 @@ async function launchServer(): Promise { ...process.env }; - let serverLocation; + 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 { @@ -79,24 +72,33 @@ async function launchServer(): Promise { process.env.VSCODE_DEV = '1'; } - serverProcess = cp.spawn( + let serverProcess = cp.spawn( serverLocation, ['--browser', 'none', '--driver', 'web'], { env } ); - serverProcess?.stderr?.on('data', e => console.log(`Server stderr: ${e}`)); - serverProcess?.stdout?.on('data', e => console.log(`Server stdout: ${e}`)); + 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(r => { - serverProcess?.stdout?.on('data', d => { - const matches = d.toString('ascii').match(/Web UI available at (.+)/); + return new Promise(c => { + serverProcess?.stdout?.on('data', data => { + const matches = data.toString('ascii').match(/Web UI available at (.+)/); if (matches !== null) { - r(matches[1]); + c(matches[1]); } }); }); diff --git a/test/integration/browser/yarn.lock b/test/integration/browser/yarn.lock index 059c3c8f3cf8b6be3ac564540b7b110984b5c19a..647f08c5cebe3ec8b7c45e119260f3a0da27f5c5 100644 --- a/test/integration/browser/yarn.lock +++ b/test/integration/browser/yarn.lock @@ -46,6 +46,11 @@ "@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"