From d59c618603a39af87f66194b8a53420afba1a4f8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 13 Mar 2020 08:19:33 -0700 Subject: [PATCH] Add unit test for link handler Ideally there would be an integration test as well but currently it's not possible as there's no extension API to activate links within the terminal --- .../terminal/browser/terminalLinkHandler.ts | 22 +++++----- .../test/browser/terminalLinkHandler.test.ts | 41 ++++++++++++++++++- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts index 2260378c8be..c323e7923ae 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts @@ -60,7 +60,7 @@ const CUSTOM_LINK_PRIORITY = -1; /** Lowest */ const LOCAL_LINK_PRIORITY = -2; -export type XtermLinkMatcherHandler = (event: MouseEvent, link: string) => boolean | void; +export type XtermLinkMatcherHandler = (event: MouseEvent, link: string) => Promise; export type XtermLinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; interface IPath { @@ -77,6 +77,9 @@ export class TerminalLinkHandler extends DisposableStore { private readonly _leaveCallback: () => void; private _hasBeforeHandleLinkListeners = false; + protected static _LINK_INTERCEPT_THRESHOLD = LINK_INTERCEPT_THRESHOLD; + public static readonly LINK_INTERCEPT_THRESHOLD = TerminalLinkHandler._LINK_INTERCEPT_THRESHOLD; + private readonly _onBeforeHandleLink = this.add(new Emitter({ onFirstListenerAdd: () => this._hasBeforeHandleLinkListeners = true, onLastListenerRemove: () => this._hasBeforeHandleLinkListeners = false @@ -227,8 +230,8 @@ export class TerminalLinkHandler extends DisposableStore { this._xterm.registerLinkMatcher(this._gitDiffPostImagePattern, wrappedHandler, options); } - private _wrapLinkHandler(handler: (link: string) => void): XtermLinkMatcherHandler { - return (event: MouseEvent, link: string) => { + protected _wrapLinkHandler(handler: (link: string) => void): XtermLinkMatcherHandler { + return async (event: MouseEvent, link: string) => { // Prevent default electron link handling so Alt+Click mode works normally event.preventDefault(); // Require correct modifier on click @@ -238,12 +241,12 @@ export class TerminalLinkHandler extends DisposableStore { // Allow the link to be intercepted if there are listeners if (this._hasBeforeHandleLinkListeners) { - new Promise(r => { + const wasHandled = await new Promise(r => { const timeoutId = setTimeout(() => { canceled = true; this._logService.error('An extension intecepted a terminal link but did not return'); r(false); - }, LINK_INTERCEPT_THRESHOLD); + }, TerminalLinkHandler.LINK_INTERCEPT_THRESHOLD); let canceled = false; const resolve = (handled: boolean) => { if (!canceled) { @@ -252,11 +255,10 @@ export class TerminalLinkHandler extends DisposableStore { } }; this._onBeforeHandleLink.fire({ link, resolve }); - }).then(wasHandled => { - if (!wasHandled) { - handler(link); - } }); + if (!wasHandled) { + handler(link); + } return; } @@ -307,7 +309,7 @@ export class TerminalLinkHandler extends DisposableStore { this._openerService.open(url, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) }); } - private _isLinkActivationModifierDown(event: MouseEvent): boolean { + protected _isLinkActivationModifierDown(event: MouseEvent): boolean { const editorConf = this._configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); if (editorConf.multiCursorModifier === 'ctrlCmd') { return !!event.altKey; diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalLinkHandler.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalLinkHandler.test.ts index 5e114e9d60a..e4c352297b3 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalLinkHandler.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalLinkHandler.test.ts @@ -5,11 +5,12 @@ import * as assert from 'assert'; import { OperatingSystem } from 'vs/base/common/platform'; -import { TerminalLinkHandler, LineColumnInfo } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; +import { TerminalLinkHandler, LineColumnInfo, XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import * as strings from 'vs/base/common/strings'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { Event } from 'vs/base/common/event'; import { ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; class TestTerminalLinkHandler extends TerminalLinkHandler { public get localLinkRegex(): RegExp { @@ -24,6 +25,13 @@ class TestTerminalLinkHandler extends TerminalLinkHandler { public preprocessPath(link: string): string | null { return this._preprocessPath(link); } + protected _isLinkActivationModifierDown(event: MouseEvent): boolean { + return true; + } + public wrapLinkHandler(handler: (link: string) => void): XtermLinkMatcherHandler { + TerminalLinkHandler._LINK_INTERCEPT_THRESHOLD = 0; + return this._wrapLinkHandler(handler); + } } class TestXterm { @@ -302,4 +310,35 @@ suite('Workbench - TerminalLinkHandler', () => { assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null'), false); assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null '), false); }); + + suite.only('wrapLinkHandler', () => { + const nullMouseEvent: any = Object.freeze({ preventDefault: () => { } }); + + test('should allow intercepting of links with onBeforeHandleLink', async () => { + const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, { + os: OperatingSystem.Linux, + userHome: '' + } as any, testConfigHelper, null!, null!, new TestConfigurationService(), new MockTerminalInstanceService(), null!, null!); + linkHandler.onBeforeHandleLink(e => { + if (e.link === 'https://www.microsoft.com') { + intercepted = true; + e.resolve(true); + } + e.resolve(false); + }); + const wrappedHandler = linkHandler.wrapLinkHandler(() => defaultHandled = true); + + let defaultHandled = false; + let intercepted = false; + await wrappedHandler(nullMouseEvent, 'https://www.visualstudio.com'); + assert.equal(intercepted, false); + assert.equal(defaultHandled, true); + + defaultHandled = false; + intercepted = false; + await wrappedHandler(nullMouseEvent, 'https://www.microsoft.com'); + assert.equal(intercepted, true); + assert.equal(defaultHandled, false); + }); + }); }); -- GitLab