Path: blob/main/src/vs/workbench/api/test/browser/extHostWebview.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { DisposableStore } from '../../../../base/common/lifecycle.js';7import { Schemas } from '../../../../base/common/network.js';8import { URI } from '../../../../base/common/uri.js';9import { mock } from '../../../../base/test/common/mock.js';10import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';11import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';12import { NullLogService } from '../../../../platform/log/common/log.js';13import { MainThreadWebviewManager } from '../../browser/mainThreadWebviewManager.js';14import { NullApiDeprecationService } from '../../common/extHostApiDeprecationService.js';15import { IExtHostRpcService } from '../../common/extHostRpcService.js';16import { ExtHostWebviews } from '../../common/extHostWebview.js';17import { ExtHostWebviewPanels } from '../../common/extHostWebviewPanels.js';18import { SingleProxyRPCProtocol } from '../common/testRPCProtocol.js';19import { decodeAuthority, webviewResourceBaseHost } from '../../../contrib/webview/common/webview.js';20import { EditorGroupColumn } from '../../../services/editor/common/editorGroupColumn.js';21import { IExtHostContext } from '../../../services/extensions/common/extHostCustomers.js';22import type * as vscode from 'vscode';2324suite('ExtHostWebview', () => {25let disposables: DisposableStore;26let rpcProtocol: (IExtHostRpcService & IExtHostContext) | undefined;2728setup(() => {29disposables = new DisposableStore();3031const shape = createNoopMainThreadWebviews();32rpcProtocol = SingleProxyRPCProtocol(shape);33});3435teardown(() => {36disposables.dispose();37});3839ensureNoDisposablesAreLeakedInTestSuite();4041function createWebview(rpcProtocol: (IExtHostRpcService & IExtHostContext) | undefined, remoteAuthority: string | undefined) {42const extHostWebviews = disposables.add(new ExtHostWebviews(rpcProtocol!, {43authority: remoteAuthority,44isRemote: !!remoteAuthority,45}, undefined, new NullLogService(), NullApiDeprecationService));4647const extHostWebviewPanels = disposables.add(new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined));4849return disposables.add(extHostWebviewPanels.createWebviewPanel({50extensionLocation: URI.from({51scheme: remoteAuthority ? Schemas.vscodeRemote : Schemas.file,52authority: remoteAuthority,53path: '/ext/path',54})55} as IExtensionDescription, 'type', 'title', 1, {}));56}5758test('Cannot register multiple serializers for the same view type', async () => {59const viewType = 'view.type';6061const extHostWebviews = disposables.add(new ExtHostWebviews(rpcProtocol!, { authority: undefined, isRemote: false }, undefined, new NullLogService(), NullApiDeprecationService));6263const extHostWebviewPanels = disposables.add(new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined));6465let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined;6667class NoopSerializer implements vscode.WebviewPanelSerializer {68async deserializeWebviewPanel(webview: vscode.WebviewPanel, _state: any): Promise<void> {69lastInvokedDeserializer = this;70disposables.add(webview);71}72}7374const extension = {} as IExtensionDescription;7576const serializerA = new NoopSerializer();77const serializerB = new NoopSerializer();7879const serializerARegistration = extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerA);8081await extHostWebviewPanels.$deserializeWebviewPanel('x', viewType, {82title: 'title',83state: {},84panelOptions: {},85webviewOptions: {},86active: true,87}, 0 as EditorGroupColumn);88assert.strictEqual(lastInvokedDeserializer, serializerA);8990assert.throws(91() => disposables.add(extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerB)),92'Should throw when registering two serializers for the same view');9394serializerARegistration.dispose();9596disposables.add(extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerB));9798await extHostWebviewPanels.$deserializeWebviewPanel('x', viewType, {99title: 'title',100state: {},101panelOptions: {},102webviewOptions: {},103active: true,104}, 0 as EditorGroupColumn);105assert.strictEqual(lastInvokedDeserializer, serializerB);106});107108test('asWebviewUri for local file paths', () => {109const webview = createWebview(rpcProtocol, /* remoteAuthority */undefined);110111assert.strictEqual(112(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString()),113`https://file%2B.vscode-resource.${webviewResourceBaseHost}/Users/codey/file.html`,114'Unix basic'115);116117assert.strictEqual(118(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString()),119`https://file%2B.vscode-resource.${webviewResourceBaseHost}/Users/codey/file.html#frag`,120'Unix should preserve fragment'121);122123assert.strictEqual(124(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString()),125`https://file%2B.vscode-resource.${webviewResourceBaseHost}/Users/codey/f%20ile.html`,126'Unix with encoding'127);128129assert.strictEqual(130(webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString()),131`https://file%2Blocalhost.vscode-resource.${webviewResourceBaseHost}/Users/codey/file.html`,132'Unix should preserve authority'133);134135assert.strictEqual(136(webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString()),137`https://file%2B.vscode-resource.${webviewResourceBaseHost}/c%3A/codey/file.txt`,138'Windows C drive'139);140});141142test('asWebviewUri for remote file paths', () => {143const webview = createWebview(rpcProtocol, /* remoteAuthority */ 'remote');144145assert.strictEqual(146(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString()),147`https://vscode-remote%2Bremote.vscode-resource.${webviewResourceBaseHost}/Users/codey/file.html`,148'Unix basic'149);150});151152test('asWebviewUri for remote with / and + in name', () => {153const webview = createWebview(rpcProtocol, /* remoteAuthority */ 'remote');154const authority = 'ssh-remote+localhost=foo/bar';155156const sourceUri = URI.from({157scheme: 'vscode-remote',158authority: authority,159path: '/Users/cody/x.png'160});161162const webviewUri = webview.webview.asWebviewUri(sourceUri);163assert.strictEqual(164webviewUri.toString(),165`https://vscode-remote%2Bssh-002dremote-002blocalhost-003dfoo-002fbar.vscode-resource.vscode-cdn.net/Users/cody/x.png`,166'Check transform');167168assert.strictEqual(169decodeAuthority(webviewUri.authority),170`vscode-remote+${authority}.vscode-resource.vscode-cdn.net`,171'Check decoded authority'172);173});174175test('asWebviewUri for remote with port in name', () => {176const webview = createWebview(rpcProtocol, /* remoteAuthority */ 'remote');177const authority = 'localhost:8080';178179const sourceUri = URI.from({180scheme: 'vscode-remote',181authority: authority,182path: '/Users/cody/x.png'183});184185const webviewUri = webview.webview.asWebviewUri(sourceUri);186assert.strictEqual(187webviewUri.toString(),188`https://vscode-remote%2Blocalhost-003a8080.vscode-resource.vscode-cdn.net/Users/cody/x.png`,189'Check transform');190191assert.strictEqual(192decodeAuthority(webviewUri.authority),193`vscode-remote+${authority}.vscode-resource.vscode-cdn.net`,194'Check decoded authority'195);196});197});198199200function createNoopMainThreadWebviews() {201return new class extends mock<MainThreadWebviewManager>() {202$disposeWebview() { /* noop */ }203$createWebviewPanel() { /* noop */ }204$registerSerializer() { /* noop */ }205$unregisterSerializer() { /* noop */ }206};207}208209210