From 2de82a77fc743eb69876df16f321ba8af6773ea0 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 15:11:16 -0700 Subject: [PATCH 01/11] Update copy --- package.nls.json | 2 +- src/client/common/utils/localize.ts | 2 +- src/startPage-ui/startPage/startPage.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.nls.json b/package.nls.json index c3aa71c3d06c..0e1ee2047ffd 100644 --- a/package.nls.json +++ b/package.nls.json @@ -222,7 +222,7 @@ "StartPage.createAPythonFile": "Create a Python File", "StartPage.pythonFileDescription": "- Create a
new file
with a .py extension", "StartPage.openInteractiveWindow": "Use the Interactive Window to develop Python Scripts", - "StartPage.interactiveWindowDesc": "- You can create cells on a Python file by typing \"#%%\"
- Use \"
Shift + Enter
\" to run a cell, the output will be shown in the interactive window", + "StartPage.interactiveWindowDesc": "- You can create cells on a Python file by typing \"#%%\". Make sure you have the Jupyter extension installed.
- Use \"
Shift + Enter
\" to run a cell, the output will be shown in the interactive window", "StartPage.releaseNotes": "Take a look at our Release Notes to learn more about the latest features.", "StartPage.mailingList": "Sign up for tips and tutorials through our mailing list.", "StartPage.tutorialAndDoc": "Explore more features in our Tutorials or check Documentation for tips and troubleshooting.", diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index 80fd801ba783..614b90221175 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -446,7 +446,7 @@ export namespace StartPage { ); export const interactiveWindowDesc = localize( 'StartPage.interactiveWindowDesc', - '- You can create cells on a Python file by typing "#%%"
- Use "
Shift + Enter
" to run a cell, the output will be shown in the interactive window', + '- You can create cells on a Python file by typing "#%%". Make sure you have the Jupyter extension installed.
- Use "
Shift + Enter
" to run a cell, the output will be shown in the interactive window', ); export const releaseNotes = localize( diff --git a/src/startPage-ui/startPage/startPage.tsx b/src/startPage-ui/startPage/startPage.tsx index 2381fdb48d27..34002550a50f 100644 --- a/src/startPage-ui/startPage/startPage.tsx +++ b/src/startPage-ui/startPage/startPage.tsx @@ -214,7 +214,7 @@ export class StartPage extends React.Component implements IMess dangerouslySetInnerHTML={{ __html: getLocString( 'StartPage.interactiveWindowDesc', - '- You can create cells on a Python file by typing "#%%"
- Use "
Shift + Enter
" to run a cell, the output will be shown in the interactive window', + '- You can create cells on a Python file by typing "#%%". Make sure you have the Jupyter extension installed.
- Use "
Shift + Enter
" to run a cell, the output will be shown in the interactive window', ), }} /> From e8bb755b7a8d6504de2730aa3076923d3ad9f62b Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 15:12:01 -0700 Subject: [PATCH 02/11] Update origin key --- src/client/jupyter/types.ts | 8 +++----- .../jupyterNotInstalledNotificationHelper.unit.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/client/jupyter/types.ts b/src/client/jupyter/types.ts index 14d0c868adbf..c44ef1abe608 100644 --- a/src/client/jupyter/types.ts +++ b/src/client/jupyter/types.ts @@ -46,12 +46,10 @@ enum ColumnType { // eslint-disable-next-line @typescript-eslint/no-explicit-any type IRowsResponse = any[]; -// Note: While #16102 is being worked on, this enum will be updated as we add ways to display this notification. export enum JupyterNotInstalledOrigin { - StartPageCreateBlankNotebook = 'startpage_create_blank_notebook', - StartPageCreateJupyterNotebook = 'startpage_create_jupyter_notebook', - StartPageCreateSampleNotebook = 'startpage_sample_notebook', - StartPageUseInteractiveWindow = 'startpage_use_interactive_window', + StartPageOpenBlankNotebook = 'startpage_open_blank_notebook', + StartPageOpenSampleNotebook = 'startpage_open_sample_notebook', + StartPageOpenInteractiveWindow = 'startpage_open_interactive_window', } export const IJupyterNotInstalledNotificationHelper = Symbol('IJupyterNotInstalledNotificationHelper'); diff --git a/src/test/jupyter/jupyterNotInstalledNotificationHelper.unit.test.ts b/src/test/jupyter/jupyterNotInstalledNotificationHelper.unit.test.ts index f82b9cb0c8a6..01c5bc6e5242 100644 --- a/src/test/jupyter/jupyterNotInstalledNotificationHelper.unit.test.ts +++ b/src/test/jupyter/jupyterNotInstalledNotificationHelper.unit.test.ts @@ -110,7 +110,7 @@ suite('Jupyter not installed notification helper', () => { ({ createGlobalPersistentState: createGlobalPersistentStateStub } as unknown) as IPersistentStateFactory, {} as IJupyterExtensionDependencyManager, ); - await notificationHelper.jupyterNotInstalledPrompt(JupyterNotInstalledOrigin.StartPageCreateBlankNotebook); + await notificationHelper.jupyterNotInstalledPrompt(JupyterNotInstalledOrigin.StartPageOpenBlankNotebook); sinon.assert.calledOnce(createGlobalPersistentStateStub); sinon.assert.calledOnce(showInformationMessageStub); @@ -142,7 +142,7 @@ suite('Jupyter not installed notification helper', () => { ({ createGlobalPersistentState: createGlobalPersistentStateStub } as unknown) as IPersistentStateFactory, {} as IJupyterExtensionDependencyManager, ); - await notificationHelper.jupyterNotInstalledPrompt(JupyterNotInstalledOrigin.StartPageCreateBlankNotebook); + await notificationHelper.jupyterNotInstalledPrompt(JupyterNotInstalledOrigin.StartPageOpenBlankNotebook); const result = notificationHelper.shouldShowJupypterExtensionNotInstalledPrompt(); From de9f0a7ffaa873587eedfc2d6ed4f5a8d28b180e Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 15:12:58 -0700 Subject: [PATCH 03/11] Show prompt if jupyter not installed & should show --- src/client/common/startPage/startPage.ts | 80 +++++++++++++++++------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/src/client/common/startPage/startPage.ts b/src/client/common/startPage/startPage.ts index 3c9b339f8c2c..814841089d33 100644 --- a/src/client/common/startPage/startPage.ts +++ b/src/client/common/startPage/startPage.ts @@ -8,12 +8,14 @@ import * as path from 'path'; import { ConfigurationTarget, EventEmitter, UIKind, Uri, ViewColumn } from 'vscode'; import { IExtensionSingleActivationService } from '../../activation/types'; import { EXTENSION_ROOT_DIR } from '../../constants'; +import { IJupyterNotInstalledNotificationHelper, JupyterNotInstalledOrigin } from '../../jupyter/types'; import { sendTelemetryEvent } from '../../telemetry'; import { IApplicationEnvironment, IApplicationShell, ICommandManager, IDocumentManager, + IJupyterExtensionDependencyManager, IWebviewPanelProvider, IWorkspaceService, } from '../application/types'; @@ -62,6 +64,9 @@ export class StartPage extends WebviewPanelHost @inject(IApplicationShell) private appShell: IApplicationShell, @inject(IExtensionContext) private readonly context: IExtensionContext, @inject(IApplicationEnvironment) private appEnvironment: IApplicationEnvironment, + @inject(IJupyterNotInstalledNotificationHelper) + private notificationHelper: IJupyterNotInstalledNotificationHelper, + @inject(IJupyterExtensionDependencyManager) private depsManager: IJupyterExtensionDependencyManager, ) { super( configuration, @@ -128,6 +133,9 @@ export class StartPage extends WebviewPanelHost } public async onMessage(message: string, payload: unknown): Promise { + const shouldShowJupyterNotInstalledPrompt = await this.notificationHelper.shouldShowJupypterExtensionNotInstalledPrompt(); + const isJupyterInstalled = this.depsManager.isJupyterExtensionInstalled; + switch (message) { case StartPageMessages.Started: this.webviewDidLoad = true; @@ -140,19 +148,27 @@ export class StartPage extends WebviewPanelHost break; } case StartPageMessages.OpenBlankNotebook: { - sendTelemetryEvent(Telemetry.StartPageOpenBlankNotebook); - this.setTelemetryFlags(); - - const savedVersion: string | undefined = this.context.globalState.get(EXTENSION_VERSION_MEMENTO); - - if (savedVersion) { - await this.commandManager.executeCommand( - 'jupyter.opennotebook', - undefined, - CommandSource.commandPalette, - ); + if (!isJupyterInstalled) { + if (shouldShowJupyterNotInstalledPrompt) { + await this.notificationHelper.jupyterNotInstalledPrompt( + JupyterNotInstalledOrigin.StartPageOpenBlankNotebook, + ); + } } else { - this.openSampleNotebook().ignoreErrors(); + sendTelemetryEvent(Telemetry.StartPageOpenBlankNotebook); + this.setTelemetryFlags(); + + const savedVersion: string | undefined = this.context.globalState.get(EXTENSION_VERSION_MEMENTO); + + if (savedVersion) { + await this.commandManager.executeCommand( + 'jupyter.opennotebook', + undefined, + CommandSource.commandPalette, + ); + } else { + this.openSampleNotebook().ignoreErrors(); + } } break; } @@ -168,15 +184,23 @@ export class StartPage extends WebviewPanelHost break; } case StartPageMessages.OpenInteractiveWindow: { - sendTelemetryEvent(Telemetry.StartPageOpenInteractiveWindow); - this.setTelemetryFlags(); - - const doc2 = await this.documentManager.openTextDocument({ - language: 'python', - content: `#%%\nprint("${localize.StartPage.helloWorld()}")`, - }); - await this.documentManager.showTextDocument(doc2, 1, true); - await this.commandManager.executeCommand('jupyter.runallcells', Uri.parse('')); + if (!isJupyterInstalled) { + if (shouldShowJupyterNotInstalledPrompt) { + await this.notificationHelper.jupyterNotInstalledPrompt( + JupyterNotInstalledOrigin.StartPageOpenInteractiveWindow, + ); + } + } else { + sendTelemetryEvent(Telemetry.StartPageOpenInteractiveWindow); + this.setTelemetryFlags(); + + const doc2 = await this.documentManager.openTextDocument({ + language: 'python', + content: `#%%\nprint("${localize.StartPage.helloWorld()}")`, + }); + await this.documentManager.showTextDocument(doc2, 1, true); + await this.commandManager.executeCommand('jupyter.runallcells', Uri.parse('')); + } break; } case StartPageMessages.OpenCommandPalette: @@ -192,10 +216,18 @@ export class StartPage extends WebviewPanelHost await this.commandManager.executeCommand('workbench.action.quickOpen', '>Create New Blank Notebook'); break; case StartPageMessages.OpenSampleNotebook: - sendTelemetryEvent(Telemetry.StartPageOpenSampleNotebook); - this.setTelemetryFlags(); + if (!isJupyterInstalled) { + if (shouldShowJupyterNotInstalledPrompt) { + await this.notificationHelper.jupyterNotInstalledPrompt( + JupyterNotInstalledOrigin.StartPageOpenSampleNotebook, + ); + } + } else { + sendTelemetryEvent(Telemetry.StartPageOpenSampleNotebook); + this.setTelemetryFlags(); - this.openSampleNotebook().ignoreErrors(); + this.openSampleNotebook().ignoreErrors(); + } break; case StartPageMessages.OpenFileBrowser: { sendTelemetryEvent(Telemetry.StartPageOpenFileBrowser); From 9b4d07554e33460d7ef64f2c4ef6fcbad2d8a13a Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 16:41:23 -0700 Subject: [PATCH 04/11] Add tests for this functionality only --- src/test/startPage/startPage.unit.test.ts | 135 +++++++++++++++++++++- 1 file changed, 133 insertions(+), 2 deletions(-) diff --git a/src/test/startPage/startPage.unit.test.ts b/src/test/startPage/startPage.unit.test.ts index 85799f349e82..ec43c166db7a 100644 --- a/src/test/startPage/startPage.unit.test.ts +++ b/src/test/startPage/startPage.unit.test.ts @@ -4,6 +4,7 @@ 'use strict'; import * as assert from 'assert'; +import * as sinon from 'sinon'; import * as typemoq from 'typemoq'; import { ExtensionContext } from 'vscode'; import { @@ -11,15 +12,20 @@ import { IApplicationShell, ICommandManager, IDocumentManager, + IJupyterExtensionDependencyManager, IWebviewPanelProvider, IWorkspaceService, } from '../../client/common/application/types'; import { PythonSettings } from '../../client/common/configSettings'; import { IFileSystem } from '../../client/common/platform/types'; import { StartPage } from '../../client/common/startPage/startPage'; -import { ICodeCssGenerator, IStartPage, IThemeFinder } from '../../client/common/startPage/types'; -import { IConfigurationService, IExtensionContext } from '../../client/common/types'; +import { ICodeCssGenerator, IStartPage, IThemeFinder, StartPageMessages } from '../../client/common/startPage/types'; +import { IConfigurationService, IExtensionContext, IPersistentStateFactory } from '../../client/common/types'; +import { IJupyterNotInstalledNotificationHelper, JupyterNotInstalledOrigin } from '../../client/jupyter/types'; import { MockAutoSelectionService } from '../mocks/autoSelector'; +import * as Telemetry from '../../client/telemetry'; +import { EventName } from '../../client/telemetry/constants'; +import { JupyterNotInstalledNotificationHelper } from '../../client/jupyter/jupyterNotInstalledNotificationHelper'; suite('StartPage tests', () => { let startPage: IStartPage; @@ -34,7 +40,9 @@ suite('StartPage tests', () => { let appShell: typemoq.IMock; let context: typemoq.IMock; let appEnvironment: typemoq.IMock; + let depsManager: typemoq.IMock; let memento: typemoq.IMock; + let notificationHelper: IJupyterNotInstalledNotificationHelper; const dummySettings = new PythonSettings(undefined, new MockAutoSelectionService()); function setupVersions(savedVersion: string, actualVersion: string) { @@ -65,8 +73,22 @@ suite('StartPage tests', () => { appShell = typemoq.Mock.ofType(); context = typemoq.Mock.ofType(); appEnvironment = typemoq.Mock.ofType(); + // notificationHelper = typemoq.Mock.ofType(); + depsManager = typemoq.Mock.ofType(); memento = typemoq.Mock.ofType(); + // @inject(IApplicationShell) private appShell: IApplicationShell, + // @inject(IPersistentStateFactory) private persistentState: IPersistentStateFactory, + // @inject(IJupyterExtensionDependencyManager) private depsManager: IJupyterExtensionDependencyManager, + + // Notification helper object + const stateFactory = typemoq.Mock.ofType(); + notificationHelper = new JupyterNotInstalledNotificationHelper( + appShell.object, + stateFactory.object, + depsManager.object, + ); + context.setup((c) => c.globalState).returns(() => memento.object); configuration.setup((cs) => cs.getSettings(undefined)).returns(() => dummySettings); @@ -82,9 +104,15 @@ suite('StartPage tests', () => { appShell.object, context.object, appEnvironment.object, + notificationHelper, + depsManager.object, ); }); + teardown(() => { + sinon.restore(); + }); + test('Check extension version', async () => { let savedVersion: string; let actualVersion: string; @@ -116,4 +144,107 @@ suite('StartPage tests', () => { assert.equal(test3, true, 'The actual version is newer, start page should open.'); reset(); }); + + suite('"Jupyter is not installed" prompt tests', () => { + type StartPageMessageForTests = IStartPage & { + onMessage(message: string, payload: unknown): Promise; + }; + + let startPageWithMessageHandler: StartPageMessageForTests; + let telemetryEvents: { eventName: string; properties: Record }[] = []; + let sendTelemetryEventStub: sinon.SinonStub; + + setup(() => { + sendTelemetryEventStub = sinon + .stub(Telemetry, 'sendTelemetryEvent') + .callsFake((eventName: string, _, properties: Record) => { + const telemetry = { eventName, properties }; + telemetryEvents.push(telemetry); + }); + + startPageWithMessageHandler = (startPage as unknown) as StartPageMessageForTests; + }); + + teardown(() => { + telemetryEvents = []; + Telemetry._resetSharedProperties(); + }); + + const notebookActions = [ + { + testcase: 'a blank notebook', + message: StartPageMessages.OpenBlankNotebook, + entrypoint: JupyterNotInstalledOrigin.StartPageOpenBlankNotebook, + }, + { + testcase: 'a sample notebook', + message: StartPageMessages.OpenSampleNotebook, + entrypoint: JupyterNotInstalledOrigin.StartPageOpenSampleNotebook, + }, + { + testcase: 'the interactive window', + message: StartPageMessages.OpenInteractiveWindow, + entrypoint: JupyterNotInstalledOrigin.StartPageOpenInteractiveWindow, + }, + ]; + + notebookActions.forEach(({ testcase, message, entrypoint }) => { + suite(`When opening ${testcase}`, () => { + test('Should display "Jupyter is not installed" prompt if the Jupyter extension is not installed and the prompt should not be shown', async () => { + depsManager.setup((dm) => dm.isJupyterExtensionInstalled).returns(() => false); + const shouldShowPromptStub = sinon.stub( + notificationHelper, + 'shouldShowJupypterExtensionNotInstalledPrompt', + ); + shouldShowPromptStub.returns(true); + + await startPageWithMessageHandler.onMessage(message, {}); + + sinon.assert.called(sendTelemetryEventStub); + sinon.assert.calledOnce(shouldShowPromptStub); + // 2 events: one when the prompt is displayed, one with the prompt selection (in this case, nothing). + assert.strictEqual(telemetryEvents.length, 2); + assert.deepStrictEqual(telemetryEvents[0], { + eventName: EventName.JUPYTER_NOT_INSTALLED_NOTIFICATION_DISPLAYED, + properties: { entrypoint }, + }); + }); + + test('Should not display "Jupyter is not installed" prompt if the Jupyter extension is not installed and the prompt should not be shown', async () => { + depsManager.setup((dm) => dm.isJupyterExtensionInstalled).returns(() => false); + const shouldShowPromptStub = sinon.stub( + notificationHelper, + 'shouldShowJupypterExtensionNotInstalledPrompt', + ); + shouldShowPromptStub.returns(false); + + await startPageWithMessageHandler.onMessage(StartPageMessages.OpenBlankNotebook, {}); + + sinon.assert.notCalled(sendTelemetryEventStub); + sinon.assert.calledOnce(shouldShowPromptStub); + assert.strictEqual(telemetryEvents.length, 0); + }); + + test('Should not display "Jupyter is not installed" prompt if the Jupyter extension is installed', async () => { + depsManager.setup((dm) => dm.isJupyterExtensionInstalled).returns(() => true); + const shouldShowPromptStub = sinon.stub( + notificationHelper, + 'shouldShowJupypterExtensionNotInstalledPrompt', + ); + shouldShowPromptStub.returns(false); + + await startPageWithMessageHandler.onMessage(StartPageMessages.OpenBlankNotebook, {}); + + sinon.assert.called(sendTelemetryEventStub); + sinon.assert.calledOnce(shouldShowPromptStub); + // There is a telemetry event sent when performing the action. + assert.strictEqual(telemetryEvents.length, 1); + assert.notDeepStrictEqual(telemetryEvents[0], { + eventName: EventName.JUPYTER_NOT_INSTALLED_NOTIFICATION_DISPLAYED, + properties: { entrypoint }, + }); + }); + }); + }); + }); }); From 2515ee86c7213d29b008d64221fa69d5bdb0f35f Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 16:45:46 -0700 Subject: [PATCH 05/11] Update news entry --- news/1 Enhancements/16102.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/1 Enhancements/16102.md b/news/1 Enhancements/16102.md index 728d3f03fecd..3f3cb719476b 100644 --- a/news/1 Enhancements/16102.md +++ b/news/1 Enhancements/16102.md @@ -1 +1 @@ -Move the Jupyter extension from being a hard dependency to an optional one. +Move the Jupyter extension from being a hard dependency to an optional one, and display an informational prompt if Jupyter commands try to be executed from the Start Page. From ec236b330e9a04e4308bf596846804bd716ba7cd Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 16:46:51 -0700 Subject: [PATCH 06/11] Remove comments --- src/test/startPage/startPage.unit.test.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/startPage/startPage.unit.test.ts b/src/test/startPage/startPage.unit.test.ts index ec43c166db7a..83da5178b3bd 100644 --- a/src/test/startPage/startPage.unit.test.ts +++ b/src/test/startPage/startPage.unit.test.ts @@ -73,14 +73,9 @@ suite('StartPage tests', () => { appShell = typemoq.Mock.ofType(); context = typemoq.Mock.ofType(); appEnvironment = typemoq.Mock.ofType(); - // notificationHelper = typemoq.Mock.ofType(); depsManager = typemoq.Mock.ofType(); memento = typemoq.Mock.ofType(); - // @inject(IApplicationShell) private appShell: IApplicationShell, - // @inject(IPersistentStateFactory) private persistentState: IPersistentStateFactory, - // @inject(IJupyterExtensionDependencyManager) private depsManager: IJupyterExtensionDependencyManager, - // Notification helper object const stateFactory = typemoq.Mock.ofType(); notificationHelper = new JupyterNotInstalledNotificationHelper( From a606d3a61aafe4c6e4ea692b52ab836ee1b17072 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 16:54:05 -0700 Subject: [PATCH 07/11] follow-up from the merge --- src/client/common/startPage/startPage.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/common/startPage/startPage.ts b/src/client/common/startPage/startPage.ts index 814841089d33..bb9854a85f19 100644 --- a/src/client/common/startPage/startPage.ts +++ b/src/client/common/startPage/startPage.ts @@ -150,7 +150,7 @@ export class StartPage extends WebviewPanelHost case StartPageMessages.OpenBlankNotebook: { if (!isJupyterInstalled) { if (shouldShowJupyterNotInstalledPrompt) { - await this.notificationHelper.jupyterNotInstalledPrompt( + await this.notificationHelper.showJupyterNotInstalledPrompt( JupyterNotInstalledOrigin.StartPageOpenBlankNotebook, ); } @@ -186,7 +186,7 @@ export class StartPage extends WebviewPanelHost case StartPageMessages.OpenInteractiveWindow: { if (!isJupyterInstalled) { if (shouldShowJupyterNotInstalledPrompt) { - await this.notificationHelper.jupyterNotInstalledPrompt( + await this.notificationHelper.showJupyterNotInstalledPrompt( JupyterNotInstalledOrigin.StartPageOpenInteractiveWindow, ); } @@ -218,7 +218,7 @@ export class StartPage extends WebviewPanelHost case StartPageMessages.OpenSampleNotebook: if (!isJupyterInstalled) { if (shouldShowJupyterNotInstalledPrompt) { - await this.notificationHelper.jupyterNotInstalledPrompt( + await this.notificationHelper.showJupyterNotInstalledPrompt( JupyterNotInstalledOrigin.StartPageOpenSampleNotebook, ); } From 3003344a1f226e8aa1d140746596fba0f347a5bc Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 17:22:53 -0700 Subject: [PATCH 08/11] Add singletons for startpage functional tests --- src/test/startPage/startPageIocContainer.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/startPage/startPageIocContainer.ts b/src/test/startPage/startPageIocContainer.ts index e6438af254e0..edbf38cb85ad 100644 --- a/src/test/startPage/startPageIocContainer.ts +++ b/src/test/startPage/startPageIocContainer.ts @@ -33,6 +33,7 @@ import { IApplicationShell, ICommandManager, IDocumentManager, + IJupyterExtensionDependencyManager, IWebviewPanelOptions, IWebviewPanelProvider, IWorkspaceService, @@ -73,6 +74,9 @@ import { sleep } from '../../client/common/utils/async'; import { noop } from '../../client/common/utils/misc'; import { EnvironmentActivationServiceCache } from '../../client/interpreter/activation/service'; +import { JupyterExtensionDependencyManager } from '../../client/jupyter/jupyterExtensionDependencyManager'; +import { JupyterNotInstalledNotificationHelper } from '../../client/jupyter/jupyterNotInstalledNotificationHelper'; +import { IJupyterNotInstalledNotificationHelper } from '../../client/jupyter/types'; import { CacheableLocatorPromiseCache } from '../../client/pythonEnvironments/discovery/locators/services/cacheableLocatorService'; import { MockAutoSelectionService } from '../mocks/autoSelector'; @@ -255,6 +259,15 @@ export class StartPageIocContainer extends UnitTestIocContainer { this.serviceManager.add(IInstallationChannelManager, InstallationChannelManager); + this.serviceManager.addSingleton( + IJupyterExtensionDependencyManager, + JupyterExtensionDependencyManager, + ); + this.serviceManager.addSingleton( + IJupyterNotInstalledNotificationHelper, + JupyterNotInstalledNotificationHelper, + ); + const mockMemento = TypeMoq.Mock.ofType(); const mockExtensionContext = TypeMoq.Mock.ofType(); mockExtensionContext.setup((m) => m.globalState).returns(() => mockMemento.object); From 18ca0a0305fdb42c708ae6803291267c86737d89 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 7 Jun 2021 17:36:40 -0700 Subject: [PATCH 09/11] Missing one symbol --- src/test/startPage/startPageIocContainer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/startPage/startPageIocContainer.ts b/src/test/startPage/startPageIocContainer.ts index edbf38cb85ad..090a17021c57 100644 --- a/src/test/startPage/startPageIocContainer.ts +++ b/src/test/startPage/startPageIocContainer.ts @@ -48,6 +48,7 @@ import { ExperimentService } from '../../client/common/experiments/service'; import { InstallationChannelManager } from '../../client/common/installer/channelManager'; import { IInstallationChannelManager } from '../../client/common/installer/types'; import { HttpClient } from '../../client/common/net/httpClient'; +import { PersistentStateFactory } from '../../client/common/persistentState'; import { IS_WINDOWS } from '../../client/common/platform/constants'; import { FileSystem } from '../../client/common/platform/fileSystem'; import { PathUtils } from '../../client/common/platform/pathUtils'; @@ -66,6 +67,7 @@ import { IExtensions, IHttpClient, IPathUtils, + IPersistentStateFactory, IPythonSettings, IsWindows, Resource, @@ -259,6 +261,8 @@ export class StartPageIocContainer extends UnitTestIocContainer { this.serviceManager.add(IInstallationChannelManager, InstallationChannelManager); + // "Jupyter is not installed" prompt. + this.serviceManager.addSingleton(IPersistentStateFactory, PersistentStateFactory); this.serviceManager.addSingleton( IJupyterExtensionDependencyManager, JupyterExtensionDependencyManager, From 66e675939d697824d209617a8969ddfb2834d7e5 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Date: Tue, 8 Jun 2021 13:41:23 -0700 Subject: [PATCH 10/11] Update src/client/common/startPage/startPage.ts Co-authored-by: Don Jayamanne --- src/client/common/startPage/startPage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/common/startPage/startPage.ts b/src/client/common/startPage/startPage.ts index bb9854a85f19..a329104a6fc8 100644 --- a/src/client/common/startPage/startPage.ts +++ b/src/client/common/startPage/startPage.ts @@ -199,7 +199,7 @@ export class StartPage extends WebviewPanelHost content: `#%%\nprint("${localize.StartPage.helloWorld()}")`, }); await this.documentManager.showTextDocument(doc2, 1, true); - await this.commandManager.executeCommand('jupyter.runallcells', Uri.parse('')); + await this.commandManager.executeCommand('jupyter.runallcells', doc2.uri); } break; } From 251a72c860602e93cb4a368a73a1f98d74cf0819 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Tue, 8 Jun 2021 15:08:26 -0700 Subject: [PATCH 11/11] Add logging --- src/client/common/startPage/startPage.ts | 14 +++++++-- src/test/startPage/startPage.unit.test.ts | 36 ++++++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/client/common/startPage/startPage.ts b/src/client/common/startPage/startPage.ts index bb9854a85f19..dace323069f0 100644 --- a/src/client/common/startPage/startPage.ts +++ b/src/client/common/startPage/startPage.ts @@ -3,7 +3,7 @@ 'use strict'; -import { inject, injectable } from 'inversify'; +import { inject, injectable, named } from 'inversify'; import * as path from 'path'; import { ConfigurationTarget, EventEmitter, UIKind, Uri, ViewColumn } from 'vscode'; import { IExtensionSingleActivationService } from '../../activation/types'; @@ -19,10 +19,11 @@ import { IWebviewPanelProvider, IWorkspaceService, } from '../application/types'; -import { CommandSource } from '../constants'; +import { CommandSource, STANDARD_OUTPUT_CHANNEL } from '../constants'; import { IFileSystem } from '../platform/types'; -import { IConfigurationService, IExtensionContext, Resource } from '../types'; +import { IConfigurationService, IExtensionContext, IOutputChannel, Resource } from '../types'; import * as localize from '../utils/localize'; +import { Jupyter } from '../utils/localize'; import { StopWatch } from '../utils/stopWatch'; import { Telemetry } from './constants'; import { StartPageMessageListener } from './startPageMessageListener'; @@ -67,6 +68,7 @@ export class StartPage extends WebviewPanelHost @inject(IJupyterNotInstalledNotificationHelper) private notificationHelper: IJupyterNotInstalledNotificationHelper, @inject(IJupyterExtensionDependencyManager) private depsManager: IJupyterExtensionDependencyManager, + @inject(IOutputChannel) @named(STANDARD_OUTPUT_CHANNEL) private readonly output: IOutputChannel, ) { super( configuration, @@ -149,6 +151,8 @@ export class StartPage extends WebviewPanelHost } case StartPageMessages.OpenBlankNotebook: { if (!isJupyterInstalled) { + this.output.appendLine(Jupyter.jupyterExtensionNotInstalled()); + if (shouldShowJupyterNotInstalledPrompt) { await this.notificationHelper.showJupyterNotInstalledPrompt( JupyterNotInstalledOrigin.StartPageOpenBlankNotebook, @@ -185,6 +189,8 @@ export class StartPage extends WebviewPanelHost } case StartPageMessages.OpenInteractiveWindow: { if (!isJupyterInstalled) { + this.output.appendLine(Jupyter.jupyterExtensionNotInstalled()); + if (shouldShowJupyterNotInstalledPrompt) { await this.notificationHelper.showJupyterNotInstalledPrompt( JupyterNotInstalledOrigin.StartPageOpenInteractiveWindow, @@ -217,6 +223,8 @@ export class StartPage extends WebviewPanelHost break; case StartPageMessages.OpenSampleNotebook: if (!isJupyterInstalled) { + this.output.appendLine(Jupyter.jupyterExtensionNotInstalled()); + if (shouldShowJupyterNotInstalledPrompt) { await this.notificationHelper.showJupyterNotInstalledPrompt( JupyterNotInstalledOrigin.StartPageOpenSampleNotebook, diff --git a/src/test/startPage/startPage.unit.test.ts b/src/test/startPage/startPage.unit.test.ts index 83da5178b3bd..0f66880f8cff 100644 --- a/src/test/startPage/startPage.unit.test.ts +++ b/src/test/startPage/startPage.unit.test.ts @@ -20,12 +20,19 @@ import { PythonSettings } from '../../client/common/configSettings'; import { IFileSystem } from '../../client/common/platform/types'; import { StartPage } from '../../client/common/startPage/startPage'; import { ICodeCssGenerator, IStartPage, IThemeFinder, StartPageMessages } from '../../client/common/startPage/types'; -import { IConfigurationService, IExtensionContext, IPersistentStateFactory } from '../../client/common/types'; +import { + IConfigurationService, + IExtensionContext, + IOutputChannel, + IPersistentState, + IPersistentStateFactory, +} from '../../client/common/types'; import { IJupyterNotInstalledNotificationHelper, JupyterNotInstalledOrigin } from '../../client/jupyter/types'; import { MockAutoSelectionService } from '../mocks/autoSelector'; import * as Telemetry from '../../client/telemetry'; import { EventName } from '../../client/telemetry/constants'; import { JupyterNotInstalledNotificationHelper } from '../../client/jupyter/jupyterNotInstalledNotificationHelper'; +import { Jupyter } from '../../client/common/utils/localize'; suite('StartPage tests', () => { let startPage: IStartPage; @@ -41,6 +48,7 @@ suite('StartPage tests', () => { let context: typemoq.IMock; let appEnvironment: typemoq.IMock; let depsManager: typemoq.IMock; + let outputChannel: typemoq.IMock; let memento: typemoq.IMock; let notificationHelper: IJupyterNotInstalledNotificationHelper; const dummySettings = new PythonSettings(undefined, new MockAutoSelectionService()); @@ -74,10 +82,16 @@ suite('StartPage tests', () => { context = typemoq.Mock.ofType(); appEnvironment = typemoq.Mock.ofType(); depsManager = typemoq.Mock.ofType(); + outputChannel = typemoq.Mock.ofType(); memento = typemoq.Mock.ofType(); // Notification helper object const stateFactory = typemoq.Mock.ofType(); + const state = typemoq.Mock.ofType>(); + + stateFactory + .setup((s) => s.createGlobalPersistentState(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) + .returns(() => state.object); notificationHelper = new JupyterNotInstalledNotificationHelper( appShell.object, stateFactory.object, @@ -101,6 +115,7 @@ suite('StartPage tests', () => { appEnvironment.object, notificationHelper, depsManager.object, + outputChannel.object, ); }); @@ -239,6 +254,25 @@ suite('StartPage tests', () => { properties: { entrypoint }, }); }); + + test('Should write something in the Python output channel if the Jupyter extension is not installed', async () => { + let output = ''; + outputChannel + .setup((oc) => oc.appendLine(typemoq.It.isAnyString())) + .callback((line: string) => { + output += line; + }) + .verifiable(typemoq.Times.once()); + depsManager.setup((dm) => dm.isJupyterExtensionInstalled).returns(() => false); + + await startPageWithMessageHandler.onMessage(StartPageMessages.OpenBlankNotebook, {}); + + outputChannel.verify( + (oc) => oc.appendLine(Jupyter.jupyterExtensionNotInstalled()), + typemoq.Times.once(), + ); + assert.strictEqual(output, Jupyter.jupyterExtensionNotInstalled()); + }); }); }); });