diff --git a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts index 1389367cff8..183790bd295 100644 --- a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts @@ -6,9 +6,12 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { RemoteAuthorities } from 'vs/base/common/network'; +import * as performance from 'vs/base/common/performance'; +import { StopWatch } from 'vs/base/common/stopwatch'; import { URI } from 'vs/base/common/uri'; +import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { getRemoteServerRootPath, parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts'; export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { @@ -23,7 +26,12 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot private readonly _connectionToken: Promise | string | undefined; private readonly _connectionTokens: Map; - constructor(@IProductService productService: IProductService, connectionToken: Promise | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined) { + constructor( + connectionToken: Promise | string | undefined, + resourceUriProvider: ((uri: URI) => URI) | undefined, + @IProductService productService: IProductService, + @ILogService private readonly _logService: ILogService, + ) { super(); this._connectionToken = connectionToken; this._connectionTokens = new Map(); @@ -60,7 +68,13 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot } private async _doResolveAuthority(authority: string): Promise { + const authorityPrefix = getRemoteAuthorityPrefix(authority); + const sw = StopWatch.create(false); + this._logService.info(`Resolving connection token (${authorityPrefix})...`); + performance.mark(`code/willResolveConnectionToken/${authorityPrefix}`); const connectionToken = await Promise.resolve(this._connectionTokens.get(authority) || this._connectionToken); + performance.mark(`code/didResolveConnectionToken/${authorityPrefix}`); + this._logService.info(`Resolved connection token (${authorityPrefix}) after ${sw.elapsed()} ms`); const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80); const { host, port } = parseAuthorityWithOptionalPort(authority, defaultPort); const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken } }; diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index f1b22d74b1c..8cbc8ec31c4 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -9,6 +9,8 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { isCancellationError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import * as performance from 'vs/base/common/performance'; +import { StopWatch } from 'vs/base/common/stopwatch'; import { generateUuid } from 'vs/base/common/uuid'; import { IIPCLogger } from 'vs/base/parts/ipc/common/ipc'; import { Client, ConnectionHealth, ISocket, PersistentProtocol, ProtocolConstants, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net'; @@ -190,18 +192,27 @@ function readOneControlMessage(protocol: PersistentProtocol, timeoutCancellat return result.promise; } -function createSocket(logService: ILogService, socketFactory: ISocketFactory, host: string, port: number, path: string, query: string, debugLabel: string, timeoutCancellationToken: CancellationToken): Promise { +function createSocket(logService: ILogService, socketFactory: ISocketFactory, host: string, port: number, path: string, query: string, debugConnectionType: string, debugLabel: string, timeoutCancellationToken: CancellationToken): Promise { const result = new PromiseWithTimeout(timeoutCancellationToken); + const sw = StopWatch.create(false); + logService.info(`Creating a socket (${debugLabel})...`); + performance.mark(`code/willCreateSocket/${debugConnectionType}`); socketFactory.connect(host, port, path, query, debugLabel, (err: any, socket: ISocket | undefined) => { if (result.didTimeout) { + performance.mark(`code/didCreateSocketError/${debugConnectionType}`); + logService.info(`Creating a socket (${debugLabel}) finished after ${sw.elapsed()} ms, but this is too late and has timed out already.`); if (err) { logService.error(err); } socket?.dispose(); } else { if (err || !socket) { + performance.mark(`code/didCreateSocketError/${debugConnectionType}`); + logService.info(`Creating a socket (${debugLabel}) returned an error after ${sw.elapsed()} ms.`); result.reject(err); } else { + performance.mark(`code/didCreateSocketOK/${debugConnectionType}`); + logService.info(`Creating a socket (${debugLabel}) was successful after ${sw.elapsed()} ms.`); result.resolve(socket); } } @@ -233,7 +244,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio let socket: ISocket; try { - socket = await createSocket(options.logService, options.socketFactory, options.host, options.port, getRemoteServerRootPath(options), `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken); + socket = await createSocket(options.logService, options.socketFactory, options.host, options.port, getRemoteServerRootPath(options), `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, connectionTypeToString(connectionType), `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken); } catch (error) { options.logService.error(`${logPrefix} socketFactory.connect() failed or timed out. Error:`); options.logService.error(error); diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index 607f53c8c9e..d90895652e6 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -124,3 +124,11 @@ export interface IRemoteAuthorityResolverService { _setAuthorityConnectionToken(authority: string, connectionToken: string): void; _setCanonicalURIProvider(provider: (uri: URI) => Promise): void; } + +export function getRemoteAuthorityPrefix(remoteAuthority: string): string { + const plusIndex = remoteAuthority.indexOf('+'); + if (plusIndex === -1) { + return remoteAuthority; + } + return remoteAuthority.substring(0, plusIndex); +} diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 26dfc14c97e..e70f5f0e4fe 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -26,7 +26,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { VSBuffer } from 'vs/base/common/buffer'; import { ExtensionGlobalMemento, ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes'; -import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; @@ -1039,14 +1039,6 @@ function filterExtensions(globalRegistry: ExtensionDescriptionRegistry, desiredE ); } -function getRemoteAuthorityPrefix(remoteAuthority: string): string { - const plusIndex = remoteAuthority.indexOf('+'); - if (plusIndex === -1) { - return remoteAuthority; - } - return remoteAuthority.substring(0, plusIndex); -} - export class ExtensionPaths { constructor( diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index d46792f2837..e8153494560 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -247,7 +247,7 @@ export class BrowserMain extends Disposable { // Remote const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName); - const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider); + const remoteAuthorityResolverService = new RemoteAuthorityResolverService(connectionToken, this.configuration.resourceUriProvider, productService, logService); serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); // Signing diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 1fb15c5f721..c3888271178 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -88,7 +88,7 @@ suite('WorkspaceContextService - Folder', () => { fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const uriIdentityService = new UriIdentityService(fileService); const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(undefined, undefined, TestProductService, logService), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); }); @@ -130,7 +130,7 @@ suite('WorkspaceContextService - Folder', () => { fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const uriIdentityService = new UriIdentityService(fileService); const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(undefined, undefined, TestProductService, logService), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a')); @@ -152,7 +152,7 @@ suite('WorkspaceContextService - Folder', () => { fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const uriIdentityService = new UriIdentityService(fileService); const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(undefined, undefined, TestProductService, logService), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index 0d0ecf8263e..37a6002a239 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -12,7 +12,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { ExtHostCustomersRegistry, IInternalExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { Proxied, ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; -import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { RemoteAuthorityResolverErrorCode, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as nls from 'vs/nls'; import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; @@ -27,7 +27,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionHostProxy, IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -import { getRemoteAuthorityPrefix } from 'vs/workbench/services/extensions/common/remoteExtensionHost'; // Enable to see detailed message communication between window and extension host const LOG_EXTENSION_HOST_COMMUNICATION = false; diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index 54d741463b1..6f317b0fc93 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -285,10 +285,3 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { } } -export function getRemoteAuthorityPrefix(remoteAuthority: string): string { - const plusIndex = remoteAuthority.indexOf('+'); - if (plusIndex === -1) { - return remoteAuthority; - } - return remoteAuthority.substring(0, plusIndex); -} diff --git a/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts index 3383507afea..e2f85a0b4ed 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts @@ -6,14 +6,15 @@ import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner'; import { AbstractExtensionService, ExtensionHostCrashTracker, ExtensionRunningPreference, extensionRunningPreferenceToString, filterByRunningLocation } from 'vs/workbench/services/extensions/common/abstractExtensionService'; import * as nls from 'vs/nls'; +import * as performance from 'vs/base/common/performance'; import { runWhenIdle } from 'vs/base/common/async'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData, getRemoteAuthorityPrefix } from 'vs/workbench/services/extensions/common/remoteExtensionHost'; +import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -424,15 +425,9 @@ export class NativeExtensionService extends AbstractExtensionService implements const MAX_ATTEMPTS = 5; for (let attempt = 1; ; attempt++) { - const sw = StopWatch.create(false); - this._logService.info(`[attempt ${attempt}] Invoking resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)})`); try { - const resolverResult = await this._resolveAuthority(remoteAuthority); - this._logService.info(`[attempt ${attempt}] resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned '${resolverResult.authority.host}:${resolverResult.authority.port}' after ${sw.elapsed()} ms`); - return resolverResult; + return this._resolveAuthorityWithLogging(remoteAuthority); } catch (err) { - this._logService.error(`[attempt ${attempt}] resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned an error after ${sw.elapsed()} ms`, err); - if (RemoteAuthorityResolverError.isNoResolverFound(err)) { // There is no point in retrying if there is no resolver found throw err; @@ -458,18 +453,31 @@ export class NativeExtensionService extends AbstractExtensionService implements } this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority); - const sw = StopWatch.create(false); - this._logService.info(`Invoking resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)})`); try { - const result = await this._resolveAuthority(remoteAuthority); - this._logService.info(`resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned '${result.authority.host}:${result.authority.port}' after ${sw.elapsed()} ms`); + const result = await this._resolveAuthorityWithLogging(remoteAuthority); this._remoteAuthorityResolverService._setResolvedAuthority(result.authority, result.options); } catch (err) { - this._logService.error(`resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned an error after ${sw.elapsed()} ms`, err); this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err); } } + private async _resolveAuthorityWithLogging(remoteAuthority: string): Promise { + const authorityPrefix = getRemoteAuthorityPrefix(remoteAuthority); + const sw = StopWatch.create(false); + this._logService.info(`Invoking resolveAuthority(${authorityPrefix})...`); + try { + performance.mark(`code/willResolveAuthority/${authorityPrefix}`); + const result = await this._resolveAuthority(remoteAuthority); + performance.mark(`code/didResolveAuthorityOK/${authorityPrefix}`); + this._logService.info(`resolveAuthority(${authorityPrefix}) returned '${result.authority.host}:${result.authority.port}' after ${sw.elapsed()} ms`); + return result; + } catch (err) { + performance.mark(`code/didResolveAuthorityError/${authorityPrefix}`); + this._logService.error(`resolveAuthority(${authorityPrefix}) returned an error after ${sw.elapsed()} ms`, err); + throw err; + } + } + protected async _scanAndHandleExtensions(): Promise { this._extensionScanner.startScanningExtensions(); @@ -485,12 +493,14 @@ export class NativeExtensionService extends AbstractExtensionService implements // The current remote authority resolver cannot give the canonical URI for this URI return uri; } + performance.mark(`code/willGetCanonicalURI/${getRemoteAuthorityPrefix(remoteAuthority)}`); if (isCI) { this._logService.info(`Invoking getCanonicalURI for authority ${getRemoteAuthorityPrefix(remoteAuthority)}...`); } try { return this._getCanonicalURI(remoteAuthority, uri); } finally { + performance.mark(`code/didGetCanonicalURI/${getRemoteAuthorityPrefix(remoteAuthority)}`); if (isCI) { this._logService.info(`getCanonicalURI returned for authority ${getRemoteAuthorityPrefix(remoteAuthority)}.`); } diff --git a/test/integration/browser/src/index.ts b/test/integration/browser/src/index.ts index 9dfc3899f66..169ae4b33f1 100644 --- a/test/integration/browser/src/index.ts +++ b/test/integration/browser/src/index.ts @@ -35,6 +35,12 @@ type BrowserType = 'chromium' | 'firefox' | 'webkit'; async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWithStringQuery, server: cp.ChildProcess): Promise { const browser = await playwright[browserType].launch({ headless: !Boolean(optimist.argv.debug) }); const context = await browser.newContext(); + try { + await context.grantPermissions([`clipboard-read`, `clipboard-write`]); // some tests need clipboard access + } catch (error) { + // ignore, seems to fail on Windows + } + const page = await context.newPage(); await page.setViewportSize({ width, height }); @@ -58,21 +64,6 @@ 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","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}`); - } - await page.exposeFunction('codeAutomationLog', (type: string, args: any[]) => { console[type](...args); }); @@ -92,6 +83,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) { diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index 8a985c791c9..87ada258bf4 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -133,13 +133,14 @@ async function runTestsInBrowser(testModules, browserType) { if (argv.build) { target.search = `?build=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 +177,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('&')}`;