/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import 'mocha'; import * as os from 'os'; import * as vscode from 'vscode'; import { closeAllEditors, delay, disposeAll } from '../utils'; const webviewId = 'myWebview'; function workspaceFile(...segments: string[]) { return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, ...segments); } const testDocument = workspaceFile('bower.json'); suite.skip('vscode API - webview', () => { const disposables: vscode.Disposable[] = []; function _register(disposable: T) { disposables.push(disposable); return disposable; } teardown(async () => { await closeAllEditors(); disposeAll(disposables); }); test('webviews should be able to send and receive messages', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); const firstResponse = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); webview.webview.postMessage({ value: 1 }); assert.strictEqual((await firstResponse).value, 2); }); test('webviews should not have scripts enabled by default', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, {})); const response = Promise.race([ getMesssage(webview), new Promise<{}>(resolve => setTimeout(() => resolve({ value: '🎉' }), 1000)) ]); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); assert.strictEqual((await response).value, '🎉'); }); test('webviews should update html', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); { const response = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); assert.strictEqual((await response).value, 'first'); } { const response = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); assert.strictEqual((await response).value, 'second'); } }); test.skip('webviews should preserve vscode API state when they are hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); await ready; const firstResponse = await sendRecieveMessage(webview, { type: 'add' }); assert.strictEqual(firstResponse.value, 1); // Swap away from the webview const doc = await vscode.workspace.openTextDocument(testDocument); await vscode.window.showTextDocument(doc); // And then back const ready2 = getMesssage(webview); webview.reveal(vscode.ViewColumn.One); await ready2; // We should still have old state const secondResponse = await sendRecieveMessage(webview, { type: 'get' }); assert.strictEqual(secondResponse.value, 1); }); test('webviews should preserve their context when they are moved between view columns', async () => { const doc = await vscode.workspace.openTextDocument(testDocument); await vscode.window.showTextDocument(doc, vscode.ViewColumn.One); // Open webview in same column const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); const ready = getMesssage(webview); webview.webview.html = statefulWebviewHtml; await ready; const firstResponse = await sendRecieveMessage(webview, { type: 'add' }); assert.strictEqual(firstResponse.value, 1); // Now move webview to new view column webview.reveal(vscode.ViewColumn.Two); // We should still have old state const secondResponse = await sendRecieveMessage(webview, { type: 'get' }); assert.strictEqual(secondResponse.value, 1); }); test('webviews with retainContextWhenHidden should preserve their context when they are hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); webview.webview.html = statefulWebviewHtml; await ready; const firstResponse = await sendRecieveMessage(webview, { type: 'add' }); assert.strictEqual((await firstResponse).value, 1); // Swap away from the webview const doc = await vscode.workspace.openTextDocument(testDocument); await vscode.window.showTextDocument(doc); // And then back webview.reveal(vscode.ViewColumn.One); // We should still have old state const secondResponse = await sendRecieveMessage(webview, { type: 'get' }); assert.strictEqual(secondResponse.value, 1); }); test('webviews with retainContextWhenHidden should preserve their page position when hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` ${'

Header

'.repeat(200)} `); await ready; const firstResponse = getMesssage(webview); assert.strictEqual(Math.round((await firstResponse).value), 100); // Swap away from the webview const doc = await vscode.workspace.openTextDocument(testDocument); await vscode.window.showTextDocument(doc); // And then back webview.reveal(vscode.ViewColumn.One); // We should still have old scroll pos const secondResponse = await sendRecieveMessage(webview, { type: 'get' }); assert.strictEqual(Math.round(secondResponse.value), 100); }); test('webviews with retainContextWhenHidden should be able to recive messages while hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); webview.webview.html = statefulWebviewHtml; await ready; const firstResponse = await sendRecieveMessage(webview, { type: 'add' }); assert.strictEqual((await firstResponse).value, 1); // Swap away from the webview const doc = await vscode.workspace.openTextDocument(testDocument); await vscode.window.showTextDocument(doc); // Try posting a message to our hidden webview const secondResponse = await sendRecieveMessage(webview, { type: 'add' }); assert.strictEqual((await secondResponse).value, 2); // Now show webview again webview.reveal(vscode.ViewColumn.One); // We should still have old state const thirdResponse = await sendRecieveMessage(webview, { type: 'get' }); assert.strictEqual(thirdResponse.value, 2); }); test.skip('webviews should only be able to load resources from workspace by default', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); const ready = getMesssage(webview); await ready; { const imagePath = webview.webview.asWebviewUri(workspaceFile('image.png')); const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); assert.strictEqual(response.value, true); } // { // // #102188. Resource filename containing special characters like '%', '#', '?'. // const imagePath = webview.webview.asWebviewUri(workspaceFile('image%02.png')); // const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); // assert.strictEqual(response.value, true); // } // { // // #102188. Resource filename containing special characters like '%', '#', '?'. // const imagePath = webview.webview.asWebviewUri(workspaceFile('image%.png')); // const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); // assert.strictEqual(response.value, true); // } { const imagePath = webview.webview.asWebviewUri(workspaceFile('no-such-image.png')); const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); assert.strictEqual(response.value, false); } { const imagePath = webview.webview.asWebviewUri(workspaceFile('..', '..', '..', 'resources', 'linux', 'code.png')); const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); assert.strictEqual(response.value, false); } }); test.skip('webviews should allow overriding allowed resource paths using localResourceRoots', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, localResourceRoots: [workspaceFile('sub')] })); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); { const response = sendRecieveMessage(webview, { src: webview.webview.asWebviewUri(workspaceFile('sub', 'image.png')).toString() }); assert.strictEqual((await response).value, true); } { const response = sendRecieveMessage(webview, { src: webview.webview.asWebviewUri(workspaceFile('image.png')).toString() }); assert.strictEqual((await response).value, false); } }); test.skip('webviews using hard-coded old style vscode-resource uri should work', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, localResourceRoots: [workspaceFile('sub')] })); const imagePath = workspaceFile('sub', 'image.png').with({ scheme: 'vscode-resource' }).toString(); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); const firstResponse = getMesssage(webview); assert.strictEqual((await firstResponse).value, true); }); test('webviews should have real view column after they are created, #56097', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.Active }, { enableScripts: true })); // Since we used a symbolic column, we don't know what view column the webview will actually show in at first assert.strictEqual(webview.viewColumn, undefined); let changed = false; const viewStateChanged = new Promise((resolve) => { webview.onDidChangeViewState(e => { if (changed) { throw new Error('Only expected a single view state change'); } changed = true; resolve(e); }, undefined, disposables); }); assert.strictEqual((await viewStateChanged).webviewPanel.viewColumn, vscode.ViewColumn.One); const firstResponse = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` `); webview.webview.postMessage({ value: 1 }); await firstResponse; assert.strictEqual(webview.viewColumn, vscode.ViewColumn.One); }); if (os.platform() === 'darwin') { test.skip('webview can copy text from webview', async () => { const expectedText = `webview text from: ${Date.now()}!`; const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` ${expectedText} `); await ready; await vscode.commands.executeCommand('editor.action.clipboardCopyAction'); await delay(200); // Make sure copy has time to reach webview assert.strictEqual(await vscode.env.clipboard.readText(), expectedText); }); } }); function createHtmlDocumentWithBody(body: string): string { return /*html*/` Document ${body} `; } const statefulWebviewHtml = createHtmlDocumentWithBody(/*html*/ ` `); function getMesssage(webview: vscode.WebviewPanel): Promise { return new Promise(resolve => { const sub = webview.webview.onDidReceiveMessage(message => { sub.dispose(); resolve(message); }); }); } function sendRecieveMessage(webview: vscode.WebviewPanel, message: T): Promise { const p = getMesssage(webview); webview.webview.postMessage(message); return p; }