diff --git a/.eslint-allowed-bracket-notation-files b/.eslint-allowed-bracket-notation-files index 4f06ad658e78f..2006c3430f333 100644 --- a/.eslint-allowed-bracket-notation-files +++ b/.eslint-allowed-bracket-notation-files @@ -463,18 +463,8 @@ src/vs/workbench/services/telemetry/common/workbenchCommonProperties.ts src/vs/workbench/services/telemetry/test/browser/commonProperties.test.ts src/vs/workbench/services/telemetry/test/node/commonProperties.test.ts -# Remote, tunnels, networking, and authentication (11 files) +# GitHub authentication service tests (1 file) extensions/github-authentication/src/test/github.test.ts -extensions/microsoft-authentication/src/common/experimentation.ts -src/vs/platform/github/common/githubTransport.ts -src/vs/platform/remote/node/wsl.ts -src/vs/platform/request/node/requestService.ts -src/vs/platform/tunnel/node/tunnelProxy.ts -src/vs/platform/tunnel/test/node/tunnelProxy.test.ts -src/vs/platform/webContentExtractor/electron-main/webPageLoader.ts -src/vs/platform/webContentExtractor/test/electron-main/webPageLoader.test.ts -src/vs/workbench/contrib/remote/browser/remoteStartEntry.ts -src/vs/workbench/contrib/remoteTunnel/test/electron-browser/remoteTunnel.contribution.test.ts # Language feature extensions (22 files) extensions/css-language-features/client/src/node/cssClientMain.ts diff --git a/extensions/microsoft-authentication/src/common/experimentation.ts b/extensions/microsoft-authentication/src/common/experimentation.ts index dd383c4f040d4..5ed398a4d90f7 100644 --- a/extensions/microsoft-authentication/src/common/experimentation.ts +++ b/extensions/microsoft-authentication/src/common/experimentation.ts @@ -11,7 +11,7 @@ export async function createExperimentationService( isPreRelease: boolean, ): Promise { const id = context.extension.id; - const version = context.extension.packageJSON['version']; + const version = context.extension.packageJSON.version; const service = getExperimentationService( id, diff --git a/src/vs/platform/github/common/githubTransport.ts b/src/vs/platform/github/common/githubTransport.ts index a7b5779fafca8..a801cd65925e8 100644 --- a/src/vs/platform/github/common/githubTransport.ts +++ b/src/vs/platform/github/common/githubTransport.ts @@ -215,7 +215,7 @@ export class GitHubTransport extends Disposable implements IGitHubTransport { 'X-GitHub-Api-Version': defaultApiVersion, }; if (authenticated) { - headers['Authorization'] = `Bearer ${token}`; + headers.Authorization = `Bearer ${token}`; } let response: Response; try { diff --git a/src/vs/platform/remote/node/wsl.ts b/src/vs/platform/remote/node/wsl.ts index 60b96f3dd789a..b233f27aed3c2 100644 --- a/src/vs/platform/remote/node/wsl.ts +++ b/src/vs/platform/remote/node/wsl.ts @@ -48,7 +48,7 @@ async function testWSLFeatureInstalled(): Promise { } function getSystem32Path(subPath: string): string | undefined { - const systemRoot = process.env['SystemRoot']; + const systemRoot = process.env.SystemRoot; if (systemRoot) { const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); return join(systemRoot, is32ProcessOn64Windows ? 'Sysnative' : 'System32', subPath); diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index 452097590904f..6757600814fb0 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -225,10 +225,10 @@ async function nodeRequestAttempt(options: NodeRequestOptions, token: Cancellati const req = rawRequest(opts, (res: http.IncomingMessage) => { const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3; - if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { + if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers.location) { nodeRequest({ ...options, - url: res.headers['location'], + url: res.headers.location, followRedirects: followRedirects - 1 }, token).then(resolve, reject); } else { diff --git a/src/vs/platform/tunnel/node/tunnelProxy.ts b/src/vs/platform/tunnel/node/tunnelProxy.ts index e6386214be4c6..648cb3d7840fe 100644 --- a/src/vs/platform/tunnel/node/tunnelProxy.ts +++ b/src/vs/platform/tunnel/node/tunnelProxy.ts @@ -345,7 +345,7 @@ export class TunnelProxy extends Disposable { // An intermediary MUST parse the Connection header and remove any // fields named in it, then remove Connection itself. It SHOULD // also remove other known hop-by-hop headers. - const connectionTokens = (headers['connection'] ?? '') + const connectionTokens = (headers.connection ?? '') .toString() .split(',') .map(t => t.trim().toLowerCase()) @@ -353,13 +353,13 @@ export class TunnelProxy extends Disposable { for (const token of connectionTokens) { delete headers[token]; } - delete headers['connection']; + delete headers.connection; delete headers['keep-alive']; delete headers['proxy-authorization']; delete headers['proxy-connection']; - delete headers['te']; + delete headers.te; delete headers['transfer-encoding']; - delete headers['upgrade']; + delete headers.upgrade; const proxyReq = http.request({ agent: this._tunnelAgent, @@ -472,12 +472,12 @@ export class TunnelProxy extends Disposable { // Handle IPv6 bracket notation [::1]:port const bracketMatch = /^\[(?[^\]]+)\]:(?\d+)$/.exec(address); if (bracketMatch?.groups) { - host = bracketMatch.groups['host']; - port = parseInt(bracketMatch.groups['port'], 10); + host = bracketMatch.groups.host; + port = parseInt(bracketMatch.groups.port, 10); } else { const bracketOnly = /^\[(?[^\]]+)\]$/.exec(address); if (bracketOnly?.groups) { - host = bracketOnly.groups['host']; + host = bracketOnly.groups.host; port = defaultPort; } else { const lastColon = address.lastIndexOf(':'); diff --git a/src/vs/platform/tunnel/test/node/tunnelProxy.test.ts b/src/vs/platform/tunnel/test/node/tunnelProxy.test.ts index d7fb75ac93768..a94b18be17ccc 100644 --- a/src/vs/platform/tunnel/test/node/tunnelProxy.test.ts +++ b/src/vs/platform/tunnel/test/node/tunnelProxy.test.ts @@ -342,8 +342,8 @@ suite('TunnelProxy', () => { assert.strictEqual(forwarded['proxy-authorization'], undefined); assert.strictEqual(forwarded['proxy-connection'], undefined); assert.strictEqual(forwarded['keep-alive'], undefined); - assert.strictEqual(forwarded['te'], undefined); - assert.strictEqual(forwarded['upgrade'], undefined); + assert.strictEqual(forwarded.te, undefined); + assert.strictEqual(forwarded.upgrade, undefined); // Headers named in Connection must also be removed assert.strictEqual(forwarded['x-custom-hop'], undefined); // Note: connection itself is replaced by Node's http.Agent with diff --git a/src/vs/platform/webContentExtractor/electron-main/webPageLoader.ts b/src/vs/platform/webContentExtractor/electron-main/webPageLoader.ts index ee54dbf6fb45f..5cfacf02c3f37 100644 --- a/src/vs/platform/webContentExtractor/electron-main/webPageLoader.ts +++ b/src/vs/platform/webContentExtractor/electron-main/webPageLoader.ts @@ -200,13 +200,13 @@ export class WebPageLoader extends Disposable { const headers = { ...details.requestHeaders }; // Request privacy for web-sites that respect these. - headers['DNT'] = '1'; + headers.DNT = '1'; headers['Sec-GPC'] = '1'; // For the main document request, prefer markdown responses from sites that // support agent-friendly content negotiation (e.g. Microsoft Learn, Cloudflare docs). if (details.resourceType === 'mainFrame') { - headers['Accept'] = 'text/markdown, text/html;q=0.9, application/xhtml+xml;q=0.9, application/xml;q=0.8, */*;q=0.7'; + headers.Accept = 'text/markdown, text/html;q=0.9, application/xhtml+xml;q=0.9, application/xml;q=0.8, */*;q=0.7'; } callback({ requestHeaders: headers }); diff --git a/src/vs/platform/webContentExtractor/test/electron-main/webPageLoader.test.ts b/src/vs/platform/webContentExtractor/test/electron-main/webPageLoader.test.ts index 328ea15a32ba9..22ad51b1d0537 100644 --- a/src/vs/platform/webContentExtractor/test/electron-main/webPageLoader.test.ts +++ b/src/vs/platform/webContentExtractor/test/electron-main/webPageLoader.test.ts @@ -1402,11 +1402,11 @@ suite('WebPageLoader', () => { // Verify privacy headers were added assert.ok(modifiedHeaders); - assert.strictEqual(modifiedHeaders['DNT'], '1'); + assert.strictEqual(modifiedHeaders.DNT, '1'); assert.strictEqual(modifiedHeaders['Sec-GPC'], '1'); - assert.strictEqual(modifiedHeaders['TestHeader'], 'TestValue'); + assert.strictEqual(modifiedHeaders.TestHeader, 'TestValue'); // Accept header should NOT be set for non-mainFrame requests - assert.strictEqual(modifiedHeaders['Accept'], undefined); + assert.strictEqual(modifiedHeaders.Accept, undefined); }); test('onBeforeSendHeaders adds Accept header preferring markdown for mainFrame requests', () => { @@ -1431,8 +1431,8 @@ suite('WebPageLoader', () => { ); assert.ok(modifiedHeaders); - assert.ok(modifiedHeaders['Accept']?.includes('text/markdown')); - assert.ok(modifiedHeaders['Accept']?.includes('text/html')); + assert.ok(modifiedHeaders.Accept?.includes('text/markdown')); + assert.ok(modifiedHeaders.Accept?.includes('text/html')); }); //#endregion diff --git a/src/vs/workbench/contrib/remote/browser/remoteStartEntry.ts b/src/vs/workbench/contrib/remote/browser/remoteStartEntry.ts index c21a608a5ae43..aa69e455c576f 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteStartEntry.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteStartEntry.ts @@ -34,7 +34,7 @@ export class RemoteStartEntry extends Disposable implements IWorkbenchContributi super(); - const remoteExtensionTips = this.productService.remoteExtensionTips?.['tunnel']; + const remoteExtensionTips = this.productService.remoteExtensionTips?.tunnel; this.startCommand = remoteExtensionTips?.startEntry?.startCommand ?? ''; this.remoteExtensionId = remoteExtensionTips?.extensionId ?? ''; diff --git a/src/vs/workbench/contrib/remoteTunnel/test/electron-browser/remoteTunnel.contribution.test.ts b/src/vs/workbench/contrib/remoteTunnel/test/electron-browser/remoteTunnel.contribution.test.ts index 38d0538a192ab..5111bb86318d5 100644 --- a/src/vs/workbench/contrib/remoteTunnel/test/electron-browser/remoteTunnel.contribution.test.ts +++ b/src/vs/workbench/contrib/remoteTunnel/test/electron-browser/remoteTunnel.contribution.test.ts @@ -10,8 +10,11 @@ import { ITunnelApplicationConfig } from '../../../../../base/common/product.js' import { URI } from '../../../../../base/common/uri.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; +import { CommandsRegistry, ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; import { INativeEnvironmentService } from '../../../../../platform/environment/common/environment.js'; +import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js'; import { NullLoggerService } from '../../../../../platform/log/common/log.js'; import { INotificationService } from '../../../../../platform/notification/common/notification.js'; @@ -19,12 +22,11 @@ import { IProductService } from '../../../../../platform/product/common/productS import { IProgress, IProgressService, IProgressStep } from '../../../../../platform/progress/common/progress.js'; import { IQuickInputService, IQuickPick, IQuickPickItem } from '../../../../../platform/quickinput/common/quickInput.js'; import { INACTIVE_TUNNEL_MODE, IRemoteTunnelService, type ActiveTunnelMode, type TunnelStatus } from '../../../../../platform/remoteTunnel/common/remoteTunnel.js'; -import { InMemoryStorageService } from '../../../../../platform/storage/common/storage.js'; +import { InMemoryStorageService, IStorageService } from '../../../../../platform/storage/common/storage.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; import { IAuthenticationProvider, AuthenticationSession, IAuthenticationService } from '../../../../services/authentication/common/authentication.js'; -import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IExtensionService } from '../../../../services/extensions/common/extensions.js'; -import { RemoteTunnelWorkbenchContribution } from '../../electron-browser/remoteTunnel.contribution.js'; +import { RemoteTunnelCommandIds, RemoteTunnelWorkbenchContribution } from '../../electron-browser/remoteTunnel.contribution.js'; const tunnelApplicationConfig: ITunnelApplicationConfig = { authenticationProviders: { @@ -134,31 +136,62 @@ class TestProgressService extends mock() { } } -class TestDialogService extends mock() { } +class TestDialogService extends mock() { + override async confirm(): Promise<{ confirmed: boolean }> { + return { confirmed: true }; + } +} +class TestClipboardService extends mock() { } class TestCommandService extends mock() { } class TestWorkspaceContextService extends mock() { } class TestNotificationService extends mock() { } -function createContribution(store: Pick, authenticationService: TestAuthenticationService, quickInputService: TestQuickInputService, remoteTunnelService: TestRemoteTunnelService): RemoteTunnelWorkbenchContribution { - return store.add(new RemoteTunnelWorkbenchContribution( +function createContribution(store: Pick, authenticationService: TestAuthenticationService, quickInputService: TestQuickInputService, remoteTunnelService: TestRemoteTunnelService): TestInstantiationService { + const dialogService = new TestDialogService(); + const productService = new class extends mock() { + override readonly tunnelApplicationName = 'Code'; + override readonly tunnelApplicationConfig = tunnelApplicationConfig; + }; + const storageService = store.add(new InMemoryStorageService()); + const commandService = new TestCommandService(); + const notificationService = new TestNotificationService(); + + store.add(new RemoteTunnelWorkbenchContribution( authenticationService, - new TestDialogService(), + dialogService, new TestExtensionService(), store.add(new MockContextKeyService()), - new class extends mock() { - override readonly tunnelApplicationName = 'Code'; - override readonly tunnelApplicationConfig = tunnelApplicationConfig; - }, - store.add(new InMemoryStorageService()), + productService, + storageService, store.add(new NullLoggerService()), quickInputService, new TestEnvironmentService(), remoteTunnelService, - new TestCommandService(), + commandService, new TestWorkspaceContextService(), new TestProgressService(), - new TestNotificationService(), + notificationService, )); + + const instantiationService = store.add(new TestInstantiationService()); + instantiationService.set(INotificationService, notificationService); + instantiationService.set(IClipboardService, new TestClipboardService()); + instantiationService.set(ICommandService, commandService); + instantiationService.set(IStorageService, storageService); + instantiationService.set(IDialogService, dialogService); + instantiationService.set(IQuickInputService, quickInputService); + instantiationService.set(IProductService, productService); + return instantiationService; +} + +async function startTunnel(instantiationService: TestInstantiationService): Promise { + const command = CommandsRegistry.getCommand(RemoteTunnelCommandIds.turnOn); + assert.ok(command); + await instantiationService.invokeFunction(command.handler, { + authenticationProviderId: 'github', + showServiceOption: false, + showSuccessNotification: false, + }); } suite('RemoteTunnelWorkbenchContribution', () => { @@ -168,9 +201,9 @@ suite('RemoteTunnelWorkbenchContribution', () => { const authenticationService = new TestAuthenticationService([githubSession]); const quickInputService = new TestQuickInputService(); const remoteTunnelService = new TestRemoteTunnelService(); - const contribution = createContribution(store, authenticationService, quickInputService, remoteTunnelService); + const instantiationService = createContribution(store, authenticationService, quickInputService, remoteTunnelService); - await contribution['startTunnel'](false, 'github'); + await startTunnel(instantiationService); assert.deepStrictEqual({ quickPickCalls: quickInputService.createQuickPickCalls, @@ -198,9 +231,9 @@ suite('RemoteTunnelWorkbenchContribution', () => { const authenticationService = new TestAuthenticationService([]); const quickInputService = new TestQuickInputService(); const remoteTunnelService = new TestRemoteTunnelService(); - const contribution = createContribution(store, authenticationService, quickInputService, remoteTunnelService); + const instantiationService = createContribution(store, authenticationService, quickInputService, remoteTunnelService); - await contribution['startTunnel'](false, 'github'); + await startTunnel(instantiationService); assert.deepStrictEqual({ quickPickCalls: quickInputService.createQuickPickCalls,