From 076794c7c47fb99fb9f805f1aa8eee1659c6053b Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Wed, 9 Oct 2024 11:24:12 +0200 Subject: [PATCH] feat: add a button to toggle the browser preview in browser mode --- package.json | 15 ++++++ src/api.ts | 2 + src/config.ts | 3 ++ src/extension.ts | 7 ++- src/runner/runner.ts | 9 +++- src/settingsWebview.ts | 117 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 src/settingsWebview.ts diff --git a/package.json b/package.json index 11cd8222..fddf9f41 100644 --- a/package.json +++ b/package.json @@ -207,8 +207,23 @@ "markdownDescription": "The arguments to pass to the shell executable. This is applied only when `vitest.shellType` is `terminal`.", "type": "array", "scope": "resource" + }, + "vitest.previewBrowser": { + "markdownDescription": "Open the visible browser when running tests in the Browser Mode.", + "type": "boolean", + "default": false, + "scope": "resource" } } + }, + "views": { + "test": [ + { + "type": "webview", + "id": "vitest.webviewSettings", + "name": "Vitest" + } + ] } }, "scripts": { diff --git a/src/api.ts b/src/api.ts index 6da26658..225ef9d6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -177,6 +177,8 @@ export class VitestFolderAPI { } async cancelRun() { + if (this.process.closed) + return await this.meta.rpc.cancelRun() } diff --git a/src/config.ts b/src/config.ts index a35dbf72..a974c55a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -59,10 +59,13 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { const shellType = get<'child_process' | 'terminal'>('shellType', 'child_process') const nodeExecArgs = get('nodeExecArgs') + const previewBrowser = get('previewBrowser', false) + return { env: get>('nodeEnv', null), debugExclude: get('debugExclude', []), filesWatcherInclude, + previewBrowser, terminalShellArgs, terminalShellPath, shellType, diff --git a/src/extension.ts b/src/extension.ts index 0e51290c..56b8c0fa 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,9 +16,10 @@ import { TagsManager } from './tagsManager' import { coverageContext } from './coverage' import { debugTests } from './debug/api' import { VitestTerminalProcess } from './api/terminal' +import { SettingsWebview } from './settingsWebview' export async function activate(context: vscode.ExtensionContext) { - const extension = new VitestExtension() + const extension = new VitestExtension(context) context.subscriptions.push(extension) await extension.activate() } @@ -37,7 +38,7 @@ class VitestExtension { private disposables: vscode.Disposable[] = [] - constructor() { + constructor(private context: vscode.ExtensionContext) { log.info(`[v${version}] Vitest extension is activated because Vitest is installed or there is a Vite/Vitest config file in the workspace.`) this.testController = vscode.tests.createTestController(testControllerId, 'Vitest') @@ -279,6 +280,7 @@ class VitestExtension { 'vitest.terminalShellPath', ] + const settingsWebview = new SettingsWebview(this.context.extensionUri) this.disposables = [ vscode.workspace.onDidChangeConfiguration((event) => { if (reloadConfigNames.some(x => event.affectsConfiguration(x))) @@ -330,6 +332,7 @@ class VitestExtension { const tokenSource = new vscode.CancellationTokenSource() await profile.runHandler(request, tokenSource.token) }), + settingsWebview, ] // if the config changes, re-define all test profiles diff --git a/src/runner/runner.ts b/src/runner/runner.ts index e4ad4c48..26c11c76 100644 --- a/src/runner/runner.ts +++ b/src/runner/runner.ts @@ -225,7 +225,14 @@ export class TestRunner extends vscode.Disposable { log.verbose?.('Test run was cancelled manually for', join(request.include)) }) - await this.runTestItems(request) + try { + await this.runTestItems(request) + } + catch (err) { + this.endTestRun() + // TODO: do we need to show the error to the user? + log.error('Failed to run tests', err) + } this.nonContinuousRequest = undefined this._onRequestsExhausted.fire() diff --git a/src/settingsWebview.ts b/src/settingsWebview.ts new file mode 100644 index 00000000..3da9a096 --- /dev/null +++ b/src/settingsWebview.ts @@ -0,0 +1,117 @@ +import * as vscode from 'vscode' +import { getConfig } from './config' +import { nanoid } from './utils' + +export class SettingsWebview implements vscode.WebviewViewProvider, vscode.Disposable { + private disposables: vscode.Disposable[] + private view: vscode.WebviewView | undefined + + constructor( + private extensionUri: vscode.Uri, + ) { + this.disposables = [ + vscode.window.registerWebviewViewProvider('vitest.webviewSettings', this), + ] + } + + resolveWebviewView(webviewView: vscode.WebviewView, _context: vscode.WebviewViewResolveContext, _token: vscode.CancellationToken): Thenable | void { + this.view = webviewView + + webviewView.webview.options = { + enableScripts: true, + localResourceRoots: [this.extensionUri], + } + + webviewView.webview.html = createHtmlView(webviewView.webview) + + this.disposables.push( + // when we get the message from the view, process it + webviewView.webview.onDidReceiveMessage((message) => { + if (message.method === 'toggle') { + const settings = vscode.workspace.getConfiguration('vitest') + settings.update(message.args.setting, !settings.get(message.args.setting)) + } + }), + // when the user changes the configuration manually, update the view + vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('vitest')) { + this.updateSettings() + } + }), + // when the weview is opened, make sure it's up to date + webviewView.onDidChangeVisibility(() => { + if (!webviewView.visible) + return + this.updateSettings() + }), + ) + + this.updateSettings() + } + + updateSettings() { + this.view?.webview.postMessage({ + method: 'settings', + args: { + settings: getConfig(), + }, + }) + } + + dispose() { + this.disposables.forEach(d => d.dispose()) + this.disposables = [] + } +} + +// based on +// https://github.com/microsoft/playwright-vscode/blob/4454e6876bfde1b4a8570dbaeca1ad14e8cd37c8/src/settingsView.ts +function createHtmlView(webview: vscode.Webview) { + // + const nonce = nanoid() + return ` + + + + + + + Vitest + + +
+
+ +
+
+ + + + ` +}