/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import 'mocha'; import * as vscode from 'vscode'; import * as sinon from 'sinon'; import * as azdata from 'azdata'; import * as nock from 'nock'; import * as os from 'os'; import * as path from 'path'; import { promises as fs } from 'fs'; import uuid = require('uuid'); import * as loc from '../../common/localizedConstants'; import * as constants from '../../common/constants'; import { NotebookUriHandler } from '../../protocol/notebookUriHandler'; import { CellTypes } from '../../contracts/content'; describe('Notebook URI Handler', function (): void { let notebookUriHandler: NotebookUriHandler; let showErrorMessageSpy: sinon.SinonSpy; let executeCommandSpy: sinon.SinonSpy; let showNotebookDocumentStub: sinon.SinonStub; const notebookUri = vscode.Uri.parse('azuredatastudio://microsoft.notebook/open?url=https%3A%2F%2F127.0.0.1/Hello.ipynb'); const notebookContent = 'test content'; beforeEach(() => { showErrorMessageSpy = sinon.spy(vscode.window, 'showErrorMessage'); executeCommandSpy = sinon.spy(vscode.commands, 'executeCommand'); notebookUriHandler = new NotebookUriHandler(); showNotebookDocumentStub = sinon.stub(azdata.nb, 'showNotebookDocument'); }); afterEach(function (): void { sinon.restore(); nock.cleanAll(); nock.enableNetConnect(); }); it('should handle empty string gracefully', async function (): Promise { await notebookUriHandler.handleUri(vscode.Uri.parse('')); sinon.assert.calledOnce(showErrorMessageSpy); sinon.assert.neverCalledWith(executeCommandSpy, constants.notebookCommandNew); sinon.assert.notCalled(showNotebookDocumentStub); }); it('should create new notebook when new passed in', async function (): Promise { await notebookUriHandler.handleUri(vscode.Uri.parse('azuredatastudio://microsoft.notebook/new')); sinon.assert.calledWith(executeCommandSpy, constants.notebookCommandNew); }); it('should show error message when no query passed into open', async function (): Promise { await notebookUriHandler.handleUri(vscode.Uri.parse('azuredatastudio://microsoft.notebook/open')); sinon.assert.calledOnce(showErrorMessageSpy); }); it('should show error message when uri scheme is not https, http, or file', async function (): Promise { await notebookUriHandler.handleUri(vscode.Uri.parse('azuredatastudio://microsoft.notebook/open?//url=aaa%3A%2F%2Fhello.ipynb')); sinon.assert.calledOnce(showErrorMessageSpy); }); it('should show error when file is not found given file uri scheme https', async function (): Promise { let showQuickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(Promise.resolve(loc.msgYes) as any); await notebookUriHandler.handleUri(notebookUri); sinon.assert.calledOnce(showQuickPickStub); sinon.assert.callCount(showErrorMessageSpy, 1); }); it('should show error when file is not found given file uri scheme http', async function (): Promise { let notebookUriHttp = vscode.Uri.parse('azuredatastudio://microsoft.notebook/open?url=http%3A%2F%2F127.0.0.1/Hello.ipynb'); let showQuickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(Promise.resolve(loc.msgYes) as any); await notebookUriHandler.handleUri(notebookUriHttp); sinon.assert.calledOnce(showQuickPickStub); sinon.assert.callCount(showErrorMessageSpy, 1); }); it('should open the notebook when https uri file is valid', async function (): Promise { let showQuickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(Promise.resolve(loc.msgYes) as any); nock('https://127.0.0.1') .get(`/Hello.ipynb`) .reply(200, notebookContent); await notebookUriHandler.handleUri(notebookUri); sinon.assert.calledOnce(showQuickPickStub); sinon.assert.neverCalledWith(showErrorMessageSpy); sinon.assert.calledWith(showNotebookDocumentStub, sinon.match.any, sinon.match({ initialContent: notebookContent })); }); it('should not download notebook when user declines prompt', async function (): Promise { let showQuickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(Promise.resolve(loc.msgNo) as any); await notebookUriHandler.handleUri(notebookUri); sinon.assert.calledOnce(showQuickPickStub); sinon.assert.notCalled(showNotebookDocumentStub); sinon.assert.callCount(showErrorMessageSpy, 0); }); [403, 404, 500].forEach(httpErrorCode => { it(`should reject when HTTP error ${httpErrorCode} occurs`, async function (): Promise { sinon.stub(vscode.window, 'showQuickPick').returns(Promise.resolve(loc.msgYes) as any); nock('https://127.0.0.1') .get(`/Hello.ipynb`) .reply(httpErrorCode); await notebookUriHandler.handleUri(notebookUri); sinon.assert.callCount(showErrorMessageSpy, 1); sinon.assert.notCalled(showNotebookDocumentStub); }); }); it('should open notebook when file uri is valid', async function (): Promise { let notebookDir: string = path.join(os.tmpdir(), `notebook_${uuid.v4()}`); let notebookPath: string = path.join(notebookDir, 'hello.ipynb'); await fs.mkdir(notebookDir); let baseUrl = 'azuredatastudio://microsoft.notebook/open?url=file://'; if (process.platform === constants.winPlatform) { // URI paths are formatted as "hostname/path", but since we're using a local path // we omit the host part and just add the slash. Unix paths already start with a // forward slash, but we have to prepend it manually when using Windows paths. baseUrl = baseUrl + '/'; } let fileURI = baseUrl + notebookPath; let fileNotebookUri = vscode.Uri.parse(fileURI); let notebookContent: azdata.nb.INotebookContents = { cells: [{ cell_type: CellTypes.Code, source: ['x = 1 \ny = 2'], metadata: { language: 'python', tags: ['parameters'] }, execution_count: 1 }], metadata: { kernelspec: { name: 'python3', language: 'python', display_name: 'Python 3' } }, nbformat: constants.NBFORMAT, nbformat_minor: constants.NBFORMAT_MINOR }; await fs.writeFile(notebookPath, JSON.stringify(notebookContent)); await notebookUriHandler.handleUri(fileNotebookUri); sinon.assert.calledWith(showNotebookDocumentStub, sinon.match.any, sinon.match({ initialContent: notebookContent })); sinon.assert.callCount(showErrorMessageSpy, 0); }); });