Tests/notebook find tests (#8827)

* updates to existing book tests

* notebookFindModal tests

* remove commented code

* undo book test changes

* undo book test changes

* resolve the find array

* additional tests
This commit is contained in:
Maddy
2020-01-07 16:35:28 -08:00
committed by GitHub
parent 6070922579
commit b1526603cc
3 changed files with 242 additions and 44 deletions

View File

@@ -462,7 +462,7 @@ export abstract class NotebookInput extends EditorInput {
}
}
class NotebookEditorContentManager implements IContentManager {
export class NotebookEditorContentManager implements IContentManager {
constructor(
private notebookInput: NotebookInput,
@IInstantiationService private readonly instantiationService: IInstantiationService) {

View File

@@ -494,13 +494,18 @@ export class NotebookFindModel extends Disposable implements INotebookFindModel
this._findArray = new Array<NotebookRange>();
this._onFindCountChange.fire(this._findArray.length);
if (exp) {
return new Promise<NotebookRange>((resolve) => {
const disp = this.onFindCountChange(e => {
resolve(this._findArray[this._findIndex]);
disp.dispose();
});
this._startSearch(exp, maxMatches);
});
for (let i = 0; i < this.notebookModel.cells.length; i++) {
const item = this.notebookModel.cells[i];
const result = this.searchFn(item, exp, maxMatches);
if (result) {
this._findArray.push(...result);
this._onFindCountChange.fire(this._findArray.length);
if (maxMatches > 0 && this._findArray.length === maxMatches) {
break;
}
}
}
return Promise.resolve(this._findArray[this._findIndex]);
} else {
return Promise.reject(new Error('no expression'));
}
@@ -518,51 +523,41 @@ export class NotebookFindModel extends Disposable implements INotebookFindModel
return this.findArray;
}
private _startSearch(exp: string, maxMatches: number = 0): void {
let searchFn = (cell: ICellModel, exp: string): NotebookRange[] => {
let findResults: NotebookRange[] = [];
let cellVal = cell.cellType === 'markdown' ? this.cleanUpCellSource(cell.source) : cell.source;
let index: number;
let start: number;
let end: number;
if (cellVal) {
if (typeof cellVal === 'string') {
private searchFn(cell: ICellModel, exp: string, maxMatches?: number): NotebookRange[] {
let findResults: NotebookRange[] = [];
let cellVal = cell.cellType === 'markdown' ? this.cleanUpCellSource(cell.source) : cell.source;
let index: number;
let start: number;
let end: number;
if (cellVal) {
if (typeof cellVal === 'string') {
index = 0;
while (cellVal.substr(index).toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) > -1) {
start = cellVal.substr(index).toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) + index;
end = start + exp.length;
let range = new NotebookRange(cell, 0, start, 0, end);
findResults = findResults.concat(range);
index = end;
}
} else {
for (let j = 0; j < cellVal.length; j++) {
index = 0;
while (cellVal.substr(index).toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) > -1) {
start = cellVal.substr(index).toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) + index;
let cellValFormatted = cell.cellType === 'markdown' ? this.cleanMarkdownLinks(cellVal[j]) : cellVal[j];
while (cellValFormatted.substr(index).toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) > -1) {
start = cellValFormatted.substr(index).toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) + index + 1;
end = start + exp.length;
let range = new NotebookRange(cell, 0, start, 0, end);
// lineNumber: j+1 since notebook editors aren't zero indexed.
let range = new NotebookRange(cell, j + 1, start, j + 1, end);
findResults = findResults.concat(range);
index = end;
}
} else {
for (let j = 0; j < cellVal.length; j++) {
index = 0;
let cellValFormatted = cell.cellType === 'markdown' ? this.cleanMarkdownLinks(cellVal[j]) : cellVal[j];
while (cellValFormatted.substr(index).toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) > -1) {
start = cellValFormatted.substr(index).toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) + index + 1;
end = start + exp.length;
// lineNumber: j+1 since notebook editors aren't zero indexed.
let range = new NotebookRange(cell, j + 1, start, j + 1, end);
findResults = findResults.concat(range);
index = end;
}
if (findResults.length >= maxMatches) {
break;
}
}
}
return findResults;
};
for (let i = 0; i < this.notebookModel.cells.length; i++) {
const item = this.notebookModel.cells[i];
const result = searchFn!(item, exp);
if (result) {
this._findArray = this._findArray.concat(result);
this._onFindCountChange.fire(this._findArray.length);
if (maxMatches > 0 && this._findArray.length === maxMatches) {
break;
}
}
}
return findResults;
}
// In markdown links are defined as [Link Text](https://url/of/the/text). when searching for text we shouldn't

View File

@@ -0,0 +1,203 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as TypeMoq from 'typemoq';
import { nb } from 'azdata';
import * as assert from 'assert';
import { URI } from 'vs/base/common/uri';
import { NotebookManagerStub } from 'sql/workbench/contrib/notebook/test/stubs';
import { CellTypes } from 'sql/workbench/contrib/notebook/common/models/contracts';
import { IClientSession, INotebookModelOptions } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
import { NullLogService } from 'vs/platform/log/common/log';
import { NotebookFindModel } from 'sql/workbench/contrib/notebook/find/notebookFindModel';
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
import { Deferred } from 'sql/base/common/promise';
import { ModelFactory } from 'sql/workbench/contrib/notebook/browser/models/modelFactory';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { Memento } from 'vs/workbench/common/memento';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ClientSession } from 'sql/workbench/contrib/notebook/browser/models/clientSession';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { NotebookRange } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
import { NotebookEditorContentManager } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
let expectedNotebookContent: nb.INotebookContents = {
cells: [{
cell_type: CellTypes.Code,
source: 'insert into t1 values (c1, c2) \ninsert into markdown values (*hello worls*)',
metadata: { language: 'python' },
execution_count: 1
}, {
cell_type: CellTypes.Markdown,
source: 'I am *markdown*',
metadata: { language: 'python' },
execution_count: 1
}],
metadata: {
kernelspec: {
name: 'mssql',
language: 'sql'
}
},
nbformat: 4,
nbformat_minor: 5
};
let defaultUri = URI.file('/some/path.ipynb');
let max_find_count = 3;
let mockClientSession: TypeMoq.Mock<IClientSession>;
let sessionReady: Deferred<void>;
let mockModelFactory: TypeMoq.Mock<ModelFactory>;
let notificationService: TypeMoq.Mock<INotificationService>;
let capabilitiesService: TypeMoq.Mock<ICapabilitiesService>;
let instantiationService: IInstantiationService;
let serviceCollection = new ServiceCollection();
suite('Notebook Find Model', function (): void {
let notebookManagers = [new NotebookManagerStub()];
let memento: TypeMoq.Mock<Memento>;
let queryConnectionService: TypeMoq.Mock<TestConnectionManagementService>;
let defaultModelOptions: INotebookModelOptions;
const logService = new NullLogService();
let model: NotebookModel;
setup(async () => {
sessionReady = new Deferred<void>();
notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose);
capabilitiesService = TypeMoq.Mock.ofType(TestCapabilitiesService);
memento = TypeMoq.Mock.ofType(Memento, TypeMoq.MockBehavior.Loose, '');
memento.setup(x => x.getMemento(TypeMoq.It.isAny())).returns(() => void 0);
queryConnectionService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined, new TestStorageService());
queryConnectionService.callBase = true;
instantiationService = new InstantiationService(serviceCollection, true);
defaultModelOptions = {
notebookUri: defaultUri,
factory: new ModelFactory(instantiationService),
notebookManagers,
contentManager: undefined,
notificationService: notificationService.object,
connectionService: queryConnectionService.object,
providerId: 'SQL',
cellMagicMapper: undefined,
defaultKernel: undefined,
layoutChanged: undefined,
capabilitiesService: capabilitiesService.object
};
mockClientSession = TypeMoq.Mock.ofType(ClientSession, undefined, defaultModelOptions);
mockClientSession.setup(c => c.initialize()).returns(() => {
return Promise.resolve();
});
mockClientSession.setup(c => c.ready).returns(() => sessionReady.promise);
mockModelFactory = TypeMoq.Mock.ofType(ModelFactory);
mockModelFactory.callBase = true;
mockModelFactory.setup(f => f.createClientSession(TypeMoq.It.isAny())).returns(() => {
return mockClientSession.object;
});
await initNotebookModel(expectedNotebookContent);
});
test('Should find results in the notebook', async function (): Promise<void> {
//initialize find
let notebookFindModel = new NotebookFindModel(model);
await notebookFindModel.find('markdown', max_find_count);
assert(notebookFindModel.findMatches, new Error('Find in notebook failed.'));
assert.equal(notebookFindModel.findMatches.length, 2, 'Find couldnt find all occurances');
});
test('Should not find results in the notebook', async function (): Promise<void> {
//initialize find
let notebookFindModel = new NotebookFindModel(model);
await notebookFindModel.find('notFound', max_find_count);
assert.equal(notebookFindModel.findMatches.length, 0, 'Find failed');
});
test('Should match find result ranges', async function (): Promise<void> {
let notebookFindModel = new NotebookFindModel(model);
await notebookFindModel.find('markdown', max_find_count);
let expectedFindRange1 = new NotebookRange(model.cells[0], 2, 13, 2, 21);
assert.deepEqual(notebookFindModel.findMatches[0].range, expectedFindRange1, 'Find in markdown range is wrong :\n' + JSON.stringify(expectedFindRange1) + '\n ' + JSON.stringify(notebookFindModel.findMatches[0].range));
let expectedFindRange2 = new NotebookRange(model.cells[1], 1, 6, 1, 14);
assert.deepEqual(notebookFindModel.findMatches[1].range, expectedFindRange2, 'Find in markdown range is wrong :\n' + JSON.stringify(expectedFindRange2) + '\n ' + JSON.stringify(notebookFindModel.findMatches[1].range));
});
test('Should ignore hyperlink markdown data and find correctly', async function (): Promise<void> {
let markdownContent: nb.INotebookContents = {
cells: [{
cell_type: CellTypes.Markdown,
source: 'I am markdown link: [best link ever](https://url/of/the/best-link-ever)',
metadata: { language: 'python' },
execution_count: 1
}],
metadata: {
kernelspec: {
name: 'mssql',
language: 'sql'
}
},
nbformat: 4,
nbformat_minor: 5
};
await initNotebookModel(markdownContent);
let notebookFindModel = new NotebookFindModel(model);
await notebookFindModel.find('best', max_find_count);
assert.equal(notebookFindModel.findMatches.length, 1, 'Find failed on markdown link');
let expectedFindRange1 = new NotebookRange(model.cells[0], 1, 21, 1, 25);
assert.deepEqual(notebookFindModel.findMatches[0].range, expectedFindRange1, 'Find in markdown range is wrong :\n' + JSON.stringify(expectedFindRange1) + '\n ' + JSON.stringify(notebookFindModel.findMatches[0].range));
});
test('Should not find more than max results in the notebook', async function (): Promise<void> {
let codeContent: nb.INotebookContents = {
cells: [{
cell_type: CellTypes.Code,
source: ['import x', 'x.init()', 'x.show()', 'x.analyze()'],
metadata: { language: 'python' },
execution_count: 1
}],
metadata: {
kernelspec: {
name: 'python',
language: 'python'
}
},
nbformat: 4,
nbformat_minor: 5
};
await initNotebookModel(codeContent);
//initialize find
let notebookFindModel = new NotebookFindModel(model);
await notebookFindModel.find('x', max_find_count);
assert.equal(notebookFindModel.findMatches.length, 3, 'Find failed');
});
async function initNotebookModel(contents: nb.INotebookContents): Promise<void> {
let mockContentManager = TypeMoq.Mock.ofType(NotebookEditorContentManager);
mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(contents));
defaultModelOptions.contentManager = mockContentManager.object;
// Initialize the model
model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
await model.loadContents();
await model.requestModelLoad();
}
});