Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/.mocha.unittests.ts.opts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
--require ts-node/register
--require out/test/unittests.js
--require src/test/unittests.ts
--reporter mocha-multi-reporters
--reporter-options configFile=build/.mocha-multi-reporters.config
--ui tdd
--recursive
--colors
./src/test/**/*.unit.test.ts
./src/test/**/*.unit.test.ts
1 change: 1 addition & 0 deletions news/1 Enhancements/8078.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Prompt to open exported `Notebook` in the `Notebook Editor`.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
IInteractiveBase,
IInteractiveWindowProvider,
IJupyterExecution,
INotebookEditorProvider,
INotebookExporter,
INotebookImporter,
INotebookServer,
Expand All @@ -45,7 +46,8 @@ export class InteractiveWindowCommandListener implements IDataScienceCommandList
@inject(IConfigurationService) private configuration: IConfigurationService,
@inject(IStatusProvider) private statusProvider: IStatusProvider,
@inject(INotebookImporter) private jupyterImporter: INotebookImporter,
@inject(IDataScienceErrorHandler) private dataScienceErrorHandler: IDataScienceErrorHandler
@inject(IDataScienceErrorHandler) private dataScienceErrorHandler: IDataScienceErrorHandler,
@inject(INotebookEditorProvider) protected ipynbProvider: INotebookEditorProvider
) {
}

Expand Down Expand Up @@ -159,7 +161,6 @@ export class InteractiveWindowCommandListener implements IDataScienceCommandList
saveLabel: localize.DataScience.exportDialogTitle(),
filters: filtersObject
});

await this.waitForStatus(async () => {
if (uri) {
let directoryChange;
Expand All @@ -172,16 +173,19 @@ export class InteractiveWindowCommandListener implements IDataScienceCommandList
await this.fileSystem.writeFile(uri.fsPath, JSON.stringify(notebook));
}
}, localize.DataScience.exportingFormat(), file);

// When all done, show a notice that it completed.
const openQuestion = (await this.jupyterExecution.isSpawnSupported()) ? localize.DataScience.exportOpenQuestion() : undefined;
if (uri && uri.fsPath) {
this.showInformationMessage(localize.DataScience.exportDialogComplete().format(uri.fsPath), openQuestion).then((str: string | undefined) => {
if (str === openQuestion) {
// If the user wants to, open the notebook they just generated.
this.jupyterExecution.spawnNotebook(uri.fsPath).ignoreErrors();
}
});
const openQuestion1 = localize.DataScience.exportOpenQuestion1();
const openQuestion2 = (await this.jupyterExecution.isSpawnSupported()) ? localize.DataScience.exportOpenQuestion() : undefined;
const questions = [openQuestion1, ...(openQuestion2 ? [openQuestion2] : [])];
const selection = await this.applicationShell.showInformationMessage(localize.DataScience.exportDialogComplete().format(uri.fsPath), ...questions);
if (selection === openQuestion1) {
await this.ipynbProvider.open(uri, await this.fileSystem.readFile(uri.fsPath));
}
if (selection === openQuestion2) {
// If the user wants to, open the notebook they just generated.
this.jupyterExecution.spawnNotebook(uri.fsPath).ignoreErrors();
}
}
}
}
Expand Down Expand Up @@ -220,13 +224,17 @@ export class InteractiveWindowCommandListener implements IDataScienceCommandList
});

// When all done, show a notice that it completed.
const openQuestion = (await this.jupyterExecution.isSpawnSupported()) ? localize.DataScience.exportOpenQuestion() : undefined;
this.showInformationMessage(localize.DataScience.exportDialogComplete().format(output), openQuestion).then((str: string | undefined) => {
if (str === openQuestion && output) {
// If the user wants to, open the notebook they just generated.
this.jupyterExecution.spawnNotebook(output).ignoreErrors();
}
});
const openQuestion1 = localize.DataScience.exportOpenQuestion1();
const openQuestion2 = (await this.jupyterExecution.isSpawnSupported()) ? localize.DataScience.exportOpenQuestion() : undefined;
const questions = [openQuestion1, ...(openQuestion2 ? [openQuestion2] : [])];
const selection = await this.applicationShell.showInformationMessage(localize.DataScience.exportDialogComplete().format(output), ...questions);
if (selection === openQuestion1) {
await this.ipynbProvider.open(Uri.file(output), await this.fileSystem.readFile(output));
}
if (selection === openQuestion2) {
// If the user wants to, open the notebook they just generated.
this.jupyterExecution.spawnNotebook(output).ignoreErrors();
}

return Uri.file(output);
}
Expand Down
46 changes: 25 additions & 21 deletions src/test/datascience/interactiveWindowCommandListener.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@
'use strict';
import { nbformat } from '@jupyterlab/coreutils/lib/nbformat';
import { assert } from 'chai';
import { anything, instance, mock, when } from 'ts-mockito';
import { anything, instance, mock, verify, when } from 'ts-mockito';
import { Matcher } from 'ts-mockito/lib/matcher/type/Matcher';
import * as TypeMoq from 'typemoq';
import * as uuid from 'uuid/v4';
import { EventEmitter, Uri } from 'vscode';

import { ApplicationShell } from '../../client/common/application/applicationShell';
import { IApplicationShell } from '../../client/common/application/types';
import { PythonSettings } from '../../client/common/configSettings';
import { ConfigurationService } from '../../client/common/configuration/service';
import { Logger } from '../../client/common/logger';
import { FileSystem } from '../../client/common/platform/fileSystem';
import { IFileSystem } from '../../client/common/platform/types';
import { IConfigurationService, IDisposable, ILogger } from '../../client/common/types';
import * as localize from '../../client/common/utils/localize';
import { generateCells } from '../../client/datascience/cellFactory';
import { Commands } from '../../client/datascience/constants';
import { DataScienceErrorHandler } from '../../client/datascience/errorHandler/errorHandler';
import { NativeEditorProvider } from '../../client/datascience/interactive-ipynb/nativeEditorProvider';
import {
InteractiveWindowCommandListener
} from '../../client/datascience/interactive-window/interactiveWindowCommandListener';
Expand All @@ -28,15 +31,15 @@ import { JupyterExporter } from '../../client/datascience/jupyter/jupyterExporte
import { JupyterImporter } from '../../client/datascience/jupyter/jupyterImporter';
import {
IInteractiveWindow,
IJupyterExecution,
INotebook,
INotebookEditorProvider,
INotebookServer
} from '../../client/datascience/types';
import { InterpreterService } from '../../client/interpreter/interpreterService';
import { KnownSearchPathsForInterpreters } from '../../client/interpreter/locators/services/KnownPathsService';
import { ServiceContainer } from '../../client/ioc/container';
import { noop } from '../core';
import { MockAutoSelectionService } from '../mocks/autoSelector';
import * as vscodeMocks from '../vscode-mock';
import { MockCommandManager } from './mockCommandManager';
import { MockDocumentManager } from './mockDocumentManager';
import { MockStatusProvider } from './mockStatusProvider';
Expand Down Expand Up @@ -67,26 +70,16 @@ suite('Interactive window command listener', async () => {
const dataScienceErrorHandler = mock(DataScienceErrorHandler);
const notebookImporter = mock(JupyterImporter);
const notebookExporter = mock(JupyterExporter);
const applicationShell = mock(ApplicationShell);
const jupyterExecution = mock(JupyterExecutionFactory);
let applicationShell: IApplicationShell;
let jupyterExecution: IJupyterExecution;
const interactiveWindow = createTypeMoq<IInteractiveWindow>('Interactive Window');
const documentManager = new MockDocumentManager();
const statusProvider = new MockStatusProvider();
const commandManager = new MockCommandManager();
let notebookEditorProvider: INotebookEditorProvider;
const server = createTypeMoq<INotebookServer>('jupyter server');
let lastFileContents: any;

suiteSetup(() => {
vscodeMocks.initialize();
});
suiteTeardown(() => {
noop();
});

setup(() => {
noop();
});

teardown(() => {
documentManager.activeTextEditor = undefined;
lastFileContents = undefined;
Expand All @@ -111,6 +104,10 @@ suite('Interactive window command listener', async () => {
}

function createCommandListener(): InteractiveWindowCommandListener {
notebookEditorProvider = mock(NativeEditorProvider);
jupyterExecution = mock(JupyterExecutionFactory);
applicationShell = mock(ApplicationShell);

// Setup defaults
when(interpreterService.onDidChangeInterpreter).thenReturn(dummyEvent.event);
when(interpreterService.getInterpreterDetails(argThat(o => !o.includes || !o.includes('python')))).thenReject('Unknown interpreter');
Expand Down Expand Up @@ -190,9 +187,7 @@ suite('Interactive window command listener', async () => {
}
);

if (jupyterExecution.isNotebookSupported) {
when(jupyterExecution.isNotebookSupported()).thenResolve(true);
}
when(jupyterExecution.isNotebookSupported()).thenResolve(true);

documentManager.addDocument('#%%\r\nprint("code")', 'bar.ipynb');

Expand All @@ -211,7 +206,8 @@ suite('Interactive window command listener', async () => {
instance(configService),
statusProvider,
instance(notebookImporter),
instance(dataScienceErrorHandler));
instance(dataScienceErrorHandler),
instance(notebookEditorProvider));
result.register(commandManager);

return result;
Expand All @@ -233,9 +229,14 @@ suite('Interactive window command listener', async () => {
const doc = await documentManager.openTextDocument('bar.ipynb');
await documentManager.showTextDocument(doc);
when(applicationShell.showSaveDialog(argThat(o => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn(Promise.resolve(Uri.file('foo')));
when(applicationShell.showInformationMessage(anything(), anything())).thenReturn(Promise.resolve('moo'));
when(applicationShell.showInformationMessage(anything(), anything(), anything())).thenReturn(Promise.resolve('moo'));
when(jupyterExecution.isSpawnSupported()).thenResolve(true);

await commandManager.executeCommand(Commands.ExportFileAsNotebook, Uri.file('bar.ipynb'), undefined);

assert.ok(lastFileContents, 'Export file was not written to');
verify(applicationShell.showInformationMessage(anything(), localize.DataScience.exportOpenQuestion1(), localize.DataScience.exportOpenQuestion())).once();
});
test('Export File and output', async () => {
createCommandListener();
Expand All @@ -250,9 +251,13 @@ suite('Interactive window command listener', async () => {

when(applicationShell.showSaveDialog(argThat(o => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn(Promise.resolve(Uri.file('foo')));
when(applicationShell.showInformationMessage(anything(), anything())).thenReturn(Promise.resolve('moo'));
when(applicationShell.showInformationMessage(anything(), anything(), anything())).thenReturn(Promise.resolve('moo'));
when(jupyterExecution.isSpawnSupported()).thenResolve(true);

await commandManager.executeCommand(Commands.ExportFileAndOutputAsNotebook, Uri.file('bar.ipynb'));

assert.ok(lastFileContents, 'Export file was not written to');
verify(applicationShell.showInformationMessage(anything(), localize.DataScience.exportOpenQuestion1(), localize.DataScience.exportOpenQuestion())).once();
});
test('Export skipped on no file', async () => {
createCommandListener();
Expand All @@ -268,5 +273,4 @@ suite('Interactive window command listener', async () => {
await commandManager.executeCommand(Commands.ExportFileAsNotebook, undefined, undefined);
assert.ok(lastFileContents, 'Export file was not written to');
});

});
3 changes: 1 addition & 2 deletions src/test/mocks/vsc/telemetryReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
'use strict';

// tslint:disable:all
import * as telemetry from 'vscode-extension-telemetry';
export class vscMockTelemetryReporter implements telemetry.default {
export class vscMockTelemetryReporter {
constructor() {
//
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/vscode-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function initialize() {
return mockedVSCode;
}
if (request === 'vscode-extension-telemetry') {
return { default: vscMockTelemetryReporter };
return { default: vscMockTelemetryReporter as any };
}
// less files need to be in import statements to be converted to css
// But we don't want to try to load them in the mock vscode
Expand Down