mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-09 17:52:34 -05:00
Pinning Notebooks on Notebooks view (#11963)
* initial commit * added tests * code cleanup and more tests * add missed util test * changes to address comments * remove pin from resources
This commit is contained in:
@@ -124,6 +124,7 @@ describe('BooksTreeViewTests', function () {
|
||||
should(appContext).not.be.undefined();
|
||||
should(appContext.bookTreeViewProvider).not.be.undefined();
|
||||
should(appContext.providedBookTreeViewProvider).not.be.undefined();
|
||||
should(appContext.pinnedBookTreeViewProvider).not.be.undefined();
|
||||
});
|
||||
|
||||
it('should initialize correctly with empty workspace array', async () => {
|
||||
@@ -346,6 +347,67 @@ describe('BooksTreeViewTests', function () {
|
||||
|
||||
});
|
||||
|
||||
describe('pinnedBookTreeViewProvider', function (): void {
|
||||
let pinnedTreeViewProvider: BookTreeViewProvider;
|
||||
let bookTreeViewProvider: BookTreeViewProvider;
|
||||
let bookItem: BookTreeItem;
|
||||
|
||||
this.beforeAll(async () => {
|
||||
pinnedTreeViewProvider = appContext.pinnedBookTreeViewProvider;
|
||||
bookTreeViewProvider = appContext.bookTreeViewProvider;
|
||||
let errorCase = new Promise((resolve, reject) => setTimeout(() => resolve(), 5000));
|
||||
await Promise.race([bookTreeViewProvider.initialized, errorCase.then(() => { throw new Error('BookTreeViewProvider did not initialize in time'); })]);
|
||||
await Promise.race([pinnedTreeViewProvider.initialized, errorCase.then(() => { throw new Error('PinnedTreeViewProvider did not initialize in time'); })]);
|
||||
await bookTreeViewProvider.openBook(bookFolderPath, undefined, false, false);
|
||||
bookItem = bookTreeViewProvider.books[0].bookItems[0];
|
||||
});
|
||||
|
||||
afterEach(function (): void {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('pinnedBookTreeViewProvider should not have any books when there are no pinned notebooks', async function (): Promise<void> {
|
||||
const notebooks = pinnedTreeViewProvider.books;
|
||||
should(notebooks.length).equal(0, 'Pinned Notebooks view should not have any notebooks');
|
||||
});
|
||||
|
||||
it('pinNotebook should add notebook to pinnedBookTreeViewProvider', async function (): Promise<void> {
|
||||
await vscode.commands.executeCommand('notebook.command.pinNotebook', bookItem);
|
||||
const notebooks = pinnedTreeViewProvider.books;
|
||||
should(notebooks.length).equal(1, 'Pinned Notebooks view should have a notebook');
|
||||
});
|
||||
|
||||
it('unpinNotebook should remove notebook from pinnedBookTreeViewProvider', async function (): Promise<void> {
|
||||
await vscode.commands.executeCommand('notebook.command.unpinNotebook', pinnedTreeViewProvider.books[0].bookItems[0]);
|
||||
const notebooks = pinnedTreeViewProvider.books;
|
||||
should(notebooks.length).equal(0, 'Pinned Notebooks view should not have any notebooks');
|
||||
});
|
||||
|
||||
it('pinNotebook should invoke bookPinManagers pinNotebook method', async function (): Promise<void> {
|
||||
let pinBookSpy = sinon.spy(bookTreeViewProvider.bookPinManager, 'pinNotebook');
|
||||
await bookTreeViewProvider.pinNotebook(bookItem);
|
||||
should(pinBookSpy.calledOnce).be.true('Should invoke bookPinManagers pinNotebook to update pinnedNotebooks config');
|
||||
});
|
||||
|
||||
it('unpinNotebook should invoke bookPinManagers unpinNotebook method', async function (): Promise<void> {
|
||||
let unpinNotebookSpy = sinon.spy(bookTreeViewProvider.bookPinManager, 'unpinNotebook');
|
||||
await bookTreeViewProvider.unpinNotebook(bookItem);
|
||||
should(unpinNotebookSpy.calledOnce).be.true('Should invoke bookPinManagers unpinNotebook to update pinnedNotebooks config');
|
||||
});
|
||||
|
||||
it('addNotebookToPinnedView should add notebook to the TreeViewProvider', async function (): Promise<void> {
|
||||
let notebooks = pinnedTreeViewProvider.books.length;
|
||||
await pinnedTreeViewProvider.addNotebookToPinnedView(bookItem);
|
||||
should(pinnedTreeViewProvider.books.length).equal(notebooks + 1, 'Should add the notebook as new item to the TreeViewProvider');
|
||||
});
|
||||
|
||||
it('removeNotebookFromPinnedView should remove notebook from the TreeViewProvider', async function (): Promise<void> {
|
||||
let notebooks = pinnedTreeViewProvider.books.length;
|
||||
await pinnedTreeViewProvider.removeNotebookFromPinnedView(pinnedTreeViewProvider.books[0].bookItems[0]);
|
||||
should(pinnedTreeViewProvider.books.length).equal(notebooks - 1, 'Should remove the notebook from the TreeViewProvider');
|
||||
});
|
||||
});
|
||||
|
||||
this.afterAll(async function (): Promise<void> {
|
||||
console.log('Removing temporary files...');
|
||||
if (await exists(rootFolderPath)) {
|
||||
|
||||
167
extensions/notebook/src/test/book/bookPinManager.test.ts
Normal file
167
extensions/notebook/src/test/book/bookPinManager.test.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import * as path from 'path';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as constants from '../../common/constants';
|
||||
import { IBookPinManager, BookPinManager } from '../../book/bookPinManager';
|
||||
import { BookTreeItem, BookTreeItemFormat, BookTreeItemType } from '../../book/bookTreeItem';
|
||||
import * as vscode from 'vscode';
|
||||
import { BookModel } from '../../book/bookModel';
|
||||
import * as sinon from 'sinon';
|
||||
import { isBookItemPinned } from '../../common/utils';
|
||||
|
||||
describe('BookPinManagerTests', function () {
|
||||
|
||||
describe('PinningNotebooks', () => {
|
||||
let bookPinManager: IBookPinManager;
|
||||
let pinnedNotebooks: string[];
|
||||
let books: BookModel[];
|
||||
|
||||
afterEach(function (): void {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
pinnedNotebooks = ['/temp/SubFolder/content/sample/notebook1.ipynb', '/temp/SubFolder/content/sample/notebook2.ipynb'];
|
||||
|
||||
// Mock Workspace Configuration
|
||||
let workspaceConfigurtionMock: TypeMoq.IMock<vscode.WorkspaceConfiguration> = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
||||
workspaceConfigurtionMock.setup(config => config.get(TypeMoq.It.isValue(constants.pinnedBooksConfigKey))).returns(() => [].concat(pinnedNotebooks));
|
||||
workspaceConfigurtionMock.setup(config => config.update(TypeMoq.It.isValue(constants.pinnedBooksConfigKey), TypeMoq.It.isAny(), TypeMoq.It.isValue(false))).returns((key: string, newValues: string[]) => {
|
||||
pinnedNotebooks.splice(0, pinnedNotebooks.length, ...newValues);
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
sinon.replaceGetter(vscode.workspace, 'workspaceFolders', () => {
|
||||
return <vscode.WorkspaceFolder[]>[{
|
||||
uri: {
|
||||
fsPath: '/temp/'
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: {
|
||||
fsPath: '/temp2/'
|
||||
}
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(workspaceConfigurtionMock.object);
|
||||
|
||||
// Mock Book Data
|
||||
let bookTreeItemFormat1: BookTreeItemFormat = {
|
||||
contentPath: '/temp/SubFolder/content/sample/notebook1.ipynb',
|
||||
root: '/temp/SubFolder/',
|
||||
tableOfContents: {
|
||||
sections: [
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook1')
|
||||
},
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook2')
|
||||
}
|
||||
]
|
||||
},
|
||||
isUntitled: undefined,
|
||||
page: undefined,
|
||||
title: undefined,
|
||||
treeItemCollapsibleState: undefined,
|
||||
type: BookTreeItemType.Book
|
||||
};
|
||||
|
||||
let bookTreeItemFormat2: BookTreeItemFormat = {
|
||||
contentPath: '/temp/SubFolder2/content/sample/notebook.ipynb',
|
||||
root: '/temp/SubFolder2/',
|
||||
tableOfContents: {
|
||||
sections: [
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook')
|
||||
}
|
||||
]
|
||||
},
|
||||
isUntitled: undefined,
|
||||
page: undefined,
|
||||
title: undefined,
|
||||
treeItemCollapsibleState: undefined,
|
||||
type: BookTreeItemType.Book
|
||||
};
|
||||
|
||||
let bookTreeItemFormat3: BookTreeItemFormat = {
|
||||
contentPath: '/temp2/SubFolder3/content/sample/notebook.ipynb',
|
||||
root: '/temp2/SubFolder3/',
|
||||
tableOfContents: {
|
||||
sections: [
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook')
|
||||
}
|
||||
]
|
||||
},
|
||||
isUntitled: undefined,
|
||||
page: undefined,
|
||||
title: undefined,
|
||||
treeItemCollapsibleState: undefined,
|
||||
type: BookTreeItemType.Book
|
||||
};
|
||||
|
||||
let bookModel1Mock: TypeMoq.IMock<BookModel> = TypeMoq.Mock.ofType<BookModel>();
|
||||
bookModel1Mock.setup(model => model.bookItems).returns(() => [new BookTreeItem(bookTreeItemFormat1, undefined), new BookTreeItem(bookTreeItemFormat2, undefined)]);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep, 'temp', 'SubFolder', 'content', 'sample', 'notebook.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep, 'temp', 'SubFolder', 'content', 'sample', 'notebook2.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep, 'temp', 'SubFolder2', 'content', 'sample', 'notebook.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isAnyString())).returns((uri: string) => undefined);
|
||||
|
||||
let bookModel2Mock: TypeMoq.IMock<BookModel> = TypeMoq.Mock.ofType<BookModel>();
|
||||
bookModel2Mock.setup(model => model.bookItems).returns(() => [new BookTreeItem(bookTreeItemFormat3, undefined)]);
|
||||
bookModel2Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep, 'temp2', 'SubFolder', 'content', 'sample', 'notebook.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel2Mock.setup(model => model.getNotebook(TypeMoq.It.isAnyString())).returns((uri: string) => undefined);
|
||||
|
||||
books = [bookModel1Mock.object, bookModel2Mock.object];
|
||||
|
||||
bookPinManager = new BookPinManager();
|
||||
});
|
||||
|
||||
it('should have notebooks in the pinnedBooksConfigKey when pinned within a workspace', async () => {
|
||||
let notebookUri1 = books[0].bookItems[0].book.contentPath;
|
||||
|
||||
let isNotebook1Pinned = isBookItemPinned(notebookUri1);
|
||||
|
||||
should(isNotebook1Pinned).be.true('Notebook 1 should be pinned');
|
||||
});
|
||||
|
||||
it('should NOT pin a notebook that is not pinned within a workspace', async () => {
|
||||
let notebookUri = path.join(path.sep, 'temp', 'SubFolder2', 'content', 'sample', 'notebook.ipynb');
|
||||
let isNotebookPinned = isBookItemPinned(notebookUri);
|
||||
|
||||
should(isNotebookPinned).be.false('Notebook should not be pinned');
|
||||
});
|
||||
|
||||
it('should pin notebook after book has been pinned from viewlet within a workspace', async () => {
|
||||
let notebookUri = books[0].bookItems[1].book.contentPath;
|
||||
|
||||
let isNotebookPinnedBeforeChange = isBookItemPinned(notebookUri);
|
||||
should(isNotebookPinnedBeforeChange).be.false('Notebook should NOT be pinned');
|
||||
|
||||
// mock pin book item from viewlet
|
||||
bookPinManager.pinNotebook(books[0].bookItems[1]);
|
||||
|
||||
let isNotebookPinnedAfterChange = isBookItemPinned(notebookUri);
|
||||
should(isNotebookPinnedAfterChange).be.true('Notebook should be pinned');
|
||||
});
|
||||
|
||||
it('should NOT pin a notebook when unpinned from viewlet within a workspace', async () => {
|
||||
let notebookUri = books[0].bookItems[0].book.contentPath;
|
||||
let isNotebookPinned = isBookItemPinned(notebookUri);
|
||||
|
||||
should(isNotebookPinned).be.true('Notebook should be pinned');
|
||||
|
||||
bookPinManager.unpinNotebook(books[0].bookItems[0]);
|
||||
let isNotebookPinnedAfterChange = isBookItemPinned(notebookUri);
|
||||
|
||||
should(isNotebookPinnedAfterChange).be.false('Notebook should not be pinned after notebook is unpinned');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -327,4 +327,22 @@ describe('Utils Tests', function () {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBookItemPinned', function (): void {
|
||||
it('Should NOT pin an unknown book within a workspace', async function (): Promise<void> {
|
||||
|
||||
let notebookUri = path.join(path.sep, 'randomfolder', 'randomsubfolder', 'content', 'randomnotebook.ipynb');
|
||||
let isNotebookPinned = utils.isBookItemPinned(notebookUri);
|
||||
|
||||
should(isNotebookPinned).be.false('Random notebooks should not be pinned');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPinnedNotebooks', function (): void {
|
||||
it('Should NOT have any pinned notebooks', async function (): Promise<void> {
|
||||
let pinnedNotebooks: string[] = utils.getPinnedNotebooks();
|
||||
|
||||
should(pinnedNotebooks.length).equal(0, 'Should not have any pinned notebooks');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user