From 35ac5ba880e0036da6a78f39e90898fad80b143a Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 08:38:59 -0800 Subject: [PATCH 01/21] basic implementation from prototype --- .../interactiveWindowTypes.ts | 6 +++- .../interactive-common/synchronization.ts | 2 ++ src/client/datascience/variablesView/types.ts | 7 ++++- .../variablesView/variableViewProvider.ts | 10 ++++++ .../datascience/webviews/webviewHost.ts | 31 +++++++++++++++++++ .../redux/reducers/commonEffects.ts | 9 ++++++ .../variable-view/redux/reducers/index.ts | 3 +- 7 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/client/datascience/interactive-common/interactiveWindowTypes.ts b/src/client/datascience/interactive-common/interactiveWindowTypes.ts index b654b2003e9..8b02ff41bf5 100644 --- a/src/client/datascience/interactive-common/interactiveWindowTypes.ts +++ b/src/client/datascience/interactive-common/interactiveWindowTypes.ts @@ -150,7 +150,9 @@ export enum InteractiveWindowMessages { GetCellCode = 'get_cell_code', ReturnCellCode = 'return_cell_code', GetAllCellCode = 'get_all_cell_code', - ReturnAllCellCode = 'return_all_cell_code' + ReturnAllCellCode = 'return_all_cell_code', + GetHTMLByIdRequest = 'get_html_by_id_request', + GetHTMLByIdResponse = 'get_html_by_id_response' } export enum IPyWidgetMessages { @@ -715,4 +717,6 @@ export class IInteractiveWindowMapping { public [InteractiveWindowMessages.HasCellResponse]: { id: string; result: boolean }; public [InteractiveWindowMessages.UpdateExternalCellButtons]: IExternalWebviewCellButton[]; public [InteractiveWindowMessages.ExecuteExternalCommand]: IExternalCommandFromWebview; + public [InteractiveWindowMessages.GetHTMLByIdRequest]: string; + public [InteractiveWindowMessages.GetHTMLByIdResponse]: string; } diff --git a/src/client/datascience/interactive-common/synchronization.ts b/src/client/datascience/interactive-common/synchronization.ts index 9726a4b4fea..7bd5858d650 100644 --- a/src/client/datascience/interactive-common/synchronization.ts +++ b/src/client/datascience/interactive-common/synchronization.ts @@ -225,6 +225,8 @@ const messageWithMessageTypes: MessageMapping & Messa [InteractiveWindowMessages.ConvertUriForUseInWebViewResponse]: MessageType.other, [InteractiveWindowMessages.UpdateExternalCellButtons]: MessageType.other, [InteractiveWindowMessages.ExecuteExternalCommand]: MessageType.other, + [InteractiveWindowMessages.GetHTMLByIdRequest]: MessageType.other, + [InteractiveWindowMessages.GetHTMLByIdResponse]: MessageType.other, // Types from CssMessages [CssMessages.GetCssRequest]: MessageType.other, [CssMessages.GetCssResponse]: MessageType.other, diff --git a/src/client/datascience/variablesView/types.ts b/src/client/datascience/variablesView/types.ts index 2b938ffbc70..539a9ed7952 100644 --- a/src/client/datascience/variablesView/types.ts +++ b/src/client/datascience/variablesView/types.ts @@ -7,6 +7,7 @@ import { } from '../../datascience/interactive-common/interactiveWindowTypes'; import { CssMessages, IGetCssRequest, IGetCssResponse, SharedMessages } from '../messages'; import { IJupyterVariablesRequest, IJupyterVariablesResponse, INotebook, IVSCWebviewViewProvider } from '../types'; +import { VariableView } from './variableView'; // Mapping of Message to payload that our VariableViewPanel needs to support export class IVariableViewPanelMapping { @@ -27,6 +28,8 @@ export class IVariableViewPanelMapping { public [SharedMessages.LocInit]: string; public [InteractiveWindowMessages.FinishCell]: IFinishCell; public [InteractiveWindowMessages.UpdateVariableViewExecutionCount]: { executionCount: number }; + public [InteractiveWindowMessages.GetHTMLByIdRequest]: string; + public [InteractiveWindowMessages.GetHTMLByIdResponse]: string; } export const INotebookWatcher = Symbol('INotebookWatcher'); @@ -37,4 +40,6 @@ export interface INotebookWatcher { } export const IVariableViewProvider = Symbol('IVariableViewProvider'); -export interface IVariableViewProvider extends IVSCWebviewViewProvider {} +export interface IVariableViewProvider extends IVSCWebviewViewProvider { + activeVariableView?: VariableView; +} diff --git a/src/client/datascience/variablesView/variableViewProvider.ts b/src/client/datascience/variablesView/variableViewProvider.ts index f939e524ff6..ee3809afb96 100644 --- a/src/client/datascience/variablesView/variableViewProvider.ts +++ b/src/client/datascience/variablesView/variableViewProvider.ts @@ -5,6 +5,7 @@ import { inject, injectable, named } from 'inversify'; import { CancellationToken, WebviewView, WebviewViewResolveContext } from 'vscode'; import { IApplicationShell, IWebviewViewProvider, IWorkspaceService } from '../../common/application/types'; +import { isTestExecution } from '../../common/constants'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; import { Identifiers } from '../constants'; import { IDataViewerFactory } from '../data-viewing/types'; @@ -17,6 +18,15 @@ import { VariableView } from './variableView'; export class VariableViewProvider implements IVariableViewProvider { public readonly viewType = 'jupyterViewVariables'; + // Just for test execution allow for accessing the VariableView off of the provider + public get activeVariableView(): VariableView | undefined { + if (!isTestExecution()) { + throw new Error('activeVariableView only accessible from test code'); + } + + return this.variableView; + } + private variableView?: VariableView; constructor( diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index 986e71f2d86..c8ba91854e5 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -24,6 +24,7 @@ import * as localize from '../../common/utils/localize'; import { StopWatch } from '../../common/utils/stopWatch'; import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; import { DefaultTheme, PythonExtension, Telemetry } from '../constants'; +import { InteractiveWindowMessages } from '../interactive-common/interactiveWindowTypes'; import { CssMessages, IGetCssRequest, IGetMonacoThemeRequest, SharedMessages } from '../messages'; import { ICodeCssGenerator, IJupyterExtraSettings, IThemeFinder } from '../types'; @@ -42,6 +43,10 @@ export abstract class WebviewHost implements IDisposable { protected readonly _disposables: IDisposable[] = []; private startupStopwatch = new StopWatch(); + + // For testing, holds the current request for webview HTML + private activeHTMLRequest?: Deferred; + constructor( @unmanaged() protected configService: IConfigurationService, @unmanaged() private cssGenerator: ICodeCssGenerator, @@ -79,6 +84,25 @@ export abstract class WebviewHost implements IDisposable { } } + // This function is used for testing webview by fetching HTML from the webview + // only to be use for testing + public getHTMLById(id: string): Promise { + // Test only + if (!isTestExecution()) { + throw new Error('getHTMLById to be run only in test code'); + } + + if (!this.activeHTMLRequest) { + this.activeHTMLRequest = createDeferred(); + this.postMessageInternal(InteractiveWindowMessages.GetHTMLByIdRequest, id).ignoreErrors(); + } else { + // No localization for test only fuction + throw new Error('getHTMLById request already in progress'); + } + + return this.activeHTMLRequest.promise; + } + protected abstract provideWebview( cwd: string, settings: IJupyterExtraSettings, @@ -120,6 +144,13 @@ export abstract class WebviewHost implements IDisposable { this.handleMonacoThemeRequest(payload as IGetMonacoThemeRequest).ignoreErrors(); break; + case InteractiveWindowMessages.GetHTMLByIdResponse: + if (this.activeHTMLRequest) { + this.activeHTMLRequest.resolve(payload); + this.activeHTMLRequest = undefined; + } + break; + default: break; } diff --git a/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts b/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts index 14220dc1c11..7089ae67249 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts @@ -318,4 +318,13 @@ export namespace CommonEffects { externalButtons: arg.payload.data }; } + + export function getHTMLByIdRequest(arg: CommonReducerArg): IMainState { + const element = document.getElementById(arg.payload.data); + + if (element) { + postActionToExtension(arg, InteractiveWindowMessages.GetHTMLByIdResponse, element.innerHTML); + } + return arg.prevState; + } } diff --git a/src/datascience-ui/variable-view/redux/reducers/index.ts b/src/datascience-ui/variable-view/redux/reducers/index.ts index 20b3378c362..be8e824ff9e 100644 --- a/src/datascience-ui/variable-view/redux/reducers/index.ts +++ b/src/datascience-ui/variable-view/redux/reducers/index.ts @@ -17,5 +17,6 @@ export const reducerMap: Partial = { [CssMessages.GetCssResponse]: CommonEffects.handleCss, [SharedMessages.UpdateSettings]: Effects.updateSettings, [SharedMessages.LocInit]: CommonEffects.handleLocInit, - [CommonActionType.VARIABLE_VIEW_LOADED]: Transfer.variableViewStarted + [CommonActionType.VARIABLE_VIEW_LOADED]: Transfer.variableViewStarted, + [InteractiveWindowMessages.GetHTMLByIdRequest]: CommonEffects.getHTMLByIdRequest }; From 0326e0e36b5ed27cf8e73333fa8b6bb21a277b56 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 08:58:06 -0800 Subject: [PATCH 02/21] TESTCOMMAND hooked up and working --- package.json | 7 ++++++- src/client/common/application/commands.ts | 1 + .../datascience/commands/commandRegistry.ts | 17 ++++++++++++++++- src/client/datascience/constants.ts | 1 + .../variablesView/variableViewProvider.ts | 6 +++--- src/client/datascience/webviews/webviewHost.ts | 6 +++--- .../commands/commandRegistry.unit.test.ts | 5 ++++- 7 files changed, 34 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 9b55b8f5079..6f1eab231e8 100644 --- a/package.json +++ b/package.json @@ -704,7 +704,12 @@ "command": "jupyter.openVariableView", "title": "%jupyter.command.jupyter.openVariableView.title%", "category": "Jupyter" - } + }, + { + "command": "jupyter.TESTCOMMAND", + "title": "TESTCOMMAND", + "category": "Jupyter" + } ], "menus": { "editor/context": [ diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index a2258a94cc5..edb72754123 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -60,6 +60,7 @@ interface ICommandNameWithoutArgumentTypeMapping { [DSCommands.EnableDebugLogging]: []; [DSCommands.ResetLoggingLevel]: []; [DSCommands.OpenVariableView]: []; + [DSCommands.TESTCOMMAND]: []; } /** diff --git a/src/client/datascience/commands/commandRegistry.ts b/src/client/datascience/commands/commandRegistry.ts index f0b2dcb2438..80f3c01dac6 100644 --- a/src/client/datascience/commands/commandRegistry.ts +++ b/src/client/datascience/commands/commandRegistry.ts @@ -30,6 +30,7 @@ import { IJupyterVariableDataProviderFactory, INotebookEditorProvider } from '../types'; +import { IVariableViewProvider } from '../variablesView/types'; import { JupyterCommandLineSelectorCommand } from './commandLineSelector'; import { ExportCommands } from './exportCommands'; import { NotebookCommands } from './notebookCommands'; @@ -60,7 +61,8 @@ export class CommandRegistry implements IDisposable { @inject(IJupyterVariableDataProviderFactory) private readonly jupyterVariableDataProviderFactory: IJupyterVariableDataProviderFactory, @inject(IDataViewerFactory) private readonly dataViewerFactory: IDataViewerFactory, - @inject(IJupyterServerUriStorage) private readonly serverUriStorage: IJupyterServerUriStorage + @inject(IJupyterServerUriStorage) private readonly serverUriStorage: IJupyterServerUriStorage, + @inject(IVariableViewProvider) private readonly variableViewProvider: IVariableViewProvider ) { this.disposables.push(this.serverSelectedCommand); this.disposables.push(this.notebookCommands); @@ -117,6 +119,7 @@ export class CommandRegistry implements IDisposable { ); this.registerCommand(Commands.ClearSavedJupyterUris, this.clearJupyterUris); this.registerCommand(Commands.OpenVariableView, this.openVariableView); + this.registerCommand(Commands.TESTCOMMAND, this.handleTESTCOMMAND); if (this.commandListeners) { this.commandListeners.forEach((listener: IDataScienceCommandListener) => { listener.register(this.commandManager); @@ -126,6 +129,18 @@ export class CommandRegistry implements IDisposable { public dispose() { this.disposables.forEach((d) => d.dispose()); } + + // IANHU Just for testing, also remove injections required here + private async handleTESTCOMMAND(): Promise { + const variableView = this.variableViewProvider.activeVariableView; + + if (variableView) { + //variableView.getElementById('variable-view-main-panel');; + const result = await variableView.getHTMLById('variable-view-main-panel'); + traceError(result); + } + } + private registerCommand< E extends keyof ICommandNameArgumentTypeMapping, U extends ICommandNameArgumentTypeMapping[E] diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 13980f99a75..4616a6e6abe 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -132,6 +132,7 @@ export namespace Commands { export const ShowDataViewer = 'jupyter.showDataViewer'; export const ClearSavedJupyterUris = 'jupyter.clearSavedJupyterUris'; export const OpenVariableView = 'jupyter.openVariableView'; + export const TESTCOMMAND = 'jupyter.TESTCOMMAND'; } export namespace CodeLensCommands { diff --git a/src/client/datascience/variablesView/variableViewProvider.ts b/src/client/datascience/variablesView/variableViewProvider.ts index ee3809afb96..c9535dc0835 100644 --- a/src/client/datascience/variablesView/variableViewProvider.ts +++ b/src/client/datascience/variablesView/variableViewProvider.ts @@ -20,9 +20,9 @@ export class VariableViewProvider implements IVariableViewProvider { // Just for test execution allow for accessing the VariableView off of the provider public get activeVariableView(): VariableView | undefined { - if (!isTestExecution()) { - throw new Error('activeVariableView only accessible from test code'); - } + //if (!isTestExecution()) { + //throw new Error('activeVariableView only accessible from test code'); + //} return this.variableView; } diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index c8ba91854e5..6a4171c8242 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -88,9 +88,9 @@ export abstract class WebviewHost implements IDisposable { // only to be use for testing public getHTMLById(id: string): Promise { // Test only - if (!isTestExecution()) { - throw new Error('getHTMLById to be run only in test code'); - } + //if (!isTestExecution()) { + //throw new Error('getHTMLById to be run only in test code'); + //} if (!this.activeHTMLRequest) { this.activeHTMLRequest = createDeferred(); diff --git a/src/test/datascience/commands/commandRegistry.unit.test.ts b/src/test/datascience/commands/commandRegistry.unit.test.ts index 6bcf64fa531..36b9e394854 100644 --- a/src/test/datascience/commands/commandRegistry.unit.test.ts +++ b/src/test/datascience/commands/commandRegistry.unit.test.ts @@ -19,6 +19,7 @@ import { JupyterVariableDataProviderFactory } from '../../../client/datascience/ import { DataScienceCodeLensProvider } from '../../../client/datascience/editor-integration/codelensprovider'; import { JupyterServerUriStorage } from '../../../client/datascience/jupyter/serverUriStorage'; import { NativeEditorProvider } from '../../../client/datascience/notebookStorage/nativeEditorProvider'; +import { IVariableViewProvider } from '../../../client/datascience/variablesView/types'; import { MockOutputChannel } from '../../mockClasses'; // tslint:disable: max-func-body-length @@ -45,6 +46,7 @@ suite('DataScience - Commands', () => { const dataViewerFactory = mock(DataViewerFactory); const fileSystem = mock(FileSystem); const serverUriStorage = mock(JupyterServerUriStorage); + const varViewProvider = mock(); commandRegistry = new CommandRegistry( documentManager, @@ -63,7 +65,8 @@ suite('DataScience - Commands', () => { instance(fileSystem), instance(jupyterVariableDataProviderFactory), instance(dataViewerFactory), - instance(serverUriStorage) + instance(serverUriStorage), + instance(varViewProvider) ); }); From 29c273a7c99501f1e2dea15f0159526c2d916b3f Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 09:04:46 -0800 Subject: [PATCH 03/21] test passing locally --- .vscode/launch.json | 3 +- .../datascience/commands/commandRegistry.ts | 1 - .../variableView/variableView.vscode.test.ts | 107 ++++++++++++++++++ 3 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 src/test/datascience/variableView/variableView.vscode.test.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 8943b53d7e8..049fa637d90 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -201,7 +201,8 @@ "--extensionTestsPath=${workspaceFolder}/out/test" ], "env": { - "VSC_JUPYTER_CI_TEST_GREP": "VSCode Notebook", // Leave as `VSCode Notebook` to run only Notebook tests. + "XVSC_JUPYTER_CI_TEST_GREP": "VSCode Notebook", // Leave as `VSCode Notebook` to run only Notebook tests. + "VSC_JUPYTER_CI_TEST_GREP": "VariableView", // Leave as `VSCode Notebook` to run only Notebook tests. "VSC_JUPYTER_CI_TEST_INVERT_GREP": "", // Initialize this to invert the grep (exclude tests with value defined in grep). "CI_PYTHON_PATH": "", // Update with path to real python interpereter used for testing. "VSC_FORCE_REAL_JUPYTER": "true", // Enalbe tests that require Jupyter. diff --git a/src/client/datascience/commands/commandRegistry.ts b/src/client/datascience/commands/commandRegistry.ts index 80f3c01dac6..59e13c69e16 100644 --- a/src/client/datascience/commands/commandRegistry.ts +++ b/src/client/datascience/commands/commandRegistry.ts @@ -135,7 +135,6 @@ export class CommandRegistry implements IDisposable { const variableView = this.variableViewProvider.activeVariableView; if (variableView) { - //variableView.getElementById('variable-view-main-panel');; const result = await variableView.getHTMLById('variable-view-main-panel'); traceError(result); } diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts new file mode 100644 index 00000000000..070fbc6dfb1 --- /dev/null +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -0,0 +1,107 @@ +// tslint:disable:no-console +// IANHU: remove no-console +import { assert, expect } from 'chai'; +import * as sinon from 'sinon'; +import { ICommandManager, IVSCodeNotebook } from '../../../client/common/application/types'; +import { IDisposable } from '../../../client/common/types'; +import { Commands, VSCodeNotebookProvider } from '../../../client/datascience/constants'; +import { IVariableViewProvider } from '../../../client/datascience/variablesView/types'; +import { IExtensionTestApi } from '../../common'; +import { sleep } from '../../core'; +import { initialize } from '../../initialize'; +import { + canRunNotebookTests, + closeNotebooks, + closeNotebooksAndCleanUpAfterTests, + deleteAllCellsAndWait, + executeCell, + insertCodeCell, + startJupyter, + trustAllNotebooks, + waitForExecutionCompletedSuccessfully, + waitForKernelToGetAutoSelected +} from '../notebook/helper'; +import { INotebookEditorProvider } from '../../../client/datascience/types'; + +suite('DataScience - VariableView', () => { + let api: IExtensionTestApi; + const disposables: IDisposable[] = []; + let commandManager: ICommandManager; + let variableViewProvider: IVariableViewProvider; + let editorProvider: INotebookEditorProvider; + let vscodeNotebook: IVSCodeNotebook; + suiteSetup(async function () { + this.timeout(120_000); // IANHU: From other tests? Reduce this? + console.log('**** Start variableView suiteSetup ****'); + api = await initialize(); + + // Don't run if we can't use the native notebook interface + if (!(await canRunNotebookTests())) { + return this.skip(); + } + await trustAllNotebooks(); + await startJupyter(true); + sinon.restore(); + commandManager = api.serviceContainer.get(ICommandManager); + variableViewProvider = api.serviceContainer.get(IVariableViewProvider); + vscodeNotebook = api.serviceContainer.get(IVSCodeNotebook); + editorProvider = api.serviceContainer.get(VSCodeNotebookProvider); + }); + setup(async function () { + console.log('**** Start variableView setup ****'); + sinon.restore(); + + // Create an editor to use for our tests + await editorProvider.createNew(); + await waitForKernelToGetAutoSelected(); + await deleteAllCellsAndWait(); + assert.isOk(vscodeNotebook.activeNotebookEditor, 'No active notebook'); + }); + teardown(async function () { + await closeNotebooks(disposables); + await closeNotebooksAndCleanUpAfterTests(disposables); + }); + + // Cleanup after suite is finished + suiteTeardown(() => closeNotebooksAndCleanUpAfterTests(disposables)); + + // Test showing the variable view + test('Can show VariableView', async function () { + this.timeout(120_000); // IANHU: Just for testing + //commands.executeCommand('workbench.action.togglePanel'); + console.log('**** Start variableView test ****'); + // Add one simple cell and execute it + await insertCodeCell('test = "MYTESTVALUE"', { index: 0 }); + const cell = vscodeNotebook.activeNotebookEditor?.document.cells![0]!; + await executeCell(cell); + await waitForExecutionCompletedSuccessfully(cell); + + console.log('**** Cell execution done ****'); + // Send the command to open the view + commandManager.executeCommand(Commands.OpenVariableView); + + // IANHU: Remove, just for testing + await sleep(5_000); + + // Another view open? + commandManager.executeCommand(Commands.OpenVariableView); + await sleep(5_000); + + console.log('**** Sleep finished ****'); + + // Now check to see if we can actually look at the variable view + const variableView = variableViewProvider.activeVariableView; + + if (variableView) { + console.log('**** found variableView'); + } + + const htmlResult = await variableView?.getHTMLById('variable-view-main-panel'); + //const rootHtml = await variableView?.getElementByIdAsync('root'); + + //console.log(`**** rootHTML ${rootHtml}`); + console.log(`**** htmlResult ${htmlResult} ****`); + + expect(htmlResult).to.contain('MYTESTVALUE'); + }); +}); From ca115ee2131219b3fb39dcf3ba9256d3bb6a966f Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 12:30:10 -0800 Subject: [PATCH 04/21] create test middleware for our VSCode tests --- .vscode/launch.json | 2 + .../common/application/webviews/webview.ts | 5 + .../datascience/commands/commandRegistry.ts | 2 +- src/client/datascience/variablesView/types.ts | 4 +- .../datascience/variablesView/variableView.ts | 1 + .../variablesView/variableViewProvider.ts | 52 +++++++- .../datascience/webviews/webviewHost.ts | 17 +++ .../interactive-common/redux/store.ts | 5 +- .../variableView/variableView.vscode.test.ts | 22 +++- src/test/datascience/vscodeTestHelpers.ts | 112 ++++++++++++++++++ 10 files changed, 212 insertions(+), 10 deletions(-) create mode 100644 src/test/datascience/vscodeTestHelpers.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 049fa637d90..106ae197aa2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -31,6 +31,7 @@ "XVSC_JUPYTER_LOG_TELEMETRY": "1", // Enable this to log IPYWIDGET messages "VSC_JUPYTER_LOG_IPYWIDGETS": "1", + "VSC_JUPYTER_WEBVIEW_TEST_MIDDLEWARE": "true", // Initialize to create the webview test middleware // Enable this to log debugger output. Directory must exist ahead of time "XDEBUGPY_LOG_DIR": "${workspaceRoot}/tmp/Debug_Output_Ex" }, @@ -208,6 +209,7 @@ "VSC_FORCE_REAL_JUPYTER": "true", // Enalbe tests that require Jupyter. "VSC_JUPYTER_CI_RUN_NON_PYTHON_NB_TEST": "", // Initialize this to run tests again Julia & other kernels. "VSC_JUPYTER_RUN_NB_TEST": "true", // Initialize this to run notebook tests (must be using VSC Insiders). + "VSC_JUPYTER_WEBVIEW_TEST_MIDDLEWARE": "true", // Initialize to create the webview test middleware "VSC_JUPYTER_LOAD_EXPERIMENTS_FROM_FILE": "true", "TEST_FILES_SUFFIX": "vscode.test", "XVSC_JUPYTER_INSTRUMENT_CODE_FOR_COVERAGE": "1", diff --git a/src/client/common/application/webviews/webview.ts b/src/client/common/application/webviews/webview.ts index 7ee9577ef76..97d8de7d9e9 100644 --- a/src/client/common/application/webviews/webview.ts +++ b/src/client/common/application/webviews/webview.ts @@ -92,6 +92,8 @@ export abstract class Webview implements IWebview { ) ) .toString(); + + const forceTestMiddleware = process.env.VSC_JUPYTER_WEBVIEW_TEST_MIDDLEWARE; return ` @@ -121,6 +123,9 @@ export abstract class Webview implements IWebview { return "${uriBase}" + relativePath; } + function forceTestMiddleware() { + return ${forceTestMiddleware}; + } ${uris.map((uri) => ``).join('\n')} diff --git a/src/client/datascience/commands/commandRegistry.ts b/src/client/datascience/commands/commandRegistry.ts index 59e13c69e16..32241b797a8 100644 --- a/src/client/datascience/commands/commandRegistry.ts +++ b/src/client/datascience/commands/commandRegistry.ts @@ -132,7 +132,7 @@ export class CommandRegistry implements IDisposable { // IANHU Just for testing, also remove injections required here private async handleTESTCOMMAND(): Promise { - const variableView = this.variableViewProvider.activeVariableView; + const variableView = await this.variableViewProvider.activeVariableView; if (variableView) { const result = await variableView.getHTMLById('variable-view-main-panel'); diff --git a/src/client/datascience/variablesView/types.ts b/src/client/datascience/variablesView/types.ts index 539a9ed7952..dec2aa231dc 100644 --- a/src/client/datascience/variablesView/types.ts +++ b/src/client/datascience/variablesView/types.ts @@ -41,5 +41,7 @@ export interface INotebookWatcher { export const IVariableViewProvider = Symbol('IVariableViewProvider'); export interface IVariableViewProvider extends IVSCWebviewViewProvider { - activeVariableView?: VariableView; + //activeVariableView?: VariableView; + //readonly onDidResolveWebview: Event; + readonly activeVariableView: Promise; } diff --git a/src/client/datascience/variablesView/variableView.ts b/src/client/datascience/variablesView/variableView.ts index 3d2eef1d453..99de0742cf9 100644 --- a/src/client/datascience/variablesView/variableView.ts +++ b/src/client/datascience/variablesView/variableView.ts @@ -91,6 +91,7 @@ export class VariableView extends WebviewViewHost imp //tslint:disable-next-line:no-any protected onMessage(message: string, payload: any) { + console.log(`%%%%% onMessage ${message}`); switch (message) { case InteractiveWindowMessages.GetVariablesRequest: this.handleMessage(message, payload, this.requestVariables); diff --git a/src/client/datascience/variablesView/variableViewProvider.ts b/src/client/datascience/variablesView/variableViewProvider.ts index c9535dc0835..2932b2af5dd 100644 --- a/src/client/datascience/variablesView/variableViewProvider.ts +++ b/src/client/datascience/variablesView/variableViewProvider.ts @@ -3,10 +3,11 @@ 'use strict'; import { inject, injectable, named } from 'inversify'; -import { CancellationToken, WebviewView, WebviewViewResolveContext } from 'vscode'; +import { CancellationToken, Event, EventEmitter, WebviewView, WebviewViewResolveContext } from 'vscode'; import { IApplicationShell, IWebviewViewProvider, IWorkspaceService } from '../../common/application/types'; import { isTestExecution } from '../../common/constants'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; +import { createDeferred, Deferred } from '../../common/utils/async'; import { Identifiers } from '../constants'; import { IDataViewerFactory } from '../data-viewing/types'; import { ICodeCssGenerator, IJupyterVariableDataProviderFactory, IJupyterVariables, IThemeFinder } from '../types'; @@ -18,15 +19,54 @@ import { VariableView } from './variableView'; export class VariableViewProvider implements IVariableViewProvider { public readonly viewType = 'jupyterViewVariables'; - // Just for test execution allow for accessing the VariableView off of the provider - public get activeVariableView(): VariableView | undefined { + public get activeVariableView(): Promise { + // For test execution only //if (!isTestExecution()) { //throw new Error('activeVariableView only accessible from test code'); //} - return this.variableView; + // If we have already created the view, then just return it + if (this.variableView) { + return Promise.resolve(this.variableView); + } + + // If not wait until created and then return + this.activeVariableViewPromise = createDeferred(); + return this.activeVariableViewPromise.promise; } + private activeVariableViewPromise?: Deferred; + + // Just for test execution allow for accessing the VariableView off of the provider + //public get activeVariableView(): VariableView | undefined { + ////if (!isTestExecution()) { + ////throw new Error('activeVariableView only accessible from test code'); + ////} + + //return this.variableView; + //} + + //public get onDidResolveWebview(): Event { + ////if (!isTestExecution()) { + ////throw new Error('Access only for tests'); + ////} + //return this._onDidResolveWebview.event; + //} + //private readonly _onDidResolveWebview = new EventEmitter(); + + //public get onDidChangeActiveVariableViewNotebook(): Event { + //return this._onDidChangeActiveVariableViewNotebook.event; + //} + //public get onDidExecuteActiveVariableViewNotebook(): Event<{ executionCount: number }> { + //return this._onDidExecuteActiveVariableViewNotebook.event; + //} + //public get activeVariableViewNotebook(): INotebook | undefined { + //return this.notebookEditorProvider.activeEditor?.notebook; + //} + + //private readonly _onDidExecuteActiveVariableViewNotebook = new EventEmitter<{ executionCount: number }>(); + //private readonly _onDidChangeActiveVariableViewNotebook = new EventEmitter(); + private variableView?: VariableView; constructor( @@ -66,6 +106,10 @@ export class VariableViewProvider implements IVariableViewProvider { this.notebookWatcher ); + if (this.activeVariableViewPromise) { + this.activeVariableViewPromise.resolve(this.variableView); + } + await this.variableView.load(webviewView); } } diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index 6a4171c8242..438da9ecff4 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -47,6 +47,9 @@ export abstract class WebviewHost implements IDisposable { // For testing, holds the current request for webview HTML private activeHTMLRequest?: Deferred; + // For testing, broadcast messages to the following listeners + private onMessageListeners: { onMessage(message: string, payload: any): void }[] = []; + constructor( @unmanaged() protected configService: IConfigurationService, @unmanaged() private cssGenerator: ICodeCssGenerator, @@ -103,6 +106,15 @@ export abstract class WebviewHost implements IDisposable { return this.activeHTMLRequest.promise; } + public addOnMessageListener(listener: { onMessage(message: string, payload: any): void }): void { + // Test only + //if (!isTestExecution()) { + //throw new Error('getHTMLById to be run only in test code'); + //} + + this.onMessageListeners.push(listener); + } + protected abstract provideWebview( cwd: string, settings: IJupyterExtraSettings, @@ -154,6 +166,11 @@ export abstract class WebviewHost implements IDisposable { default: break; } + + // Broadcast to onMessage listeners + this.onMessageListeners.forEach((listener) => { + listener.onMessage(message, payload); + }); } protected async loadWebview(cwd: string, webView?: vscodeWebviewPanel | vscodeWebviewView) { diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index 5f88e8db662..60e0525ec4d 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -34,6 +34,9 @@ import { generateMonacoReducer, IMonacoState } from './reducers/monaco'; import { CommonActionType } from './reducers/types'; import { generateVariableReducer, IVariableState } from './reducers/variables'; +// Externally defined function to see if we need to force on test middleware +export declare function forceTestMiddleware(): boolean; + function generateDefaultState( skipDefault: boolean, testMode: boolean, @@ -306,7 +309,7 @@ function createMiddleWare(testMode: boolean, postOffice: PostOffice): Redux.Midd // Or if testing in UI Test. // tslint:disable-next-line: no-any const isUITest = (postOffice.acquireApi() as any)?.handleMessage ? true : false; - const testMiddleware = testMode || isUITest ? createTestMiddleware() : undefined; + const testMiddleware = forceTestMiddleware() || testMode || isUITest ? createTestMiddleware() : undefined; // Create the logger if we're not in production mode or we're forcing logging const reduceLogMessage = ''; diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index 070fbc6dfb1..304a6e589ba 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -22,6 +22,7 @@ import { waitForKernelToGetAutoSelected } from '../notebook/helper'; import { INotebookEditorProvider } from '../../../client/datascience/types'; +import { OnMessageListener, OnMessageWrapper } from '../vscodeTestHelpers'; suite('DataScience - VariableView', () => { let api: IExtensionTestApi; @@ -81,21 +82,34 @@ suite('DataScience - VariableView', () => { commandManager.executeCommand(Commands.OpenVariableView); // IANHU: Remove, just for testing - await sleep(5_000); + await sleep(4_000); // Another view open? commandManager.executeCommand(Commands.OpenVariableView); - await sleep(5_000); + await sleep(4_000); console.log('**** Sleep finished ****'); // Now check to see if we can actually look at the variable view - const variableView = variableViewProvider.activeVariableView; + const variableView = await variableViewProvider.activeVariableView; if (variableView) { console.log('**** found variableView'); } + // Check our messages for variable view + //const variableMessageWrapper = new OnMessageWrapper(variableView as any); + + // Add our message listener + const onMessageListener = new OnMessageListener(); + variableView.addOnMessageListener(onMessageListener); + + // Send a second cell + await insertCodeCell('test2 = "MYTESTVALUE2"', { index: 1 }); + const cell2 = vscodeNotebook.activeNotebookEditor?.document.cells![1]!; + await executeCell(cell2); + await waitForExecutionCompletedSuccessfully(cell2); + const htmlResult = await variableView?.getHTMLById('variable-view-main-panel'); //const rootHtml = await variableView?.getElementByIdAsync('root'); @@ -103,5 +117,7 @@ suite('DataScience - VariableView', () => { console.log(`**** htmlResult ${htmlResult} ****`); expect(htmlResult).to.contain('MYTESTVALUE'); + + await sleep(5_000); }); }); diff --git a/src/test/datascience/vscodeTestHelpers.ts b/src/test/datascience/vscodeTestHelpers.ts new file mode 100644 index 00000000000..116058231d9 --- /dev/null +++ b/src/test/datascience/vscodeTestHelpers.ts @@ -0,0 +1,112 @@ +interface IOnMessage { + onMessage(message: string, payload: any): void; +} + +// IANHU: Same as mounted webview? Share? +export type WaitForMessageOptions = { + /** + * Timeout for waiting for message. + * Defaults to 65_000ms. + * + * @type {number} + */ + timeoutMs?: number; + /** + * Number of times the message should be received. + * Defaults to 1. + * + * @type {number} + */ + numberOfTimes?: number; + + // Optional check for the payload of the message + // will only return (or count) message if this returns true + // tslint:disable-next-line: no-any + withPayload?(payload: any): boolean; +}; + +export class OnMessageListener { + public onMessage(message: string, payload: any) { + console.log(`#### ${message}`); + } +} + +// Wrap anything that supports the onMessage(message: string, payload: any): void +export class OnMessageWrapper { + private wrappedClass: IOnMessage; + private originalOnMessage; + + constructor(wrapped: IOnMessage) { + this.wrappedClass = wrapped; + + // Patch out the onMessage function with our interceptor + this.originalOnMessage = this.wrappedClass.onMessage; + this.wrappedClass.onMessage = this.onMessage; + } + + public async waitForMessage(message: string, options?: WaitForMessageOptions): Promise { + //const timeoutMs = options && options.timeoutMs ? options.timeoutMs : undefined; + //const numberOfTimes = options && options.numberOfTimes ? options.numberOfTimes : 1; + } + + private onMessage(message: string, payload: any): void { + // Last thing call the original onMessage + this.originalOnMessage(message, payload); + } +} + +//public async waitForMessage(message: string, options?: WaitForMessageOptions): Promise { +//const timeoutMs = options && options.timeoutMs ? options.timeoutMs : undefined; +//const numberOfTimes = options && options.numberOfTimes ? options.numberOfTimes : 1; +//// Wait for the mounted web panel to send a message back to the data explorer +//const promise = createDeferred(); +//traceInfo(`Waiting for message ${message} with timeout of ${timeoutMs}`); +//let handler: (m: string, p: any) => void; +//const timer = timeoutMs +//? setTimeout(() => { +//if (!promise.resolved) { +//promise.reject(new Error(`Waiting for ${message} timed out`)); +//} +//}, timeoutMs) +//: undefined; +//let timesMessageReceived = 0; +//const dispatchedAction = `DISPATCHED_ACTION_${message}`; +//handler = (m: string, payload: any) => { +//if (m === message || m === dispatchedAction) { +//// First verify the payload matches +//if (options?.withPayload) { +//if (!options.withPayload(payload)) { +//return; +//} +//} + +//timesMessageReceived += 1; +//if (timesMessageReceived < numberOfTimes) { +//return; +//} +//if (timer) { +//clearTimeout(timer); +//} +//this.removeMessageListener(handler); +//// Make sure to rerender current state. +//if (this.wrapper) { +//this.wrapper.update(); +//} +//if (m === message) { +//promise.resolve(); +//} else { +//// It could a redux dispatched message. +//// Wait for 10ms, wait for other stuff to finish. +//// We can wait for 100ms or 1s. But thats too long. +//// The assumption is that currently we do not have any setTimeouts +//// in UI code that's in the magnitude of 100ms or more. +//// We do have a couple of setTiemout's, but they wait for 1ms, not 100ms. +//// 10ms more than sufficient for all the UI timeouts. +//setTimeout(() => promise.resolve(), 10); +//} +//} +//}; + +//this.addMessageListener(handler); +//return promise.promise; +//} From fb3f1c101168aea2dc930b1ad6bbac99fa469e59 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 14:08:30 -0800 Subject: [PATCH 05/21] test running with message --- .../datascience/webviews/webviewHost.ts | 20 +++- .../variableView/variableView.vscode.test.ts | 8 +- src/test/datascience/vscodeTestHelpers.ts | 113 +++++++++++++++++- 3 files changed, 130 insertions(+), 11 deletions(-) diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index 438da9ecff4..24381100712 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -33,6 +33,7 @@ export abstract class WebviewHost implements IDisposable { protected abstract get owningResource(): Resource; protected abstract get title(): string; + protected webview?: IWebview; protected disposed = false; @@ -48,7 +49,7 @@ export abstract class WebviewHost implements IDisposable { private activeHTMLRequest?: Deferred; // For testing, broadcast messages to the following listeners - private onMessageListeners: { onMessage(message: string, payload: any): void }[] = []; + private onMessageListeners: ((message: string, payload: any) => void)[] = []; constructor( @unmanaged() protected configService: IConfigurationService, @@ -106,13 +107,24 @@ export abstract class WebviewHost implements IDisposable { return this.activeHTMLRequest.promise; } - public addOnMessageListener(listener: { onMessage(message: string, payload: any): void }): void { + public addOnMessageListener(callback: (message: string, payload: any) => void) { // Test only //if (!isTestExecution()) { //throw new Error('getHTMLById to be run only in test code'); //} - this.onMessageListeners.push(listener); + this.onMessageListeners.push(callback); + } + + public removeOnMessageListener(callback: (message: string, payload: any) => void) { + // Test only + //if (!isTestExecution()) { + //throw new Error('getHTMLById to be run only in test code'); + //} + const index = this.onMessageListeners.indexOf(callback); + if (index >= 0) { + this.onMessageListeners.splice(index, 1); + } } protected abstract provideWebview( @@ -169,7 +181,7 @@ export abstract class WebviewHost implements IDisposable { // Broadcast to onMessage listeners this.onMessageListeners.forEach((listener) => { - listener.onMessage(message, payload); + listener(message, payload); }); } diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index 304a6e589ba..cd32f3a629c 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -23,6 +23,7 @@ import { } from '../notebook/helper'; import { INotebookEditorProvider } from '../../../client/datascience/types'; import { OnMessageListener, OnMessageWrapper } from '../vscodeTestHelpers'; +import { InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; suite('DataScience - VariableView', () => { let api: IExtensionTestApi; @@ -101,14 +102,15 @@ suite('DataScience - VariableView', () => { //const variableMessageWrapper = new OnMessageWrapper(variableView as any); // Add our message listener - const onMessageListener = new OnMessageListener(); - variableView.addOnMessageListener(onMessageListener); + const onMessageListener = new OnMessageListener(variableView); // Send a second cell await insertCodeCell('test2 = "MYTESTVALUE2"', { index: 1 }); const cell2 = vscodeNotebook.activeNotebookEditor?.document.cells![1]!; await executeCell(cell2); - await waitForExecutionCompletedSuccessfully(cell2); + //await waitForExecutionCompletedSuccessfully(cell2); + + await onMessageListener.waitForMessage(InteractiveWindowMessages.VariablesComplete); const htmlResult = await variableView?.getHTMLById('variable-view-main-panel'); //const rootHtml = await variableView?.getElementByIdAsync('root'); diff --git a/src/test/datascience/vscodeTestHelpers.ts b/src/test/datascience/vscodeTestHelpers.ts index 116058231d9..970f1bee5c1 100644 --- a/src/test/datascience/vscodeTestHelpers.ts +++ b/src/test/datascience/vscodeTestHelpers.ts @@ -1,7 +1,33 @@ +import { createDeferred } from '../../client/common/utils/async'; + interface IOnMessage { onMessage(message: string, payload: any): void; } +interface IOnMessageListener { + addOnMessageListener(callback: (message: string, payload: any) => void): void; + removeOnMessageListener(callback: (message: string, payload: any) => void): void; +} +//public addOnMessageListener(callback: (message: string, payload: any) => void) { +//// Test only +////if (!isTestExecution()) { +////throw new Error('getHTMLById to be run only in test code'); +////} + +//this.onMessageListeners.push(callback); +//} + +//public removeOnMessageListener(callback: (message: string, payload: any) => void) { +//// Test only +////if (!isTestExecution()) { +////throw new Error('getHTMLById to be run only in test code'); +////} +//const index = this.onMessageListeners.indexOf(callback); +//if (index >= 0) { +//this.onMessageListeners.splice(index, 1); +//} +//} + // IANHU: Same as mounted webview? Share? export type WaitForMessageOptions = { /** @@ -26,8 +52,74 @@ export type WaitForMessageOptions = { }; export class OnMessageListener { - public onMessage(message: string, payload: any) { - console.log(`#### ${message}`); + private target: IOnMessageListener; + constructor(target: IOnMessageListener) { + this.target = target; + } + public async waitForMessage(message: string, options?: WaitForMessageOptions): Promise { + const timeoutMs = options && options.timeoutMs ? options.timeoutMs : undefined; + const numberOfTimes = options && options.numberOfTimes ? options.numberOfTimes : 1; + + const promise = createDeferred(); + + let handler: (m: string, p: any) => void; + const timer = timeoutMs + ? setTimeout(() => { + if (!promise.resolved) { + promise.reject(new Error(`Waiting for ${message} timed out`)); + } + }, timeoutMs) + : undefined; + let timesMessageReceived = 0; + const dispatchedAction = `DISPATCHED_ACTION_${message}`; + handler = (m: string, payload: any) => { + console.log(`#### message in handler ${message}`); + if (m === message || m === dispatchedAction) { + // First verify the payload matches + if (options?.withPayload) { + if (!options.withPayload(payload)) { + return; + } + } + + timesMessageReceived += 1; + if (timesMessageReceived < numberOfTimes) { + return; + } + if (timer) { + clearTimeout(timer); + } + this.removeMessageListener(handler); + if (m === message) { + promise.resolve(); + } else { + // It could a redux dispatched message. + // Wait for 10ms, wait for other stuff to finish. + // We can wait for 100ms or 1s. But thats too long. + // The assumption is that currently we do not have any setTimeouts + // in UI code that's in the magnitude of 100ms or more. + // We do have a couple of setTiemout's, but they wait for 1ms, not 100ms. + // 10ms more than sufficient for all the UI timeouts. + setTimeout(() => promise.resolve(), 10); + } + } + }; + + this.addMessageListener(handler); + return promise.promise; + } + + public addMessageListener(callback: (m: string, p: any) => void) { + this.target.addOnMessageListener(callback); + //this.extraListeners.push(callback); + } + + public removeMessageListener(callback: (m: string, p: any) => void) { + this.target.removeOnMessageListener(callback); + //const index = this.extraListeners.indexOf(callback); + //if (index >= 0) { + //this.extraListeners.splice(index, 1); + //} } } @@ -45,8 +137,21 @@ export class OnMessageWrapper { } public async waitForMessage(message: string, options?: WaitForMessageOptions): Promise { - //const timeoutMs = options && options.timeoutMs ? options.timeoutMs : undefined; - //const numberOfTimes = options && options.numberOfTimes ? options.numberOfTimes : 1; + const timeoutMs = options && options.timeoutMs ? options.timeoutMs : undefined; + const numberOfTimes = options && options.numberOfTimes ? options.numberOfTimes : 1; + + const promise = createDeferred(); + + const timer = timeoutMs + ? setTimeout(() => { + if (!promise.resolved) { + promise.reject(new Error(`Waiting for ${message} timed out`)); + } + }, timeoutMs) + : undefined; + let timesMessageReceived = 0; + + return promise.promise; } private onMessage(message: string, payload: any): void { From 55b1be33818c8bb49a3d72372e3b93ce128a4612 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 14:43:35 -0800 Subject: [PATCH 06/21] working with sleeps removed --- .../datascience/variableView/variableView.vscode.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index cd32f3a629c..24c7c8a639b 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -83,11 +83,11 @@ suite('DataScience - VariableView', () => { commandManager.executeCommand(Commands.OpenVariableView); // IANHU: Remove, just for testing - await sleep(4_000); + //await sleep(4_000); // Another view open? - commandManager.executeCommand(Commands.OpenVariableView); - await sleep(4_000); + //commandManager.executeCommand(Commands.OpenVariableView); + //await sleep(4_000); console.log('**** Sleep finished ****'); @@ -119,7 +119,8 @@ suite('DataScience - VariableView', () => { console.log(`**** htmlResult ${htmlResult} ****`); expect(htmlResult).to.contain('MYTESTVALUE'); + expect(htmlResult).to.contain('MYTESTVALUE2'); - await sleep(5_000); + //await sleep(5_000); }); }); From d8eae7df3d3d7bdab13d9d2d0c4c40357ad70dcc Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 14:52:18 -0800 Subject: [PATCH 07/21] removing some logging --- .../variableView/variableView.vscode.test.ts | 28 ------------------- .../variableView/variableViewHelpers.ts | 0 2 files changed, 28 deletions(-) create mode 100644 src/test/datascience/variableView/variableViewHelpers.ts diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index 24c7c8a639b..bfb053d0460 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -34,7 +34,6 @@ suite('DataScience - VariableView', () => { let vscodeNotebook: IVSCodeNotebook; suiteSetup(async function () { this.timeout(120_000); // IANHU: From other tests? Reduce this? - console.log('**** Start variableView suiteSetup ****'); api = await initialize(); // Don't run if we can't use the native notebook interface @@ -69,38 +68,18 @@ suite('DataScience - VariableView', () => { // Test showing the variable view test('Can show VariableView', async function () { - this.timeout(120_000); // IANHU: Just for testing - //commands.executeCommand('workbench.action.togglePanel'); - console.log('**** Start variableView test ****'); // Add one simple cell and execute it await insertCodeCell('test = "MYTESTVALUE"', { index: 0 }); const cell = vscodeNotebook.activeNotebookEditor?.document.cells![0]!; await executeCell(cell); await waitForExecutionCompletedSuccessfully(cell); - console.log('**** Cell execution done ****'); // Send the command to open the view commandManager.executeCommand(Commands.OpenVariableView); - // IANHU: Remove, just for testing - //await sleep(4_000); - - // Another view open? - //commandManager.executeCommand(Commands.OpenVariableView); - //await sleep(4_000); - - console.log('**** Sleep finished ****'); - // Now check to see if we can actually look at the variable view const variableView = await variableViewProvider.activeVariableView; - if (variableView) { - console.log('**** found variableView'); - } - - // Check our messages for variable view - //const variableMessageWrapper = new OnMessageWrapper(variableView as any); - // Add our message listener const onMessageListener = new OnMessageListener(variableView); @@ -108,19 +87,12 @@ suite('DataScience - VariableView', () => { await insertCodeCell('test2 = "MYTESTVALUE2"', { index: 1 }); const cell2 = vscodeNotebook.activeNotebookEditor?.document.cells![1]!; await executeCell(cell2); - //await waitForExecutionCompletedSuccessfully(cell2); await onMessageListener.waitForMessage(InteractiveWindowMessages.VariablesComplete); const htmlResult = await variableView?.getHTMLById('variable-view-main-panel'); - //const rootHtml = await variableView?.getElementByIdAsync('root'); - - //console.log(`**** rootHTML ${rootHtml}`); - console.log(`**** htmlResult ${htmlResult} ****`); expect(htmlResult).to.contain('MYTESTVALUE'); expect(htmlResult).to.contain('MYTESTVALUE2'); - - //await sleep(5_000); }); }); diff --git a/src/test/datascience/variableView/variableViewHelpers.ts b/src/test/datascience/variableView/variableViewHelpers.ts new file mode 100644 index 00000000000..e69de29bb2d From fc4ac478dd90e40f52d5ec3290922c80394d3c7f Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 16:09:44 -0800 Subject: [PATCH 08/21] full test with waits and verify passing --- .../variableView/variableView.vscode.test.ts | 9 +++- .../variableView/variableViewHelpers.ts | 50 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index bfb053d0460..28aa465833e 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -24,6 +24,7 @@ import { import { INotebookEditorProvider } from '../../../client/datascience/types'; import { OnMessageListener, OnMessageWrapper } from '../vscodeTestHelpers'; import { InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; +import { verifyViewVariables } from './variableViewHelpers'; suite('DataScience - VariableView', () => { let api: IExtensionTestApi; @@ -92,7 +93,11 @@ suite('DataScience - VariableView', () => { const htmlResult = await variableView?.getHTMLById('variable-view-main-panel'); - expect(htmlResult).to.contain('MYTESTVALUE'); - expect(htmlResult).to.contain('MYTESTVALUE2'); + //const viewVariables = parseVariableViewHTML(htmlResult); + const expectedVariables = [ + { name: 'test', type: 'str', length: '11', value: ' MYTESTVALUE' }, + { name: 'test2', type: 'str', length: '12', value: ' MYTESTVALUE2' } + ]; + verifyViewVariables(expectedVariables, htmlResult); }); }); diff --git a/src/test/datascience/variableView/variableViewHelpers.ts b/src/test/datascience/variableView/variableViewHelpers.ts index e69de29bb2d..3ded3a49d18 100644 --- a/src/test/datascience/variableView/variableViewHelpers.ts +++ b/src/test/datascience/variableView/variableViewHelpers.ts @@ -0,0 +1,50 @@ +import { assert, expect } from 'chai'; + +export interface IVariableInfo { + name: string; + type: string; + length: string; + value: string; +} + +// For the given html, verify that the expected variables are in it +export function verifyViewVariables(expected: IVariableInfo[], html: string) { + const htmlVariables = parseVariableViewHTML(html); + + // Check our size first + expect(htmlVariables.length).to.be.equal(expected.length, 'Did not find expected number of variables'); + + expected.forEach((expectedInfo, index) => { + compareVariableInfos(expectedInfo, htmlVariables[index]); + }); +} + +// Helper function to parse the view HTML +export function parseVariableViewHTML(html: string): IVariableInfo[] { + const parser = new DOMParser(); + const htmlDoc = parser.parseFromString(html, 'text/html'); + const variableRows = htmlDoc.getElementsByClassName('react-grid-Row'); + + const variableInfos: IVariableInfo[] = []; + // HTMLCollectionOf doesn't support nice iterators + for (let index = 0; index < variableRows.length; index++) { + variableInfos.push(extractVariableFromRow(variableRows[index])); + } + + return variableInfos; +} + +function extractVariableFromRow(variableHTMLRow: Element): IVariableInfo { + const cellElements = variableHTMLRow.querySelectorAll('[role=cell]'); + return { + name: cellElements[0].innerHTML, + type: cellElements[1].innerHTML, + length: cellElements[2].innerHTML, + value: cellElements[3].innerHTML + }; +} + +function compareVariableInfos(expected: IVariableInfo, actual: IVariableInfo) { + //expect({ a: 1 }).to.deep.equal({ a: 1 }); + expect(expected).to.deep.equal(actual, 'Found Variable incorrect'); +} From ecd9a3c4283b08c2774191a912788ce8270ad347 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 16:18:12 -0800 Subject: [PATCH 09/21] basic cleanup + old code removal --- .../datascience/variablesView/variableView.ts | 1 - .../variablesView/variableViewProvider.ts | 3 +- .../variableView/variableView.vscode.test.ts | 8 +- .../variableView/variableViewHelpers.ts | 2 +- src/test/datascience/vscodeTestHelpers.ts | 123 ------------------ 5 files changed, 4 insertions(+), 133 deletions(-) diff --git a/src/client/datascience/variablesView/variableView.ts b/src/client/datascience/variablesView/variableView.ts index 99de0742cf9..3d2eef1d453 100644 --- a/src/client/datascience/variablesView/variableView.ts +++ b/src/client/datascience/variablesView/variableView.ts @@ -91,7 +91,6 @@ export class VariableView extends WebviewViewHost imp //tslint:disable-next-line:no-any protected onMessage(message: string, payload: any) { - console.log(`%%%%% onMessage ${message}`); switch (message) { case InteractiveWindowMessages.GetVariablesRequest: this.handleMessage(message, payload, this.requestVariables); diff --git a/src/client/datascience/variablesView/variableViewProvider.ts b/src/client/datascience/variablesView/variableViewProvider.ts index 2932b2af5dd..9758f3248f1 100644 --- a/src/client/datascience/variablesView/variableViewProvider.ts +++ b/src/client/datascience/variablesView/variableViewProvider.ts @@ -3,9 +3,8 @@ 'use strict'; import { inject, injectable, named } from 'inversify'; -import { CancellationToken, Event, EventEmitter, WebviewView, WebviewViewResolveContext } from 'vscode'; +import { CancellationToken, WebviewView, WebviewViewResolveContext } from 'vscode'; import { IApplicationShell, IWebviewViewProvider, IWorkspaceService } from '../../common/application/types'; -import { isTestExecution } from '../../common/constants'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; import { createDeferred, Deferred } from '../../common/utils/async'; import { Identifiers } from '../constants'; diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index 28aa465833e..9edcb90bf31 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -1,13 +1,10 @@ -// tslint:disable:no-console -// IANHU: remove no-console -import { assert, expect } from 'chai'; +import { assert } from 'chai'; import * as sinon from 'sinon'; import { ICommandManager, IVSCodeNotebook } from '../../../client/common/application/types'; import { IDisposable } from '../../../client/common/types'; import { Commands, VSCodeNotebookProvider } from '../../../client/datascience/constants'; import { IVariableViewProvider } from '../../../client/datascience/variablesView/types'; import { IExtensionTestApi } from '../../common'; -import { sleep } from '../../core'; import { initialize } from '../../initialize'; import { canRunNotebookTests, @@ -22,7 +19,7 @@ import { waitForKernelToGetAutoSelected } from '../notebook/helper'; import { INotebookEditorProvider } from '../../../client/datascience/types'; -import { OnMessageListener, OnMessageWrapper } from '../vscodeTestHelpers'; +import { OnMessageListener } from '../vscodeTestHelpers'; import { InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; import { verifyViewVariables } from './variableViewHelpers'; @@ -93,7 +90,6 @@ suite('DataScience - VariableView', () => { const htmlResult = await variableView?.getHTMLById('variable-view-main-panel'); - //const viewVariables = parseVariableViewHTML(htmlResult); const expectedVariables = [ { name: 'test', type: 'str', length: '11', value: ' MYTESTVALUE' }, { name: 'test2', type: 'str', length: '12', value: ' MYTESTVALUE2' } diff --git a/src/test/datascience/variableView/variableViewHelpers.ts b/src/test/datascience/variableView/variableViewHelpers.ts index 3ded3a49d18..66ecb95e768 100644 --- a/src/test/datascience/variableView/variableViewHelpers.ts +++ b/src/test/datascience/variableView/variableViewHelpers.ts @@ -1,4 +1,4 @@ -import { assert, expect } from 'chai'; +import { expect } from 'chai'; export interface IVariableInfo { name: string; diff --git a/src/test/datascience/vscodeTestHelpers.ts b/src/test/datascience/vscodeTestHelpers.ts index 970f1bee5c1..5005a538fce 100644 --- a/src/test/datascience/vscodeTestHelpers.ts +++ b/src/test/datascience/vscodeTestHelpers.ts @@ -1,34 +1,10 @@ import { createDeferred } from '../../client/common/utils/async'; -interface IOnMessage { - onMessage(message: string, payload: any): void; -} - interface IOnMessageListener { addOnMessageListener(callback: (message: string, payload: any) => void): void; removeOnMessageListener(callback: (message: string, payload: any) => void): void; } -//public addOnMessageListener(callback: (message: string, payload: any) => void) { -//// Test only -////if (!isTestExecution()) { -////throw new Error('getHTMLById to be run only in test code'); -////} - -//this.onMessageListeners.push(callback); -//} - -//public removeOnMessageListener(callback: (message: string, payload: any) => void) { -//// Test only -////if (!isTestExecution()) { -////throw new Error('getHTMLById to be run only in test code'); -////} -//const index = this.onMessageListeners.indexOf(callback); -//if (index >= 0) { -//this.onMessageListeners.splice(index, 1); -//} -//} -// IANHU: Same as mounted webview? Share? export type WaitForMessageOptions = { /** * Timeout for waiting for message. @@ -73,7 +49,6 @@ export class OnMessageListener { let timesMessageReceived = 0; const dispatchedAction = `DISPATCHED_ACTION_${message}`; handler = (m: string, payload: any) => { - console.log(`#### message in handler ${message}`); if (m === message || m === dispatchedAction) { // First verify the payload matches if (options?.withPayload) { @@ -111,107 +86,9 @@ export class OnMessageListener { public addMessageListener(callback: (m: string, p: any) => void) { this.target.addOnMessageListener(callback); - //this.extraListeners.push(callback); } public removeMessageListener(callback: (m: string, p: any) => void) { this.target.removeOnMessageListener(callback); - //const index = this.extraListeners.indexOf(callback); - //if (index >= 0) { - //this.extraListeners.splice(index, 1); - //} - } -} - -// Wrap anything that supports the onMessage(message: string, payload: any): void -export class OnMessageWrapper { - private wrappedClass: IOnMessage; - private originalOnMessage; - - constructor(wrapped: IOnMessage) { - this.wrappedClass = wrapped; - - // Patch out the onMessage function with our interceptor - this.originalOnMessage = this.wrappedClass.onMessage; - this.wrappedClass.onMessage = this.onMessage; - } - - public async waitForMessage(message: string, options?: WaitForMessageOptions): Promise { - const timeoutMs = options && options.timeoutMs ? options.timeoutMs : undefined; - const numberOfTimes = options && options.numberOfTimes ? options.numberOfTimes : 1; - - const promise = createDeferred(); - - const timer = timeoutMs - ? setTimeout(() => { - if (!promise.resolved) { - promise.reject(new Error(`Waiting for ${message} timed out`)); - } - }, timeoutMs) - : undefined; - let timesMessageReceived = 0; - - return promise.promise; - } - - private onMessage(message: string, payload: any): void { - // Last thing call the original onMessage - this.originalOnMessage(message, payload); } } - -//public async waitForMessage(message: string, options?: WaitForMessageOptions): Promise { -//const timeoutMs = options && options.timeoutMs ? options.timeoutMs : undefined; -//const numberOfTimes = options && options.numberOfTimes ? options.numberOfTimes : 1; -//// Wait for the mounted web panel to send a message back to the data explorer -//const promise = createDeferred(); -//traceInfo(`Waiting for message ${message} with timeout of ${timeoutMs}`); -//let handler: (m: string, p: any) => void; -//const timer = timeoutMs -//? setTimeout(() => { -//if (!promise.resolved) { -//promise.reject(new Error(`Waiting for ${message} timed out`)); -//} -//}, timeoutMs) -//: undefined; -//let timesMessageReceived = 0; -//const dispatchedAction = `DISPATCHED_ACTION_${message}`; -//handler = (m: string, payload: any) => { -//if (m === message || m === dispatchedAction) { -//// First verify the payload matches -//if (options?.withPayload) { -//if (!options.withPayload(payload)) { -//return; -//} -//} - -//timesMessageReceived += 1; -//if (timesMessageReceived < numberOfTimes) { -//return; -//} -//if (timer) { -//clearTimeout(timer); -//} -//this.removeMessageListener(handler); -//// Make sure to rerender current state. -//if (this.wrapper) { -//this.wrapper.update(); -//} -//if (m === message) { -//promise.resolve(); -//} else { -//// It could a redux dispatched message. -//// Wait for 10ms, wait for other stuff to finish. -//// We can wait for 100ms or 1s. But thats too long. -//// The assumption is that currently we do not have any setTimeouts -//// in UI code that's in the magnitude of 100ms or more. -//// We do have a couple of setTiemout's, but they wait for 1ms, not 100ms. -//// 10ms more than sufficient for all the UI timeouts. -//setTimeout(() => promise.resolve(), 10); -//} -//} -//}; - -//this.addMessageListener(handler); -//return promise.promise; -//} From 891db2303ef4f633eac5b8c7d8228b8fe2c085e2 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 16:28:06 -0800 Subject: [PATCH 10/21] minor rename --- src/client/datascience/webviews/webviewHost.ts | 7 +++++-- src/test/datascience/vscodeTestHelpers.ts | 9 +++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index 24381100712..ca91459925a 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -49,6 +49,7 @@ export abstract class WebviewHost implements IDisposable { private activeHTMLRequest?: Deferred; // For testing, broadcast messages to the following listeners + // tslint:disable-next-line:no-any private onMessageListeners: ((message: string, payload: any) => void)[] = []; constructor( @@ -107,7 +108,8 @@ export abstract class WebviewHost implements IDisposable { return this.activeHTMLRequest.promise; } - public addOnMessageListener(callback: (message: string, payload: any) => void) { + // tslint:disable-next-line:no-any + public addMessageListener(callback: (message: string, payload: any) => void) { // Test only //if (!isTestExecution()) { //throw new Error('getHTMLById to be run only in test code'); @@ -116,7 +118,8 @@ export abstract class WebviewHost implements IDisposable { this.onMessageListeners.push(callback); } - public removeOnMessageListener(callback: (message: string, payload: any) => void) { + // tslint:disable-next-line:no-any + public removeMessageListener(callback: (message: string, payload: any) => void) { // Test only //if (!isTestExecution()) { //throw new Error('getHTMLById to be run only in test code'); diff --git a/src/test/datascience/vscodeTestHelpers.ts b/src/test/datascience/vscodeTestHelpers.ts index 5005a538fce..94a649a767c 100644 --- a/src/test/datascience/vscodeTestHelpers.ts +++ b/src/test/datascience/vscodeTestHelpers.ts @@ -1,8 +1,8 @@ import { createDeferred } from '../../client/common/utils/async'; interface IOnMessageListener { - addOnMessageListener(callback: (message: string, payload: any) => void): void; - removeOnMessageListener(callback: (message: string, payload: any) => void): void; + addMessageListener(callback: (message: string, payload: any) => void): void; + removeMessageListener(callback: (message: string, payload: any) => void): void; } export type WaitForMessageOptions = { @@ -27,6 +27,7 @@ export type WaitForMessageOptions = { withPayload?(payload: any): boolean; }; +// This class is for usage in .vscode test to hook up to webviews which expose the addMessageListener and removeMessageListener functions export class OnMessageListener { private target: IOnMessageListener; constructor(target: IOnMessageListener) { @@ -85,10 +86,10 @@ export class OnMessageListener { } public addMessageListener(callback: (m: string, p: any) => void) { - this.target.addOnMessageListener(callback); + this.target.addMessageListener(callback); } public removeMessageListener(callback: (m: string, p: any) => void) { - this.target.removeOnMessageListener(callback); + this.target.removeMessageListener(callback); } } From 676d7ddfdd0a1ae886bae7e8c1f2e02465d3f44c Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 16:30:47 -0800 Subject: [PATCH 11/21] remove TESTCOMMAND --- package.json | 7 +------ src/client/common/application/commands.ts | 1 - .../datascience/commands/commandRegistry.ts | 15 +-------------- src/client/datascience/constants.ts | 1 - .../commands/commandRegistry.unit.test.ts | 5 +---- 5 files changed, 3 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 6f1eab231e8..9b55b8f5079 100644 --- a/package.json +++ b/package.json @@ -704,12 +704,7 @@ "command": "jupyter.openVariableView", "title": "%jupyter.command.jupyter.openVariableView.title%", "category": "Jupyter" - }, - { - "command": "jupyter.TESTCOMMAND", - "title": "TESTCOMMAND", - "category": "Jupyter" - } + } ], "menus": { "editor/context": [ diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index edb72754123..a2258a94cc5 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -60,7 +60,6 @@ interface ICommandNameWithoutArgumentTypeMapping { [DSCommands.EnableDebugLogging]: []; [DSCommands.ResetLoggingLevel]: []; [DSCommands.OpenVariableView]: []; - [DSCommands.TESTCOMMAND]: []; } /** diff --git a/src/client/datascience/commands/commandRegistry.ts b/src/client/datascience/commands/commandRegistry.ts index 32241b797a8..70303b8a62c 100644 --- a/src/client/datascience/commands/commandRegistry.ts +++ b/src/client/datascience/commands/commandRegistry.ts @@ -30,7 +30,6 @@ import { IJupyterVariableDataProviderFactory, INotebookEditorProvider } from '../types'; -import { IVariableViewProvider } from '../variablesView/types'; import { JupyterCommandLineSelectorCommand } from './commandLineSelector'; import { ExportCommands } from './exportCommands'; import { NotebookCommands } from './notebookCommands'; @@ -61,8 +60,7 @@ export class CommandRegistry implements IDisposable { @inject(IJupyterVariableDataProviderFactory) private readonly jupyterVariableDataProviderFactory: IJupyterVariableDataProviderFactory, @inject(IDataViewerFactory) private readonly dataViewerFactory: IDataViewerFactory, - @inject(IJupyterServerUriStorage) private readonly serverUriStorage: IJupyterServerUriStorage, - @inject(IVariableViewProvider) private readonly variableViewProvider: IVariableViewProvider + @inject(IJupyterServerUriStorage) private readonly serverUriStorage: IJupyterServerUriStorage ) { this.disposables.push(this.serverSelectedCommand); this.disposables.push(this.notebookCommands); @@ -119,7 +117,6 @@ export class CommandRegistry implements IDisposable { ); this.registerCommand(Commands.ClearSavedJupyterUris, this.clearJupyterUris); this.registerCommand(Commands.OpenVariableView, this.openVariableView); - this.registerCommand(Commands.TESTCOMMAND, this.handleTESTCOMMAND); if (this.commandListeners) { this.commandListeners.forEach((listener: IDataScienceCommandListener) => { listener.register(this.commandManager); @@ -130,16 +127,6 @@ export class CommandRegistry implements IDisposable { this.disposables.forEach((d) => d.dispose()); } - // IANHU Just for testing, also remove injections required here - private async handleTESTCOMMAND(): Promise { - const variableView = await this.variableViewProvider.activeVariableView; - - if (variableView) { - const result = await variableView.getHTMLById('variable-view-main-panel'); - traceError(result); - } - } - private registerCommand< E extends keyof ICommandNameArgumentTypeMapping, U extends ICommandNameArgumentTypeMapping[E] diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 4616a6e6abe..13980f99a75 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -132,7 +132,6 @@ export namespace Commands { export const ShowDataViewer = 'jupyter.showDataViewer'; export const ClearSavedJupyterUris = 'jupyter.clearSavedJupyterUris'; export const OpenVariableView = 'jupyter.openVariableView'; - export const TESTCOMMAND = 'jupyter.TESTCOMMAND'; } export namespace CodeLensCommands { diff --git a/src/test/datascience/commands/commandRegistry.unit.test.ts b/src/test/datascience/commands/commandRegistry.unit.test.ts index 36b9e394854..6bcf64fa531 100644 --- a/src/test/datascience/commands/commandRegistry.unit.test.ts +++ b/src/test/datascience/commands/commandRegistry.unit.test.ts @@ -19,7 +19,6 @@ import { JupyterVariableDataProviderFactory } from '../../../client/datascience/ import { DataScienceCodeLensProvider } from '../../../client/datascience/editor-integration/codelensprovider'; import { JupyterServerUriStorage } from '../../../client/datascience/jupyter/serverUriStorage'; import { NativeEditorProvider } from '../../../client/datascience/notebookStorage/nativeEditorProvider'; -import { IVariableViewProvider } from '../../../client/datascience/variablesView/types'; import { MockOutputChannel } from '../../mockClasses'; // tslint:disable: max-func-body-length @@ -46,7 +45,6 @@ suite('DataScience - Commands', () => { const dataViewerFactory = mock(DataViewerFactory); const fileSystem = mock(FileSystem); const serverUriStorage = mock(JupyterServerUriStorage); - const varViewProvider = mock(); commandRegistry = new CommandRegistry( documentManager, @@ -65,8 +63,7 @@ suite('DataScience - Commands', () => { instance(fileSystem), instance(jupyterVariableDataProviderFactory), instance(dataViewerFactory), - instance(serverUriStorage), - instance(varViewProvider) + instance(serverUriStorage) ); }); From 31190f5d62e4b7c59284cb580bb1ae2019263dd5 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 16:47:30 -0800 Subject: [PATCH 12/21] test only checks added in --- .vscode/launch.json | 1 - .../common/application/webviews/webview.ts | 3 +- src/client/datascience/variablesView/types.ts | 2 - .../variablesView/variableViewProvider.ts | 39 +++---------------- .../datascience/webviews/webviewHost.ts | 24 ++++++------ 5 files changed, 19 insertions(+), 50 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 106ae197aa2..abc82cbfdea 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -31,7 +31,6 @@ "XVSC_JUPYTER_LOG_TELEMETRY": "1", // Enable this to log IPYWIDGET messages "VSC_JUPYTER_LOG_IPYWIDGETS": "1", - "VSC_JUPYTER_WEBVIEW_TEST_MIDDLEWARE": "true", // Initialize to create the webview test middleware // Enable this to log debugger output. Directory must exist ahead of time "XDEBUGPY_LOG_DIR": "${workspaceRoot}/tmp/Debug_Output_Ex" }, diff --git a/src/client/common/application/webviews/webview.ts b/src/client/common/application/webviews/webview.ts index 97d8de7d9e9..80b8e8b98f3 100644 --- a/src/client/common/application/webviews/webview.ts +++ b/src/client/common/application/webviews/webview.ts @@ -93,7 +93,8 @@ export abstract class Webview implements IWebview { ) .toString(); - const forceTestMiddleware = process.env.VSC_JUPYTER_WEBVIEW_TEST_MIDDLEWARE; + // Check to see if we should force on Test middleware for our react code + const forceTestMiddleware = process.env.VSC_JUPYTER_WEBVIEW_TEST_MIDDLEWARE || 'false'; return ` diff --git a/src/client/datascience/variablesView/types.ts b/src/client/datascience/variablesView/types.ts index dec2aa231dc..26089d909d9 100644 --- a/src/client/datascience/variablesView/types.ts +++ b/src/client/datascience/variablesView/types.ts @@ -41,7 +41,5 @@ export interface INotebookWatcher { export const IVariableViewProvider = Symbol('IVariableViewProvider'); export interface IVariableViewProvider extends IVSCWebviewViewProvider { - //activeVariableView?: VariableView; - //readonly onDidResolveWebview: Event; readonly activeVariableView: Promise; } diff --git a/src/client/datascience/variablesView/variableViewProvider.ts b/src/client/datascience/variablesView/variableViewProvider.ts index 9758f3248f1..0057cb740ec 100644 --- a/src/client/datascience/variablesView/variableViewProvider.ts +++ b/src/client/datascience/variablesView/variableViewProvider.ts @@ -5,6 +5,7 @@ import { inject, injectable, named } from 'inversify'; import { CancellationToken, WebviewView, WebviewViewResolveContext } from 'vscode'; import { IApplicationShell, IWebviewViewProvider, IWorkspaceService } from '../../common/application/types'; +import { isTestExecution } from '../../common/constants'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; import { createDeferred, Deferred } from '../../common/utils/async'; import { Identifiers } from '../constants'; @@ -20,9 +21,9 @@ export class VariableViewProvider implements IVariableViewProvider { public get activeVariableView(): Promise { // For test execution only - //if (!isTestExecution()) { - //throw new Error('activeVariableView only accessible from test code'); - //} + if (!isTestExecution()) { + throw new Error('activeVariableView only accessible from test code'); + } // If we have already created the view, then just return it if (this.variableView) { @@ -33,39 +34,8 @@ export class VariableViewProvider implements IVariableViewProvider { this.activeVariableViewPromise = createDeferred(); return this.activeVariableViewPromise.promise; } - private activeVariableViewPromise?: Deferred; - // Just for test execution allow for accessing the VariableView off of the provider - //public get activeVariableView(): VariableView | undefined { - ////if (!isTestExecution()) { - ////throw new Error('activeVariableView only accessible from test code'); - ////} - - //return this.variableView; - //} - - //public get onDidResolveWebview(): Event { - ////if (!isTestExecution()) { - ////throw new Error('Access only for tests'); - ////} - //return this._onDidResolveWebview.event; - //} - //private readonly _onDidResolveWebview = new EventEmitter(); - - //public get onDidChangeActiveVariableViewNotebook(): Event { - //return this._onDidChangeActiveVariableViewNotebook.event; - //} - //public get onDidExecuteActiveVariableViewNotebook(): Event<{ executionCount: number }> { - //return this._onDidExecuteActiveVariableViewNotebook.event; - //} - //public get activeVariableViewNotebook(): INotebook | undefined { - //return this.notebookEditorProvider.activeEditor?.notebook; - //} - - //private readonly _onDidExecuteActiveVariableViewNotebook = new EventEmitter<{ executionCount: number }>(); - //private readonly _onDidChangeActiveVariableViewNotebook = new EventEmitter(); - private variableView?: VariableView; constructor( @@ -105,6 +75,7 @@ export class VariableViewProvider implements IVariableViewProvider { this.notebookWatcher ); + // If someone is waiting for the variable view resolve that here if (this.activeVariableViewPromise) { this.activeVariableViewPromise.resolve(this.variableView); } diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index ca91459925a..0dcaf24d6c3 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -89,41 +89,41 @@ export abstract class WebviewHost implements IDisposable { } } - // This function is used for testing webview by fetching HTML from the webview - // only to be use for testing + // This function is used for testing webview by fetching HTML from the webview via a message public getHTMLById(id: string): Promise { // Test only - //if (!isTestExecution()) { - //throw new Error('getHTMLById to be run only in test code'); - //} + if (!isTestExecution()) { + throw new Error('getHTMLById to be run only in test code'); + } if (!this.activeHTMLRequest) { this.activeHTMLRequest = createDeferred(); this.postMessageInternal(InteractiveWindowMessages.GetHTMLByIdRequest, id).ignoreErrors(); } else { - // No localization for test only fuction throw new Error('getHTMLById request already in progress'); } return this.activeHTMLRequest.promise; } + // For testing add a callback listening to messages from the webview // tslint:disable-next-line:no-any public addMessageListener(callback: (message: string, payload: any) => void) { // Test only - //if (!isTestExecution()) { - //throw new Error('getHTMLById to be run only in test code'); - //} + if (!isTestExecution()) { + throw new Error('addMessageListener to be run only in test code'); + } this.onMessageListeners.push(callback); } + // For testing remove a callback listening to messages from the webview // tslint:disable-next-line:no-any public removeMessageListener(callback: (message: string, payload: any) => void) { // Test only - //if (!isTestExecution()) { - //throw new Error('getHTMLById to be run only in test code'); - //} + if (!isTestExecution()) { + throw new Error('removeMessageListener to be run only in test code'); + } const index = this.onMessageListeners.indexOf(callback); if (index >= 0) { this.onMessageListeners.splice(index, 1); From 81f760a75a9d014da92d06e46941c246a5b24bad Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 17:02:15 -0800 Subject: [PATCH 13/21] lots of commenting --- src/client/datascience/commands/commandRegistry.ts | 1 - .../datascience/variablesView/variableViewProvider.ts | 1 + src/client/datascience/webviews/webviewHost.ts | 4 ++-- .../interactive-common/redux/reducers/commonEffects.ts | 1 + .../variableView/variableView.vscode.test.ts | 10 +++++++--- .../datascience/variableView/variableViewHelpers.ts | 9 +++++++-- src/test/datascience/vscodeTestHelpers.ts | 9 +++++++++ 7 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/client/datascience/commands/commandRegistry.ts b/src/client/datascience/commands/commandRegistry.ts index 70303b8a62c..f0b2dcb2438 100644 --- a/src/client/datascience/commands/commandRegistry.ts +++ b/src/client/datascience/commands/commandRegistry.ts @@ -126,7 +126,6 @@ export class CommandRegistry implements IDisposable { public dispose() { this.disposables.forEach((d) => d.dispose()); } - private registerCommand< E extends keyof ICommandNameArgumentTypeMapping, U extends ICommandNameArgumentTypeMapping[E] diff --git a/src/client/datascience/variablesView/variableViewProvider.ts b/src/client/datascience/variablesView/variableViewProvider.ts index 0057cb740ec..9fa1fddce98 100644 --- a/src/client/datascience/variablesView/variableViewProvider.ts +++ b/src/client/datascience/variablesView/variableViewProvider.ts @@ -19,6 +19,7 @@ import { VariableView } from './variableView'; export class VariableViewProvider implements IVariableViewProvider { public readonly viewType = 'jupyterViewVariables'; + // Either return the active variable view or wait until it's created and return it public get activeVariableView(): Promise { // For test execution only if (!isTestExecution()) { diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index 0dcaf24d6c3..71d189abe39 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -33,7 +33,6 @@ export abstract class WebviewHost implements IDisposable { protected abstract get owningResource(): Resource; protected abstract get title(): string; - protected webview?: IWebview; protected disposed = false; @@ -172,6 +171,7 @@ export abstract class WebviewHost implements IDisposable { break; case InteractiveWindowMessages.GetHTMLByIdResponse: + // Webview has returned HTML, resolve the request and clear it if (this.activeHTMLRequest) { this.activeHTMLRequest.resolve(payload); this.activeHTMLRequest = undefined; @@ -182,7 +182,7 @@ export abstract class WebviewHost implements IDisposable { break; } - // Broadcast to onMessage listeners + // Broadcast to any onMessage listeners this.onMessageListeners.forEach((listener) => { listener(message, payload); }); diff --git a/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts b/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts index 7089ae67249..bd2c379d388 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts @@ -319,6 +319,7 @@ export namespace CommonEffects { }; } + // Extension has requested HTML for the webview, get it by ID and send it back as a message export function getHTMLByIdRequest(arg: CommonReducerArg): IMainState { const element = document.getElementById(arg.payload.data); diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index 9edcb90bf31..e1f0b256461 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; import { assert } from 'chai'; import * as sinon from 'sinon'; import { ICommandManager, IVSCodeNotebook } from '../../../client/common/application/types'; @@ -47,7 +50,6 @@ suite('DataScience - VariableView', () => { editorProvider = api.serviceContainer.get(VSCodeNotebookProvider); }); setup(async function () { - console.log('**** Start variableView setup ****'); sinon.restore(); // Create an editor to use for our tests @@ -64,7 +66,7 @@ suite('DataScience - VariableView', () => { // Cleanup after suite is finished suiteTeardown(() => closeNotebooksAndCleanUpAfterTests(disposables)); - // Test showing the variable view + // Test showing the basic variable view with a value or two test('Can show VariableView', async function () { // Add one simple cell and execute it await insertCodeCell('test = "MYTESTVALUE"', { index: 0 }); @@ -75,7 +77,7 @@ suite('DataScience - VariableView', () => { // Send the command to open the view commandManager.executeCommand(Commands.OpenVariableView); - // Now check to see if we can actually look at the variable view + // Aquire the variable view from the provider const variableView = await variableViewProvider.activeVariableView; // Add our message listener @@ -86,10 +88,12 @@ suite('DataScience - VariableView', () => { const cell2 = vscodeNotebook.activeNotebookEditor?.document.cells![1]!; await executeCell(cell2); + // Wait until our VariablesComplete message to see that we have the new variables and have rendered them await onMessageListener.waitForMessage(InteractiveWindowMessages.VariablesComplete); const htmlResult = await variableView?.getHTMLById('variable-view-main-panel'); + // Parse the HTML for our expected variables const expectedVariables = [ { name: 'test', type: 'str', length: '11', value: ' MYTESTVALUE' }, { name: 'test2', type: 'str', length: '12', value: ' MYTESTVALUE2' } diff --git a/src/test/datascience/variableView/variableViewHelpers.ts b/src/test/datascience/variableView/variableViewHelpers.ts index 66ecb95e768..dfa2ef79551 100644 --- a/src/test/datascience/variableView/variableViewHelpers.ts +++ b/src/test/datascience/variableView/variableViewHelpers.ts @@ -1,5 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; import { expect } from 'chai'; +// Basic shape of a variable result export interface IVariableInfo { name: string; type: string; @@ -20,7 +24,7 @@ export function verifyViewVariables(expected: IVariableInfo[], html: string) { } // Helper function to parse the view HTML -export function parseVariableViewHTML(html: string): IVariableInfo[] { +function parseVariableViewHTML(html: string): IVariableInfo[] { const parser = new DOMParser(); const htmlDoc = parser.parseFromString(html, 'text/html'); const variableRows = htmlDoc.getElementsByClassName('react-grid-Row'); @@ -34,6 +38,7 @@ export function parseVariableViewHTML(html: string): IVariableInfo[] { return variableInfos; } +// From a single row pull out the values we care about function extractVariableFromRow(variableHTMLRow: Element): IVariableInfo { const cellElements = variableHTMLRow.querySelectorAll('[role=cell]'); return { @@ -44,7 +49,7 @@ function extractVariableFromRow(variableHTMLRow: Element): IVariableInfo { }; } +// Compare two variable infos function compareVariableInfos(expected: IVariableInfo, actual: IVariableInfo) { - //expect({ a: 1 }).to.deep.equal({ a: 1 }); expect(expected).to.deep.equal(actual, 'Found Variable incorrect'); } diff --git a/src/test/datascience/vscodeTestHelpers.ts b/src/test/datascience/vscodeTestHelpers.ts index 94a649a767c..d33cc2974a1 100644 --- a/src/test/datascience/vscodeTestHelpers.ts +++ b/src/test/datascience/vscodeTestHelpers.ts @@ -1,5 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; import { createDeferred } from '../../client/common/utils/async'; +// Basic shape that something needs to support to hook up to this interface IOnMessageListener { addMessageListener(callback: (message: string, payload: any) => void): void; removeMessageListener(callback: (message: string, payload: any) => void): void; @@ -33,6 +37,8 @@ export class OnMessageListener { constructor(target: IOnMessageListener) { this.target = target; } + + // For our target object wait for a specific message to come in from onMessage function public async waitForMessage(message: string, options?: WaitForMessageOptions): Promise { const timeoutMs = options && options.timeoutMs ? options.timeoutMs : undefined; const numberOfTimes = options && options.numberOfTimes ? options.numberOfTimes : 1; @@ -49,6 +55,7 @@ export class OnMessageListener { : undefined; let timesMessageReceived = 0; const dispatchedAction = `DISPATCHED_ACTION_${message}`; + // Create the handler that we will hook up to the on message listener handler = (m: string, payload: any) => { if (m === message || m === dispatchedAction) { // First verify the payload matches @@ -85,10 +92,12 @@ export class OnMessageListener { return promise.promise; } + // Add the callback on the target public addMessageListener(callback: (m: string, p: any) => void) { this.target.addMessageListener(callback); } + // Remove the callback on the target public removeMessageListener(callback: (m: string, p: any) => void) { this.target.removeMessageListener(callback); } From bc8bd9d338b33acb95fc51dd67a0771fc084bd87 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 17:03:46 -0800 Subject: [PATCH 14/21] add news --- news/3 Code Health/4355.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/3 Code Health/4355.md diff --git a/news/3 Code Health/4355.md b/news/3 Code Health/4355.md new file mode 100644 index 00000000000..bf21c4f1128 --- /dev/null +++ b/news/3 Code Health/4355.md @@ -0,0 +1 @@ +Add .vscode tests to test the new variable view \ No newline at end of file From 3cf08006fa2a9d4ee05dd55bd5a94c0d959d4d61 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 14 Jan 2021 17:06:39 -0800 Subject: [PATCH 15/21] remove variableView grep --- .vscode/launch.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index abc82cbfdea..310ba276c39 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -201,8 +201,7 @@ "--extensionTestsPath=${workspaceFolder}/out/test" ], "env": { - "XVSC_JUPYTER_CI_TEST_GREP": "VSCode Notebook", // Leave as `VSCode Notebook` to run only Notebook tests. - "VSC_JUPYTER_CI_TEST_GREP": "VariableView", // Leave as `VSCode Notebook` to run only Notebook tests. + "VSC_JUPYTER_CI_TEST_GREP": "VSCode Notebook", // Leave as `VSCode Notebook` to run only Notebook tests. "VSC_JUPYTER_CI_TEST_INVERT_GREP": "", // Initialize this to invert the grep (exclude tests with value defined in grep). "CI_PYTHON_PATH": "", // Update with path to real python interpereter used for testing. "VSC_FORCE_REAL_JUPYTER": "true", // Enalbe tests that require Jupyter. From ede710e8d1d72476e25c73c2c53b0af53dae6eeb Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Fri, 15 Jan 2021 11:16:02 -0800 Subject: [PATCH 16/21] lint fixes --- .../datascience/variableView/variableView.vscode.test.ts | 2 +- src/test/datascience/vscodeTestHelpers.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index e1f0b256461..21e86661f47 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -75,7 +75,7 @@ suite('DataScience - VariableView', () => { await waitForExecutionCompletedSuccessfully(cell); // Send the command to open the view - commandManager.executeCommand(Commands.OpenVariableView); + await commandManager.executeCommand(Commands.OpenVariableView); // Aquire the variable view from the provider const variableView = await variableViewProvider.activeVariableView; diff --git a/src/test/datascience/vscodeTestHelpers.ts b/src/test/datascience/vscodeTestHelpers.ts index d33cc2974a1..8e07027552d 100644 --- a/src/test/datascience/vscodeTestHelpers.ts +++ b/src/test/datascience/vscodeTestHelpers.ts @@ -5,7 +5,9 @@ import { createDeferred } from '../../client/common/utils/async'; // Basic shape that something needs to support to hook up to this interface IOnMessageListener { + // eslint-disable-next-line @typescript-eslint/no-explicit-any addMessageListener(callback: (message: string, payload: any) => void): void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any removeMessageListener(callback: (message: string, payload: any) => void): void; } @@ -27,7 +29,7 @@ export type WaitForMessageOptions = { // Optional check for the payload of the message // will only return (or count) message if this returns true - // tslint:disable-next-line: no-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any withPayload?(payload: any): boolean; }; @@ -45,6 +47,7 @@ export class OnMessageListener { const promise = createDeferred(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any let handler: (m: string, p: any) => void; const timer = timeoutMs ? setTimeout(() => { @@ -56,6 +59,7 @@ export class OnMessageListener { let timesMessageReceived = 0; const dispatchedAction = `DISPATCHED_ACTION_${message}`; // Create the handler that we will hook up to the on message listener + // eslint-disable-next-line @typescript-eslint/no-explicit-any handler = (m: string, payload: any) => { if (m === message || m === dispatchedAction) { // First verify the payload matches @@ -93,11 +97,13 @@ export class OnMessageListener { } // Add the callback on the target + // eslint-disable-next-line @typescript-eslint/no-explicit-any public addMessageListener(callback: (m: string, p: any) => void) { this.target.addMessageListener(callback); } // Remove the callback on the target + // eslint-disable-next-line @typescript-eslint/no-explicit-any public removeMessageListener(callback: (m: string, p: any) => void) { this.target.removeMessageListener(callback); } From a21967e1a792b0548cc5adbae778b48e7deee900 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Fri, 15 Jan 2021 13:30:42 -0800 Subject: [PATCH 17/21] test decorator and provider interface --- .vscode/launch.json | 3 ++- src/client/common/utils/testOnlyDecorator.ts | 15 +++++++++++++++ src/client/datascience/variablesView/types.ts | 3 +-- .../variablesView/variableViewProvider.ts | 12 +++++------- src/client/datascience/webviews/webviewHost.ts | 18 ++++-------------- src/test/datascience/testInterfaces.ts | 5 +++++ .../variableView/variableView.vscode.test.ts | 9 ++++++--- .../variableView/variableViewTestInterfaces.ts | 10 ++++++++++ 8 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 src/client/common/utils/testOnlyDecorator.ts create mode 100644 src/test/datascience/testInterfaces.ts create mode 100644 src/test/datascience/variableView/variableViewTestInterfaces.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 310ba276c39..abc82cbfdea 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -201,7 +201,8 @@ "--extensionTestsPath=${workspaceFolder}/out/test" ], "env": { - "VSC_JUPYTER_CI_TEST_GREP": "VSCode Notebook", // Leave as `VSCode Notebook` to run only Notebook tests. + "XVSC_JUPYTER_CI_TEST_GREP": "VSCode Notebook", // Leave as `VSCode Notebook` to run only Notebook tests. + "VSC_JUPYTER_CI_TEST_GREP": "VariableView", // Leave as `VSCode Notebook` to run only Notebook tests. "VSC_JUPYTER_CI_TEST_INVERT_GREP": "", // Initialize this to invert the grep (exclude tests with value defined in grep). "CI_PYTHON_PATH": "", // Update with path to real python interpereter used for testing. "VSC_FORCE_REAL_JUPYTER": "true", // Enalbe tests that require Jupyter. diff --git a/src/client/common/utils/testOnlyDecorator.ts b/src/client/common/utils/testOnlyDecorator.ts new file mode 100644 index 00000000000..4224ac674e6 --- /dev/null +++ b/src/client/common/utils/testOnlyDecorator.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +import { isTestExecution } from '../constants'; + +// This decorator can be added to any method to make sure that it only runs under test execution +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function testOnlyMethod(_target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) { + if (!isTestExecution()) { + throw new Error(`Function: ${propertyKey} can only be called from test code`); + } + + return descriptor; +} diff --git a/src/client/datascience/variablesView/types.ts b/src/client/datascience/variablesView/types.ts index 26089d909d9..63cac205656 100644 --- a/src/client/datascience/variablesView/types.ts +++ b/src/client/datascience/variablesView/types.ts @@ -7,7 +7,6 @@ import { } from '../../datascience/interactive-common/interactiveWindowTypes'; import { CssMessages, IGetCssRequest, IGetCssResponse, SharedMessages } from '../messages'; import { IJupyterVariablesRequest, IJupyterVariablesResponse, INotebook, IVSCWebviewViewProvider } from '../types'; -import { VariableView } from './variableView'; // Mapping of Message to payload that our VariableViewPanel needs to support export class IVariableViewPanelMapping { @@ -41,5 +40,5 @@ export interface INotebookWatcher { export const IVariableViewProvider = Symbol('IVariableViewProvider'); export interface IVariableViewProvider extends IVSCWebviewViewProvider { - readonly activeVariableView: Promise; + //readonly activeVariableView: Promise; } diff --git a/src/client/datascience/variablesView/variableViewProvider.ts b/src/client/datascience/variablesView/variableViewProvider.ts index 9fa1fddce98..a096b0fe6a8 100644 --- a/src/client/datascience/variablesView/variableViewProvider.ts +++ b/src/client/datascience/variablesView/variableViewProvider.ts @@ -5,9 +5,9 @@ import { inject, injectable, named } from 'inversify'; import { CancellationToken, WebviewView, WebviewViewResolveContext } from 'vscode'; import { IApplicationShell, IWebviewViewProvider, IWorkspaceService } from '../../common/application/types'; -import { isTestExecution } from '../../common/constants'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; import { createDeferred, Deferred } from '../../common/utils/async'; +import { testOnlyMethod } from '../../common/utils/testOnlyDecorator'; import { Identifiers } from '../constants'; import { IDataViewerFactory } from '../data-viewing/types'; import { ICodeCssGenerator, IJupyterVariableDataProviderFactory, IJupyterVariables, IThemeFinder } from '../types'; @@ -20,12 +20,10 @@ export class VariableViewProvider implements IVariableViewProvider { public readonly viewType = 'jupyterViewVariables'; // Either return the active variable view or wait until it's created and return it - public get activeVariableView(): Promise { - // For test execution only - if (!isTestExecution()) { - throw new Error('activeVariableView only accessible from test code'); - } - + /* eslint-disable-next-line @typescript-eslint/no-unused-expressions */ + @testOnlyMethod + // @ts-ignore Property will be accessed in test code via casting to ITestVariableViewProviderInterface + private get activeVariableView(): Promise { // If we have already created the view, then just return it if (this.variableView) { return Promise.resolve(this.variableView); diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index c069087b251..982a0849c08 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -27,6 +27,7 @@ import { DefaultTheme, PythonExtension, Telemetry } from '../constants'; import { InteractiveWindowMessages } from '../interactive-common/interactiveWindowTypes'; import { CssMessages, IGetCssRequest, IGetMonacoThemeRequest, SharedMessages } from '../messages'; import { ICodeCssGenerator, IJupyterExtraSettings, IThemeFinder } from '../types'; +import { testOnlyMethod } from '../../common/utils/testOnlyDecorator'; @injectable() // For some reason this is necessary to get the class hierarchy to work. export abstract class WebviewHost implements IDisposable { @@ -89,12 +90,8 @@ export abstract class WebviewHost implements IDisposable { } // This function is used for testing webview by fetching HTML from the webview via a message + @testOnlyMethod public getHTMLById(id: string): Promise { - // Test only - if (!isTestExecution()) { - throw new Error('getHTMLById to be run only in test code'); - } - if (!this.activeHTMLRequest) { this.activeHTMLRequest = createDeferred(); this.postMessageInternal(InteractiveWindowMessages.GetHTMLByIdRequest, id).ignoreErrors(); @@ -107,22 +104,15 @@ export abstract class WebviewHost implements IDisposable { // For testing add a callback listening to messages from the webview // tslint:disable-next-line:no-any + @testOnlyMethod public addMessageListener(callback: (message: string, payload: any) => void) { - // Test only - if (!isTestExecution()) { - throw new Error('addMessageListener to be run only in test code'); - } - this.onMessageListeners.push(callback); } // For testing remove a callback listening to messages from the webview // tslint:disable-next-line:no-any + @testOnlyMethod public removeMessageListener(callback: (message: string, payload: any) => void) { - // Test only - if (!isTestExecution()) { - throw new Error('removeMessageListener to be run only in test code'); - } const index = this.onMessageListeners.indexOf(callback); if (index >= 0) { this.onMessageListeners.splice(index, 1); diff --git a/src/test/datascience/testInterfaces.ts b/src/test/datascience/testInterfaces.ts new file mode 100644 index 00000000000..1c0b9f42bc0 --- /dev/null +++ b/src/test/datascience/testInterfaces.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +// Interfaces here to expose specific private functionality to test code diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index 21e86661f47..cb25c8020a9 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -25,16 +25,17 @@ import { INotebookEditorProvider } from '../../../client/datascience/types'; import { OnMessageListener } from '../vscodeTestHelpers'; import { InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; import { verifyViewVariables } from './variableViewHelpers'; +import { ITestVariableViewProvider } from './variableViewTestInterfaces'; suite('DataScience - VariableView', () => { let api: IExtensionTestApi; const disposables: IDisposable[] = []; let commandManager: ICommandManager; - let variableViewProvider: IVariableViewProvider; + let variableViewProvider: ITestVariableViewProvider; let editorProvider: INotebookEditorProvider; let vscodeNotebook: IVSCodeNotebook; suiteSetup(async function () { - this.timeout(120_000); // IANHU: From other tests? Reduce this? + this.timeout(120_000); api = await initialize(); // Don't run if we can't use the native notebook interface @@ -45,7 +46,9 @@ suite('DataScience - VariableView', () => { await startJupyter(true); sinon.restore(); commandManager = api.serviceContainer.get(ICommandManager); - variableViewProvider = api.serviceContainer.get(IVariableViewProvider); + const coreVariableViewProvider = api.serviceContainer.get(IVariableViewProvider); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + variableViewProvider = (coreVariableViewProvider as any) as ITestVariableViewProvider; // Cast to expose the test interfaces vscodeNotebook = api.serviceContainer.get(IVSCodeNotebook); editorProvider = api.serviceContainer.get(VSCodeNotebookProvider); }); diff --git a/src/test/datascience/variableView/variableViewTestInterfaces.ts b/src/test/datascience/variableView/variableViewTestInterfaces.ts new file mode 100644 index 00000000000..bdc793e017b --- /dev/null +++ b/src/test/datascience/variableView/variableViewTestInterfaces.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +import { IVariableViewProvider } from '../../../client/datascience/variablesView/types'; +import { VariableView } from '../../../client/datascience/variablesView/variableView'; + +export interface ITestVariableViewProvider extends IVariableViewProvider { + readonly activeVariableView: Promise; +} From 0730ef9872f2dc5dc18fd4f0922acfaae650395d Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Fri, 15 Jan 2021 13:42:27 -0800 Subject: [PATCH 18/21] test interface for webview host --- src/client/datascience/webviews/webviewHost.ts | 9 ++++++--- src/test/datascience/testInterfaces.ts | 7 +++++++ .../datascience/variableView/variableView.vscode.test.ts | 5 ++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index 982a0849c08..430160870e2 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -91,7 +91,8 @@ export abstract class WebviewHost implements IDisposable { // This function is used for testing webview by fetching HTML from the webview via a message @testOnlyMethod - public getHTMLById(id: string): Promise { + // @ts-ignore Property will be accessed in test code via casting to ITestWebviewHost + private getHTMLById(id: string): Promise { if (!this.activeHTMLRequest) { this.activeHTMLRequest = createDeferred(); this.postMessageInternal(InteractiveWindowMessages.GetHTMLByIdRequest, id).ignoreErrors(); @@ -105,14 +106,16 @@ export abstract class WebviewHost implements IDisposable { // For testing add a callback listening to messages from the webview // tslint:disable-next-line:no-any @testOnlyMethod - public addMessageListener(callback: (message: string, payload: any) => void) { + // @ts-ignore Property will be accessed in test code via casting to ITestWebviewHost + private addMessageListener(callback: (message: string, payload: any) => void) { this.onMessageListeners.push(callback); } // For testing remove a callback listening to messages from the webview // tslint:disable-next-line:no-any @testOnlyMethod - public removeMessageListener(callback: (message: string, payload: any) => void) { + // @ts-ignore Property will be accessed in test code via casting to ITestWebviewHost + private removeMessageListener(callback: (message: string, payload: any) => void) { const index = this.onMessageListeners.indexOf(callback); if (index >= 0) { this.onMessageListeners.splice(index, 1); diff --git a/src/test/datascience/testInterfaces.ts b/src/test/datascience/testInterfaces.ts index 1c0b9f42bc0..8b266112bd7 100644 --- a/src/test/datascience/testInterfaces.ts +++ b/src/test/datascience/testInterfaces.ts @@ -3,3 +3,10 @@ 'use strict'; // Interfaces here to expose specific private functionality to test code +export interface ITestWebviewHost { + getHTMLById(id: string): Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + addMessageListener(callback: (message: string, payload: any) => void): void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + removeMessageListener(callback: (message: string, payload: any) => void): void; +} diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index cb25c8020a9..844bf03af36 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -26,6 +26,7 @@ import { OnMessageListener } from '../vscodeTestHelpers'; import { InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; import { verifyViewVariables } from './variableViewHelpers'; import { ITestVariableViewProvider } from './variableViewTestInterfaces'; +import { ITestWebviewHost } from '../testInterfaces'; suite('DataScience - VariableView', () => { let api: IExtensionTestApi; @@ -81,7 +82,9 @@ suite('DataScience - VariableView', () => { await commandManager.executeCommand(Commands.OpenVariableView); // Aquire the variable view from the provider - const variableView = await variableViewProvider.activeVariableView; + const coreVariableView = await variableViewProvider.activeVariableView; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const variableView = (coreVariableView as any) as ITestWebviewHost; // Add our message listener const onMessageListener = new OnMessageListener(variableView); From 5d51678a93fa21d1aa8f4516865c3c2be0d0adcb Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Fri, 15 Jan 2021 15:14:33 -0800 Subject: [PATCH 19/21] decorator fixes and moving --- .vscode/launch.json | 3 +-- src/client/common/utils/decorators.ts | 17 +++++++++++++++++ src/client/common/utils/testOnlyDecorator.ts | 15 --------------- .../variablesView/variableViewProvider.ts | 7 ++++--- src/client/datascience/webviews/webviewHost.ts | 9 +++++---- .../interactive-common/redux/store.ts | 6 +++++- .../variableView/variableView.vscode.test.ts | 1 + 7 files changed, 33 insertions(+), 25 deletions(-) delete mode 100644 src/client/common/utils/testOnlyDecorator.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index abc82cbfdea..310ba276c39 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -201,8 +201,7 @@ "--extensionTestsPath=${workspaceFolder}/out/test" ], "env": { - "XVSC_JUPYTER_CI_TEST_GREP": "VSCode Notebook", // Leave as `VSCode Notebook` to run only Notebook tests. - "VSC_JUPYTER_CI_TEST_GREP": "VariableView", // Leave as `VSCode Notebook` to run only Notebook tests. + "VSC_JUPYTER_CI_TEST_GREP": "VSCode Notebook", // Leave as `VSCode Notebook` to run only Notebook tests. "VSC_JUPYTER_CI_TEST_INVERT_GREP": "", // Initialize this to invert the grep (exclude tests with value defined in grep). "CI_PYTHON_PATH": "", // Update with path to real python interpereter used for testing. "VSC_FORCE_REAL_JUPYTER": "true", // Enalbe tests that require Jupyter. diff --git a/src/client/common/utils/decorators.ts b/src/client/common/utils/decorators.ts index 403fcd8a912..085d255b658 100644 --- a/src/client/common/utils/decorators.ts +++ b/src/client/common/utils/decorators.ts @@ -244,3 +244,20 @@ export function trace(log: (c: CallInfo, t: TraceInfo) => void) { return descriptor; }; } + +// Mark a method to be used only in tests +export function testOnlyMethod() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return function (_target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) { + const originalMethod = descriptor.value; + // eslint-disable-next-line , @typescript-eslint/no-explicit-any + descriptor.value = function (...args: any[]) { + if (!isTestExecution()) { + throw new Error(`Function: ${propertyKey} can only be called from test code`); + } + return originalMethod.apply(this, args); + }; + + return descriptor; + }; +} diff --git a/src/client/common/utils/testOnlyDecorator.ts b/src/client/common/utils/testOnlyDecorator.ts deleted file mode 100644 index 4224ac674e6..00000000000 --- a/src/client/common/utils/testOnlyDecorator.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -import { isTestExecution } from '../constants'; - -// This decorator can be added to any method to make sure that it only runs under test execution -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function testOnlyMethod(_target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) { - if (!isTestExecution()) { - throw new Error(`Function: ${propertyKey} can only be called from test code`); - } - - return descriptor; -} diff --git a/src/client/datascience/variablesView/variableViewProvider.ts b/src/client/datascience/variablesView/variableViewProvider.ts index a096b0fe6a8..b10a5ee0d6a 100644 --- a/src/client/datascience/variablesView/variableViewProvider.ts +++ b/src/client/datascience/variablesView/variableViewProvider.ts @@ -5,9 +5,9 @@ import { inject, injectable, named } from 'inversify'; import { CancellationToken, WebviewView, WebviewViewResolveContext } from 'vscode'; import { IApplicationShell, IWebviewViewProvider, IWorkspaceService } from '../../common/application/types'; +import { isTestExecution } from '../../common/constants'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; import { createDeferred, Deferred } from '../../common/utils/async'; -import { testOnlyMethod } from '../../common/utils/testOnlyDecorator'; import { Identifiers } from '../constants'; import { IDataViewerFactory } from '../data-viewing/types'; import { ICodeCssGenerator, IJupyterVariableDataProviderFactory, IJupyterVariables, IThemeFinder } from '../types'; @@ -20,10 +20,11 @@ export class VariableViewProvider implements IVariableViewProvider { public readonly viewType = 'jupyterViewVariables'; // Either return the active variable view or wait until it's created and return it - /* eslint-disable-next-line @typescript-eslint/no-unused-expressions */ - @testOnlyMethod // @ts-ignore Property will be accessed in test code via casting to ITestVariableViewProviderInterface private get activeVariableView(): Promise { + if (!isTestExecution()) { + throw new Error('activeVariableView only for test code'); + } // If we have already created the view, then just return it if (this.variableView) { return Promise.resolve(this.variableView); diff --git a/src/client/datascience/webviews/webviewHost.ts b/src/client/datascience/webviews/webviewHost.ts index 430160870e2..f54dcc86ede 100644 --- a/src/client/datascience/webviews/webviewHost.ts +++ b/src/client/datascience/webviews/webviewHost.ts @@ -27,7 +27,7 @@ import { DefaultTheme, PythonExtension, Telemetry } from '../constants'; import { InteractiveWindowMessages } from '../interactive-common/interactiveWindowTypes'; import { CssMessages, IGetCssRequest, IGetMonacoThemeRequest, SharedMessages } from '../messages'; import { ICodeCssGenerator, IJupyterExtraSettings, IThemeFinder } from '../types'; -import { testOnlyMethod } from '../../common/utils/testOnlyDecorator'; +import { testOnlyMethod } from '../../common/utils/decorators'; @injectable() // For some reason this is necessary to get the class hierarchy to work. export abstract class WebviewHost implements IDisposable { @@ -90,7 +90,8 @@ export abstract class WebviewHost implements IDisposable { } // This function is used for testing webview by fetching HTML from the webview via a message - @testOnlyMethod + // @ts-ignore Property will be accessed in test code via casting to ITestWebviewHost + @testOnlyMethod() // @ts-ignore Property will be accessed in test code via casting to ITestWebviewHost private getHTMLById(id: string): Promise { if (!this.activeHTMLRequest) { @@ -105,7 +106,7 @@ export abstract class WebviewHost implements IDisposable { // For testing add a callback listening to messages from the webview // tslint:disable-next-line:no-any - @testOnlyMethod + @testOnlyMethod() // @ts-ignore Property will be accessed in test code via casting to ITestWebviewHost private addMessageListener(callback: (message: string, payload: any) => void) { this.onMessageListeners.push(callback); @@ -113,7 +114,7 @@ export abstract class WebviewHost implements IDisposable { // For testing remove a callback listening to messages from the webview // tslint:disable-next-line:no-any - @testOnlyMethod + @testOnlyMethod() // @ts-ignore Property will be accessed in test code via casting to ITestWebviewHost private removeMessageListener(callback: (message: string, payload: any) => void) { const index = this.onMessageListeners.indexOf(callback); diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index 765aca07302..e3d22a3cbfc 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -309,7 +309,11 @@ function createMiddleWare(testMode: boolean, postOffice: PostOffice): Redux.Midd // Or if testing in UI Test. // eslint-disable-next-line @typescript-eslint/no-explicit-any const isUITest = (postOffice.acquireApi() as any)?.handleMessage ? true : false; - const testMiddleware = forceTestMiddleware() || testMode || isUITest ? createTestMiddleware() : undefined; + let forceOnTestMiddleware = false; + if (typeof forceTestMiddleware !== undefined) { + forceOnTestMiddleware = forceTestMiddleware(); + } + const testMiddleware = forceOnTestMiddleware || testMode || isUITest ? createTestMiddleware() : undefined; // Create the logger if we're not in production mode or we're forcing logging const reduceLogMessage = ''; diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index 844bf03af36..b94fa75505d 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -72,6 +72,7 @@ suite('DataScience - VariableView', () => { // Test showing the basic variable view with a value or two test('Can show VariableView', async function () { + this.skip(); // Re-enable in CI when #4412 is fixed // Add one simple cell and execute it await insertCodeCell('test = "MYTESTVALUE"', { index: 0 }); const cell = vscodeNotebook.activeNotebookEditor?.document.cells![0]!; From de96c082a878692d96479d6cf810fa3537d557d5 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Fri, 15 Jan 2021 15:32:54 -0800 Subject: [PATCH 20/21] eslint fixed --- .eslintrc.js | 5 ----- src/client/common/utils/decorators.ts | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index d9f9fd180a2..f61850ee1e4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -436,10 +436,8 @@ module.exports = { 'src/datascience-ui/interactive-common/redux/reducers/transfer.ts', 'src/datascience-ui/interactive-common/redux/reducers/types.ts', 'src/datascience-ui/interactive-common/redux/reducers/variables.ts', - 'src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts', 'src/datascience-ui/interactive-common/redux/reducers/kernel.ts', 'src/datascience-ui/interactive-common/redux/postOffice.ts', - 'src/datascience-ui/interactive-common/redux/store.ts', 'src/datascience-ui/interactive-common/transforms.tsx', 'src/datascience-ui/interactive-common/contentPanel.tsx', 'src/datascience-ui/interactive-common/inputHistory.ts', @@ -760,7 +758,6 @@ module.exports = { 'src/client/common/terminal/environmentActivationProviders/commandPrompt.ts', 'src/client/common/terminal/environmentActivationProviders/bash.ts', 'src/client/common/terminal/environmentActivationProviders/pyenvActivationProvider.ts', - 'src/client/common/utils/decorators.ts', 'src/client/common/utils/enum.ts', 'src/client/common/utils/async.ts', 'src/client/common/utils/text.ts', @@ -1033,8 +1030,6 @@ module.exports = { 'src/client/datascience/interactive-common/types.ts', 'src/client/datascience/interactive-common/linkProvider.ts', 'src/client/datascience/interactive-common/notebookUsageTracker.ts', - 'src/client/datascience/interactive-common/interactiveWindowTypes.ts', - 'src/client/datascience/interactive-common/synchronization.ts', 'src/client/datascience/interactive-common/notebookProvider.ts', 'src/client/datascience/interactive-common/interactiveWindowMessageListener.ts', 'src/client/datascience/interactive-common/intellisense/wordHelper.ts', diff --git a/src/client/common/utils/decorators.ts b/src/client/common/utils/decorators.ts index 085d255b658..86308353aff 100644 --- a/src/client/common/utils/decorators.ts +++ b/src/client/common/utils/decorators.ts @@ -204,7 +204,7 @@ export function displayProgress(title: string, location = ProgressLocation.Windo // eslint-disable-next-line no-invalid-this const promise = originalMethod.apply(this, args); if (!isTestExecution()) { - window.withProgress(progressOptions, () => promise); + void window.withProgress(progressOptions, () => promise); } return promise; }; From faad48546a6ad970d952de60f01fe45895d1018a Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Fri, 15 Jan 2021 15:58:47 -0800 Subject: [PATCH 21/21] another fix for functional tests --- src/datascience-ui/interactive-common/redux/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index e3d22a3cbfc..8b047a9b510 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -310,7 +310,7 @@ function createMiddleWare(testMode: boolean, postOffice: PostOffice): Redux.Midd // eslint-disable-next-line @typescript-eslint/no-explicit-any const isUITest = (postOffice.acquireApi() as any)?.handleMessage ? true : false; let forceOnTestMiddleware = false; - if (typeof forceTestMiddleware !== undefined) { + if (typeof forceTestMiddleware !== 'undefined') { forceOnTestMiddleware = forceTestMiddleware(); } const testMiddleware = forceOnTestMiddleware || testMode || isUITest ? createTestMiddleware() : undefined;