mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 17:22:48 -05:00
Phase 3 of notebookEditor tests (#11188)
* Phase 3 of notebookEditor tests * update visibility for visibility tests * saving in progress version to switch tasks * remove inadvertent change * corner cases tests to drive to 100% * remove commented code * pr feedback
This commit is contained in:
@@ -266,7 +266,6 @@ export class NotebookEditor extends BaseEditor implements IFindNotebookControlle
|
||||
return this._actionMap[id];
|
||||
}
|
||||
|
||||
|
||||
private async _onFindStateChange(e: FindReplaceStateChangedEvent): Promise<void> {
|
||||
if (!this._notebookModel) {
|
||||
await this.setNotebookModel();
|
||||
@@ -312,27 +311,26 @@ export class NotebookEditor extends BaseEditor implements IFindNotebookControlle
|
||||
} else {
|
||||
this.notebookInput.notebookFindModel.clearDecorations();
|
||||
this.notebookFindModel.findExpression = this._findState.searchString;
|
||||
this.notebookInput.notebookFindModel.find(this._findState.searchString, this._findState.matchCase, this._findState.wholeWord, NOTEBOOK_MAX_MATCHES).then(findRange => {
|
||||
if (findRange) {
|
||||
this.setSelection(findRange);
|
||||
} else if (this.notebookFindModel.findMatches.length > 0) {
|
||||
this.setSelection(this.notebookFindModel.findMatches[0].range);
|
||||
} else {
|
||||
this.notebookInput.notebookFindModel.clearFind();
|
||||
this._updateFinderMatchState();
|
||||
this._finder.focusFindInput();
|
||||
return;
|
||||
}
|
||||
const findRange = await this.notebookInput.notebookFindModel.find(this._findState.searchString, this._findState.matchCase, this._findState.wholeWord, NOTEBOOK_MAX_MATCHES);
|
||||
if (findRange) {
|
||||
this.setSelection(findRange);
|
||||
} else if (this.notebookFindModel.findMatches.length > 0) {
|
||||
this.setSelection(this.notebookFindModel.findMatches[0].range);
|
||||
} else {
|
||||
this.notebookInput.notebookFindModel.clearFind();
|
||||
this._updateFinderMatchState();
|
||||
this._finder.focusFindInput();
|
||||
this._findDecorations.set(this.notebookFindModel.findMatches, this._currentMatch);
|
||||
this._findState.changeMatchInfo(
|
||||
this.notebookFindModel.getFindIndex(),
|
||||
this._findDecorations.getCount(),
|
||||
this._currentMatch
|
||||
);
|
||||
this._setCurrentFindMatch(this._currentMatch);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._updateFinderMatchState();
|
||||
this._finder.focusFindInput();
|
||||
this._findDecorations.set(this.notebookFindModel.findMatches, this._currentMatch);
|
||||
this._findState.changeMatchInfo(
|
||||
this.notebookFindModel.getFindIndex(),
|
||||
this._findDecorations.getCount(),
|
||||
this._currentMatch
|
||||
);
|
||||
this._setCurrentFindMatch(this._currentMatch);
|
||||
}
|
||||
} else {
|
||||
this.notebookFindModel.clearFind();
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
import * as assert from 'assert';
|
||||
import { nb } from 'azdata';
|
||||
import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor';
|
||||
import { NotebookFindMatch } from 'sql/workbench/contrib/notebook/browser/find/notebookFindDecorations';
|
||||
import { ACTION_IDS } from 'sql/workbench/contrib/notebook/browser/find/notebookFindWidget';
|
||||
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
|
||||
import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/browser/models/untitledNotebookInput';
|
||||
import { NotebookFindNextAction, NotebookFindPreviousAction } from 'sql/workbench/contrib/notebook/browser/notebookActions';
|
||||
import { NotebookEditor } from 'sql/workbench/contrib/notebook/browser/notebookEditor';
|
||||
@@ -15,8 +17,9 @@ import * as stubs from 'sql/workbench/contrib/notebook/test/stubs';
|
||||
import { NotebookEditorStub } from 'sql/workbench/contrib/notebook/test/testCommon';
|
||||
import { CellModel } from 'sql/workbench/services/notebook/browser/models/cell';
|
||||
import { ICellModel, NotebookContentChange } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { INotebookService, NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { INotebookEditor, INotebookService, NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { NotebookService } from 'sql/workbench/services/notebook/browser/notebookServiceImpl';
|
||||
import { NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { errorHandler, onUnexpectedError } from 'vs/base/common/errors';
|
||||
@@ -27,7 +30,8 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { INewFindReplaceState } from 'vs/editor/contrib/find/findState';
|
||||
import { FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState';
|
||||
import { getRandomString } from 'vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
|
||||
@@ -56,14 +60,15 @@ import { workbenchInstantiationService } from 'vs/workbench/test/browser/workben
|
||||
|
||||
class NotebookModelStub extends stubs.NotebookModelStub {
|
||||
private _cells: Array<ICellModel> = [new CellModel(undefined, undefined)];
|
||||
private _contentChangedEmitter = new Emitter<NotebookContentChange>();
|
||||
public contentChangedEmitter = new Emitter<NotebookContentChange>();
|
||||
private _kernelChangedEmitter = new Emitter<nb.IKernelChangedArgs>();
|
||||
private _onActiveCellChanged = new Emitter<ICellModel>();
|
||||
|
||||
get cells(): ReadonlyArray<ICellModel> {
|
||||
return this._cells;
|
||||
}
|
||||
public get contentChanged(): Event<NotebookContentChange> {
|
||||
return this._contentChangedEmitter.event;
|
||||
return this.contentChangedEmitter.event;
|
||||
}
|
||||
|
||||
get kernelChanged(): Event<nb.IKernelChangedArgs> {
|
||||
@@ -81,79 +86,23 @@ class NotebookModelStub extends stubs.NotebookModelStub {
|
||||
}
|
||||
}
|
||||
|
||||
suite('Test class NotebookEditor', () => {
|
||||
|
||||
suite('Test class NotebookEditor:', () => {
|
||||
let instantiationService = <TestInstantiationService>workbenchInstantiationService();
|
||||
let workbenchThemeService = instantiationService.createInstance(WorkbenchThemeService);
|
||||
let notebookEditor: NotebookEditor;
|
||||
|
||||
const installEvent: Emitter<InstallExtensionEvent> = new Emitter<InstallExtensionEvent>();
|
||||
const didInstallEvent = new Emitter<DidInstallExtensionEvent>();
|
||||
const uninstallEvent = new Emitter<IExtensionIdentifier>();
|
||||
const didUninstallEvent = new Emitter<DidUninstallExtensionEvent>();
|
||||
|
||||
const instantiationService = <TestInstantiationService>workbenchInstantiationService();
|
||||
const workbenchThemeService = instantiationService.createInstance(WorkbenchThemeService);
|
||||
instantiationService.stub(IWorkbenchThemeService, workbenchThemeService);
|
||||
|
||||
const queryManagementService = new NBTestQueryManagementService();
|
||||
|
||||
instantiationService.stub(IExtensionManagementService, ExtensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event);
|
||||
instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event);
|
||||
instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event);
|
||||
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
|
||||
|
||||
const extensionService = instantiationService.get(IExtensionService);
|
||||
const notebookService = new NotebookService(
|
||||
instantiationService.get(ILifecycleService),
|
||||
instantiationService.get(IStorageService),
|
||||
extensionService,
|
||||
instantiationService.get(IExtensionManagementService),
|
||||
instantiationService,
|
||||
instantiationService.get(IFileService),
|
||||
instantiationService.get(ILogService),
|
||||
queryManagementService,
|
||||
instantiationService.get(IContextKeyService)
|
||||
);
|
||||
|
||||
instantiationService.stub(INotebookService, notebookService);
|
||||
|
||||
const testTitle = 'NotebookEditor.Test-Title';
|
||||
const untitledUri = URI.from({ scheme: Schemas.untitled, path: 'NotebookEditor.Test-TestPath' });
|
||||
const untitledTextEditorService = instantiationService.get(IUntitledTextEditorService);
|
||||
const untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, untitledTextEditorService.create({ associatedResource: untitledUri }));
|
||||
const untitledNotebookInput = new UntitledNotebookInput(
|
||||
testTitle, untitledUri, untitledTextInput,
|
||||
undefined, instantiationService, notebookService, extensionService
|
||||
);
|
||||
|
||||
const cellTextEditorGuid = generateUuid();
|
||||
const queryTextEditor = new QueryTextEditor(
|
||||
instantiationService.get(ITelemetryService),
|
||||
instantiationService,
|
||||
instantiationService.get(IStorageService),
|
||||
instantiationService.get(ITextResourceConfigurationService),
|
||||
instantiationService.get(IThemeService),
|
||||
instantiationService.get(IEditorGroupsService),
|
||||
instantiationService.get(IEditorService),
|
||||
instantiationService.get(IConfigurationService)
|
||||
);
|
||||
const notebookEditorStub = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: new NotebookModelStub() });
|
||||
notebookEditorStub.id = untitledNotebookInput.notebookUri.toString();
|
||||
notebookService.addNotebookEditor(notebookEditorStub);
|
||||
let testTitle: string;
|
||||
let notebookService: NotebookService;
|
||||
let extensionService: IExtensionService;
|
||||
let cellTextEditorGuid: string;
|
||||
let queryTextEditor: QueryTextEditor;
|
||||
let untitledNotebookInput: UntitledNotebookInput;
|
||||
let notebookEditorStub: NotebookEditorStub;
|
||||
|
||||
setup(async () => {
|
||||
// setup services
|
||||
({ instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub } = setupServices({ instantiationService, workbenchThemeService }));
|
||||
// Create notebookEditor
|
||||
notebookEditor = new NotebookEditor(
|
||||
instantiationService.get(ITelemetryService),
|
||||
instantiationService.get(IThemeService),
|
||||
instantiationService,
|
||||
instantiationService.get(IStorageService),
|
||||
new ContextViewService(instantiationService.get(ILayoutService)),
|
||||
instantiationService.get(IKeybindingService),
|
||||
instantiationService.get(IContextKeyService),
|
||||
workbenchThemeService,
|
||||
notebookService
|
||||
);
|
||||
notebookEditor = createNotebookEditor(instantiationService, workbenchThemeService, notebookService);
|
||||
});
|
||||
|
||||
test('Verifies that create() calls createEditor() and sets the provided parent object as the \'_overlay\' field', () => {
|
||||
@@ -165,7 +114,7 @@ suite('Test class NotebookEditor', () => {
|
||||
});
|
||||
|
||||
for (const notebookModel of [new NotebookModelStub(), undefined]) {
|
||||
test.skip(`Tests that notebookModel='${notebookModel}' set indirectly by setInput -> setNotebookModel is returned by getNotebookModel()`, async () => {
|
||||
test(`Tests that notebookModel='${notebookModel}' set indirectly by setInput -> setNotebookModel is returned by getNotebookModel()`, async () => {
|
||||
createEditor(notebookEditor);
|
||||
const untitledUri = URI.from({ scheme: Schemas.untitled, path: `NotebookEditor.Test-TestPath-${notebookModel}` });
|
||||
const untitledTextEditorService = instantiationService.get(IUntitledTextEditorService);
|
||||
@@ -179,6 +128,7 @@ suite('Test class NotebookEditor', () => {
|
||||
notebookService.addNotebookEditor(testNotebookEditor);
|
||||
notebookEditor.clearInput();
|
||||
await notebookEditor.setInput(untitledNotebookInput, EditorOptions.create({ pinned: true }));
|
||||
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
|
||||
const result = await notebookEditor.getNotebookModel();
|
||||
assert.strictEqual(result, notebookModel, `getNotebookModel() should return the model set in the INotebookEditor object`);
|
||||
});
|
||||
@@ -193,7 +143,7 @@ suite('Test class NotebookEditor', () => {
|
||||
assert.ok(isDisposed, 'notebookEditor\'s disposable store must be disposed');
|
||||
});
|
||||
|
||||
test('Tests that getPosition and getLastPosition correctly return the range set by setSelection', async () => {
|
||||
test('Tests that getPosition() and getLastPosition() correctly return the range set by setSelection', async () => {
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
let currentPosition = notebookEditor.getPosition();
|
||||
let lastPosition = notebookEditor.getLastPosition();
|
||||
@@ -334,8 +284,7 @@ suite('Test class NotebookEditor', () => {
|
||||
|
||||
test('Verifies toggleSearch changes isRevealed state with and without a notebookModel', async () => {
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
const notebookModel = await notebookEditor.getNotebookModel();
|
||||
for (const model of [notebookModel, undefined]) {
|
||||
for (const model of [new NotebookModelStub(), undefined]) {
|
||||
notebookEditor['_notebookModel'] = model;
|
||||
for (let i: number = 1; i <= 2; i++) { //Do it twice so that two toggles return back to original state verifying both transitions
|
||||
let isRevealed = notebookEditor['_findState']['_isRevealed'];
|
||||
@@ -346,11 +295,10 @@ suite('Test class NotebookEditor', () => {
|
||||
});
|
||||
|
||||
for (const action of [ACTION_IDS.FIND_NEXT, ACTION_IDS.FIND_PREVIOUS]) {
|
||||
test.skip(`Tests that ${action} raises 'no search running' error when findArray is empty`, async () => {
|
||||
test(`Tests that ${action} raises 'no search running' error when findArray is empty`, async () => {
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
let unexpectedErrorCalled = false;
|
||||
const onUnexpectedErrorVerifier = (error: any) => {
|
||||
//console.log(`Verifies that: ${onUnexpectedError} is passed an instance of ${Error}`);
|
||||
unexpectedErrorCalled = true;
|
||||
assert.ok(error instanceof Error, `${onUnexpectedError} must be passed an instance of ${Error}`);
|
||||
assert.strictEqual((error as Error).message, 'no search running', `Error text must be 'no search running' when findArray is empty`);
|
||||
@@ -366,8 +314,9 @@ suite('Test class NotebookEditor', () => {
|
||||
|
||||
for (const action of [ACTION_IDS.FIND_NEXT, ACTION_IDS.FIND_PREVIOUS]) {
|
||||
for (const range of [<NotebookRange>{}, new NotebookRange(<ICellModel>{}, 0, 0, 0, 0)]) {
|
||||
test.skip(`Tests ${action} returns the NotebookRange with cell: '${JSON.stringify(range.cell)}' that is as expected given the findArray`, async () => {
|
||||
test(`Tests ${action} returns the NotebookRange with cell: '${JSON.stringify(range.cell)}' that is as expected given the findArray`, async () => {
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
|
||||
const notebookModel = await notebookEditor.getNotebookModel();
|
||||
const mockModel = TypeMoq.Mock.ofInstance(notebookModel);
|
||||
mockModel.callBase = true; //forward calls to the base object
|
||||
@@ -393,15 +342,18 @@ suite('Test class NotebookEditor', () => {
|
||||
}
|
||||
}
|
||||
|
||||
test.skip(`Verifies visibility and decorations are set correctly when FindStateChange callbacks happen`, async () => {
|
||||
test(`Verifies visibility and decorations are set correctly when _onFindStateChange callback happens`, async () => {
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
let currentPosition = new NotebookRange(<ICellModel>{}, 0, 0, 0, 0);
|
||||
notebookEditor.setSelection(currentPosition);
|
||||
notebookEditor.notebookFindModel['_findArray'] = [currentPosition]; //set some pending finds.
|
||||
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
|
||||
await notebookEditor.setNotebookModel();
|
||||
await notebookEditor.findNext();
|
||||
const findState = notebookEditor['_findState'];
|
||||
const finder = notebookEditor['_finder'];
|
||||
const newState: INewFindReplaceState = {};
|
||||
|
||||
const findDecorations = notebookEditor.notebookInput.notebookFindModel.findDecorations;
|
||||
const findDecorationsMock = TypeMoq.Mock.ofInstance(findDecorations);
|
||||
findDecorationsMock.callBase = true; //forward to base object by default.
|
||||
@@ -422,11 +374,370 @@ suite('Test class NotebookEditor', () => {
|
||||
} else {
|
||||
assert.strictEqual(finder.getDomNode().style.visibility, 'hidden', 'finder node should be hidden when newState.isNotRevealed');
|
||||
assert.ok(clearDecorationsCalled, 'decorations are cleared when finder isNotRevealed');
|
||||
//verify that clearDecorations was called only once.
|
||||
findDecorationsMock.verify(x => x.clearDecorations(), TypeMoq.Times.once());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (const currentMatch of [null, new NotebookRange(<ICellModel>{}, 1, 1, 1, 1)]) {
|
||||
const searchString = getRandomString(1, 10); // a random string 1 to 10 characters long.
|
||||
for (const modelFindExpression of [searchString, getRandomString(1, 10)]) {
|
||||
for (const matchCase of [false, true]) {
|
||||
for (const wholeWord of [false, true]) {
|
||||
for (const findMatches of [[], [new NotebookFindMatch(currentMatch || <NotebookRange>{}, null)]]) {
|
||||
test(`Verifies _onFindStateChange callback when searchString='${searchString}', currentMatch='${currentMatch}', findExpression='${modelFindExpression}', matchCase='${matchCase}', wholeWord='${wholeWord}', findMatches='${findMatches}'`, async () => {
|
||||
await verifyFindCallsWhenFindStateChangeCallbackFires(instantiationService, workbenchThemeService, notebookService, untitledNotebookInput, modelFindExpression, currentMatch, searchString, wholeWord, matchCase, findMatches);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const searchString of ['', undefined]) {
|
||||
for (const matchCase of [true, false]) {
|
||||
for (const wholeWord of [true, false]) {
|
||||
test(`Verifies _onFindStateChange callback when searchString='${searchString}', matchCase='${matchCase}', wholeWord='${wholeWord}'`, async () => {
|
||||
await verifyClearDeocorationsAndClearFindCallsWhenFindStateChangeCallbackFires(instantiationService, workbenchThemeService, notebookService, untitledNotebookInput, searchString, wholeWord, matchCase);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const visibility of ['hidden', 'visible']) {
|
||||
const searchString = getRandomString(1, 10);
|
||||
const matchCase = true;
|
||||
const wholeWord = true;
|
||||
const searchScope = new NotebookRange(<ICellModel>{}, 1, 1, 1, 1);
|
||||
const currentMatch = <NotebookRange>{};
|
||||
test(`Verifies _onFindStateChange callback when searchScope is defined, visibility='${visibility}', searchString='${searchString}', matchCase='${matchCase}', wholeWord='${wholeWord}'`, async () => {
|
||||
const { findReplaceStateChangedEvent, notebookFindModelMock, findDecorationsMock, notebookFindModel, notebookEditor } = await findStateChangeSetup(instantiationService, workbenchThemeService, notebookService, untitledNotebookInput, undefined, currentMatch, searchString, wholeWord, matchCase, searchScope);
|
||||
notebookFindModelMock.setup(x => x.getIndexByRange(TypeMoq.It.isAny())).returns((_range: NotebookRange) => {
|
||||
assert.strictEqual(_range, currentMatch, `getIndexByRange must be called with the same NotebookRange that we set for '_currentMatch' property of notebookEditor`);
|
||||
return 0;
|
||||
});
|
||||
findReplaceStateChangedEvent.searchString = false;
|
||||
findReplaceStateChangedEvent.matchCase = false;
|
||||
findReplaceStateChangedEvent.wholeWord = false;
|
||||
const findMatches = [new NotebookFindMatch(currentMatch, null)];
|
||||
notebookFindModel['_findMatches'] = findMatches;
|
||||
notebookFindModel['_findArray'] = findMatches.map(x => x?.range);
|
||||
await notebookEditor['_onFindStateChange'](findReplaceStateChangedEvent);
|
||||
notebookEditor['_finder'].getDomNode().style.visibility = visibility;
|
||||
notebookFindModelMock.verify(x => x.find(
|
||||
TypeMoq.It.isAnyString(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAnyNumber()
|
||||
), TypeMoq.Times.once());
|
||||
findDecorationsMock.verify(x => x.clearDecorations(), TypeMoq.Times.never());
|
||||
notebookFindModelMock.verify(x => x.clearFind(), TypeMoq.Times.never());
|
||||
if (visibility === 'visible') {
|
||||
assert.strictEqual(notebookEditor.getPosition(), currentMatch, `position must be set to the same NotebookRange that we set for '_currentMatch' property of notebookEditor`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
test(`Verifies callbacks registered by registerModelChanges`, async () => {
|
||||
const searchString = getRandomString(1, 10);
|
||||
const matchCase = true;
|
||||
const wholeWord = true;
|
||||
const searchScope = new NotebookRange(<ICellModel>{}, 1, 1, 1, 1);
|
||||
const currentMatch = <NotebookRange>{};
|
||||
const { notebookFindModelMock, notebookEditor } = await findStateChangeSetup(instantiationService, workbenchThemeService, notebookService, untitledNotebookInput, undefined, currentMatch, searchString, wholeWord, matchCase, searchScope);
|
||||
notebookFindModelMock.setup(x => x.getIndexByRange(TypeMoq.It.isAny())).returns((_range: NotebookRange) => {
|
||||
assert.strictEqual(_range, currentMatch, `getIndexByRange must be called with the same NotebookRange that we set for '_currentMatch' property of notebookEditor`);
|
||||
return 0;
|
||||
});
|
||||
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
|
||||
const notebookModel = <NotebookModelStub>await notebookEditor.getNotebookModel();
|
||||
notebookEditor['registerModelChanges']();
|
||||
notebookModel.cells[0]['_onCellModeChanged'].fire(true); //fire cellModeChanged event on the first sell of our test notebookModel
|
||||
notebookModel.contentChangedEmitter.fire({ changeType: NotebookChangeType.Saved });
|
||||
(<NotebookService>notebookService)['_onNotebookEditorAdd'].fire(<INotebookEditor>{});
|
||||
notebookFindModelMock.verify(x => x.find(
|
||||
TypeMoq.It.isAnyString(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAnyNumber()
|
||||
), TypeMoq.Times.exactly(3));
|
||||
});
|
||||
|
||||
test(`Verifies _triggerInputChange raises error and does not throw'`, async () => {
|
||||
const searchString = getRandomString(1, 10);
|
||||
const matchCase = true;
|
||||
const wholeWord = true;
|
||||
const searchScope = new NotebookRange(<ICellModel>{}, 1, 1, 1, 1);
|
||||
const currentMatch = <NotebookRange>{};
|
||||
const { notebookEditor } = await findStateChangeSetup(instantiationService, workbenchThemeService, notebookService, untitledNotebookInput, undefined, currentMatch, searchString, wholeWord, matchCase, searchScope);
|
||||
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
|
||||
await notebookEditor.setNotebookModel();
|
||||
let unexpectedErrorCalled = false;
|
||||
const unexpectedErrorMessage = 'unexpected Error occurred';
|
||||
const onUnexpectedErrorVerifier = (error: any) => {
|
||||
unexpectedErrorCalled = true;
|
||||
assert.ok(error instanceof Error, `${onUnexpectedError} must be passed an instance of ${Error}`);
|
||||
assert.strictEqual((error as Error).message, unexpectedErrorMessage, `Error text must be '${unexpectedErrorMessage}' when exception occurs within _triggerInputChange method`);
|
||||
};
|
||||
errorHandler.setUnexpectedErrorHandler(onUnexpectedErrorVerifier);
|
||||
notebookEditor['_onFindStateChange'] = async (changeEvent: FindReplaceStateChangedEvent) => {
|
||||
try {
|
||||
throw new Error(unexpectedErrorMessage);
|
||||
} finally { }
|
||||
};
|
||||
notebookEditor['_triggerInputChange']();
|
||||
assert.notStrictEqual(unexpectedErrorCalled, true, '_triggerInputChange did not raise an error when an exception occurred Notebook model should be defined after findState.change->notebookEditor._onFindReplaceStateChange call');
|
||||
});
|
||||
|
||||
test(`Verifies _onFindStateChange callback sets notebookModel when it was not previously set'`, async () => {
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
notebookEditor['_notebookModel'] = undefined;
|
||||
const findState = notebookEditor['_findState'];
|
||||
const newState: INewFindReplaceState = {
|
||||
searchString: getRandomString(1, 10),
|
||||
matchCase: true,
|
||||
wholeWord: true,
|
||||
searchScope: <NotebookRange>{}
|
||||
};
|
||||
findState.change(newState, false);
|
||||
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
|
||||
const model = await notebookEditor.getNotebookModel();
|
||||
assert.notStrictEqual(model, undefined, 'Notebook model should be defined after findState.change->notebookEditor._onFindReplaceStateChange call');
|
||||
});
|
||||
|
||||
test(`Verifies _updateFinderMatchState with no notebookInput or notebookFindModel`, async () => {
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
const findState = notebookEditor['_findState'];
|
||||
findState['changeMatchInfo'] = (matchPosition: number, matchCount: number, currentMatch: NotebookRange) => {
|
||||
assert.strictEqual(matchPosition, 0, `matchPosition parameter to changeMatchInfo call when notebookInput or notebookFindModel is not defined should be 0`);
|
||||
assert.strictEqual(matchCount, 0, `matchCount parameter to changeMatchInfo call when notebookInput or notebookFindModel is not defined should be 0`);
|
||||
assert.strictEqual(currentMatch, undefined, `currentMatch parameter to changeMatchInfo call when notebookInput or notebookFindModel is not defined should be undefined`);
|
||||
};
|
||||
notebookEditor['_input'] = <NotebookInput>{};
|
||||
notebookEditor['_updateFinderMatchState']();
|
||||
notebookEditor['_input'] = undefined;
|
||||
notebookEditor['_updateFinderMatchState']();
|
||||
});
|
||||
|
||||
test(`Verifies onFindCountChange.fire invokes _updateFinderMatchState`, async () => {
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
|
||||
await notebookEditor.setNotebookModel();
|
||||
const findState = notebookEditor['_findState'];
|
||||
const newState: INewFindReplaceState = {
|
||||
searchString: getRandomString(1, 10),
|
||||
matchCase: true,
|
||||
wholeWord: true,
|
||||
searchScope: <NotebookRange>{}
|
||||
};
|
||||
findState.change(newState, false); //installs _updateFinderMatchState as event handler for onFindCountChange event
|
||||
let updateFinderMatchStateCalled = false;
|
||||
notebookEditor['_updateFinderMatchState'] = () => {
|
||||
updateFinderMatchStateCalled = true;
|
||||
};
|
||||
notebookEditor.notebookInput.notebookFindModel['_onFindCountChange'].fire(null);
|
||||
assert.strictEqual(updateFinderMatchStateCalled, true, `_updateFinderMatchState() should have been called`);
|
||||
});
|
||||
});
|
||||
|
||||
async function verifyClearDeocorationsAndClearFindCallsWhenFindStateChangeCallbackFires(instantiationService: TestInstantiationService, workbenchThemeService: any, notebookService: NotebookService, untitledNotebookInput: UntitledNotebookInput, searchString: string, wholeWord: boolean, matchCase: boolean) {
|
||||
const { findReplaceStateChangedEvent, notebookFindModelMock, findDecorationsMock, notebookEditor } = await findStateChangeSetup(instantiationService, workbenchThemeService, notebookService, untitledNotebookInput, undefined, undefined, searchString, wholeWord, matchCase);
|
||||
await notebookEditor['_onFindStateChange'](findReplaceStateChangedEvent);
|
||||
notebookFindModelMock.verify(x => x.find(
|
||||
TypeMoq.It.isAnyString(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAnyNumber()
|
||||
), TypeMoq.Times.never());
|
||||
if (searchString === '' || findReplaceStateChangedEvent.matchCase || findReplaceStateChangedEvent.wholeWord) {
|
||||
findDecorationsMock.verify(x => x.clearDecorations(), TypeMoq.Times.once());
|
||||
notebookFindModelMock.verify(x => x.clearFind(), TypeMoq.Times.once());
|
||||
}
|
||||
else {
|
||||
findDecorationsMock.verify(x => x.clearDecorations(), TypeMoq.Times.never());
|
||||
notebookFindModelMock.verify(x => x.clearFind(), TypeMoq.Times.never());
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyFindCallsWhenFindStateChangeCallbackFires(instantiationService: TestInstantiationService, workbenchThemeService: any, notebookService: NotebookService, untitledNotebookInput: UntitledNotebookInput, modelFindExpression: string, currentMatch: NotebookRange, searchString: string, wholeWord: boolean, matchCase: boolean, findMatches: NotebookFindMatch[]) {
|
||||
const { findReplaceStateChangedEvent, notebookFindModelMock, findDecorationsMock, notebookEditor } = await findStateChangeSetup(instantiationService, workbenchThemeService, notebookService, untitledNotebookInput, modelFindExpression, currentMatch, searchString, wholeWord, matchCase, null, findMatches);
|
||||
await notebookEditor['_onFindStateChange'](findReplaceStateChangedEvent);
|
||||
if (currentMatch) {
|
||||
assert.strictEqual(notebookEditor.getPosition(), currentMatch, `position must be set to the same NotebookRange that we set for '_currentMatch' property of notebookEditor`);
|
||||
}
|
||||
if (searchString === modelFindExpression && currentMatch && !matchCase && !wholeWord) {
|
||||
notebookFindModelMock.verify(x => x.find(
|
||||
TypeMoq.It.isAnyString(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAnyNumber()
|
||||
), TypeMoq.Times.never());
|
||||
findDecorationsMock.verify(x => x.set(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
}
|
||||
else {
|
||||
notebookFindModelMock.verify(x => x.find(
|
||||
TypeMoq.It.isAnyString(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAnyNumber()
|
||||
), TypeMoq.Times.once());
|
||||
assert.strictEqual(notebookEditor.notebookFindModel.findExpression, searchString, `findExpression should get set to the provided searchString:${searchString}`);
|
||||
if (!currentMatch && notebookEditor.notebookFindModel.findMatches.length === 0) {
|
||||
notebookFindModelMock.verify(x => x.clearFind(), TypeMoq.Times.once());
|
||||
findDecorationsMock.verify(x => x.set(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
assert.strictEqual(notebookEditor.notebookFindModel.findArray?.length ?? 0, 0, 'The find array should be cleared or undefined if there were no findMatches');
|
||||
}
|
||||
else {
|
||||
findDecorationsMock.verify(x => x.set(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
}
|
||||
}
|
||||
findDecorationsMock.verify(x => x.clearDecorations(), TypeMoq.Times.once());
|
||||
}
|
||||
|
||||
async function findStateChangeSetup(instantiationService: TestInstantiationService, workbenchThemeService: any, notebookService: NotebookService, untitledNotebookInput: UntitledNotebookInput, modelFindExpression: string, currentMatch: NotebookRange, searchString: string, wholeWord: boolean, matchCase: boolean, searchScope: NotebookRange | null = undefined, findMatches: Array<NotebookFindMatch> = []) {
|
||||
const findReplaceStateChangedEvent: FindReplaceStateChangedEvent = {
|
||||
searchString: searchString !== undefined,
|
||||
matchCase: matchCase,
|
||||
wholeWord: wholeWord,
|
||||
searchScope: !!searchScope,
|
||||
isRevealed: true,
|
||||
|
||||
moveCursor: false,
|
||||
updateHistory: false,
|
||||
replaceString: false,
|
||||
isReplaceRevealed: false,
|
||||
isRegex: false,
|
||||
preserveCase: false,
|
||||
matchesPosition: false,
|
||||
matchesCount: false,
|
||||
currentMatch: false,
|
||||
loop: false
|
||||
};
|
||||
|
||||
const notebookEditor = createNotebookEditor(instantiationService, workbenchThemeService, notebookService);
|
||||
await setupNotebookEditor(notebookEditor, untitledNotebookInput);
|
||||
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
|
||||
await notebookEditor.setNotebookModel();
|
||||
const findState = notebookEditor['_findState'];
|
||||
findState['_searchString'] = searchString;
|
||||
findState['_isRevealed'] = findReplaceStateChangedEvent.isRevealed;
|
||||
findState['_wholeWord'] = findReplaceStateChangedEvent.wholeWord;
|
||||
findState['_matchCase'] = findReplaceStateChangedEvent.matchCase;
|
||||
findState['_searchScope'] = searchScope;
|
||||
const notebookFindModel = notebookEditor.notebookInput.notebookFindModel;
|
||||
notebookFindModel['_findMatches'] = findMatches;
|
||||
notebookFindModel['_findArray'] = findMatches.map(x => x?.range);
|
||||
const notebookFindModelMock = TypeMoq.Mock.ofInstance(notebookFindModel);
|
||||
notebookFindModelMock.callBase = true;
|
||||
const spiedNotebookFindModel = notebookFindModelMock.object;
|
||||
notebookEditor.notebookInput['_notebookFindModel'] = spiedNotebookFindModel;
|
||||
|
||||
spiedNotebookFindModel.findExpression = modelFindExpression;
|
||||
const findDecorations = notebookFindModel.findDecorations;
|
||||
findDecorations['_currentMatch'] = currentMatch;
|
||||
|
||||
const findDecorationsMock = TypeMoq.Mock.ofInstance(findDecorations);
|
||||
const spiedFindDecorations = findDecorationsMock.object;
|
||||
findDecorationsMock.callBase = true; //forward to base object by default.
|
||||
notebookFindModel['_findDecorations'] = spiedFindDecorations;
|
||||
notebookEditor['_currentMatch'] = currentMatch;
|
||||
|
||||
findDecorationsMock.setup(x => x.clearDecorations());
|
||||
findDecorationsMock.setup(x => x.set(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((_findMatches, _currentMatch) => {
|
||||
// install a no-op method
|
||||
});
|
||||
notebookFindModelMock.setup(x => x.find(
|
||||
TypeMoq.It.isAnyString(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAny(),
|
||||
TypeMoq.It.isAnyNumber()
|
||||
)).returns((_searchString: string, _matchCase: boolean, _wholeWord: boolean, _maxMatches: number) => {
|
||||
assert.strictEqual(_searchString, searchString, `find method should be called with the test search string:'${searchString}'`);
|
||||
assert.strictEqual(_wholeWord, wholeWord, `find method should be called with the test value of wholeWord:'${wholeWord}'`);
|
||||
assert.strictEqual(_matchCase, matchCase, `find method should be called with the test value of matchCase:'${matchCase}'`);
|
||||
return Promise.resolve(currentMatch);
|
||||
});
|
||||
notebookFindModelMock.setup(x => x.clearFind());
|
||||
return { findReplaceStateChangedEvent, notebookFindModelMock, findDecorationsMock, notebookFindModel, notebookEditor };
|
||||
}
|
||||
|
||||
function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, instantiationService?: TestInstantiationService } = {}) {
|
||||
const installEvent: Emitter<InstallExtensionEvent> = new Emitter<InstallExtensionEvent>();
|
||||
const didInstallEvent = new Emitter<DidInstallExtensionEvent>();
|
||||
const uninstallEvent = new Emitter<IExtensionIdentifier>();
|
||||
const didUninstallEvent = new Emitter<DidUninstallExtensionEvent>();
|
||||
|
||||
const instantiationService = arg.instantiationService ?? <TestInstantiationService>workbenchInstantiationService();
|
||||
const workbenchThemeService = arg.workbenchThemeService ?? instantiationService.createInstance(WorkbenchThemeService);
|
||||
instantiationService.stub(IWorkbenchThemeService, workbenchThemeService);
|
||||
|
||||
const queryManagementService = new NBTestQueryManagementService();
|
||||
|
||||
instantiationService.stub(IExtensionManagementService, ExtensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event);
|
||||
instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event);
|
||||
instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event);
|
||||
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
|
||||
|
||||
const extensionService = instantiationService.get(IExtensionService);
|
||||
const notebookService = new NotebookService(
|
||||
instantiationService.get(ILifecycleService),
|
||||
instantiationService.get(IStorageService),
|
||||
extensionService,
|
||||
instantiationService.get(IExtensionManagementService),
|
||||
instantiationService,
|
||||
instantiationService.get(IFileService),
|
||||
instantiationService.get(ILogService),
|
||||
queryManagementService,
|
||||
instantiationService.get(IContextKeyService)
|
||||
);
|
||||
|
||||
instantiationService.stub(INotebookService, notebookService);
|
||||
|
||||
const testTitle = 'NotebookEditor.Test-Title';
|
||||
const untitledUri = URI.from({ scheme: Schemas.untitled, path: 'NotebookEditor.Test-TestPath' });
|
||||
const untitledTextEditorService = instantiationService.get(IUntitledTextEditorService);
|
||||
const untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, untitledTextEditorService.create({ associatedResource: untitledUri }));
|
||||
const untitledNotebookInput = new UntitledNotebookInput(
|
||||
testTitle, untitledUri, untitledTextInput,
|
||||
undefined, instantiationService, notebookService, extensionService
|
||||
);
|
||||
|
||||
const cellTextEditorGuid = generateUuid();
|
||||
const queryTextEditor = new QueryTextEditor(
|
||||
instantiationService.get(ITelemetryService),
|
||||
instantiationService,
|
||||
instantiationService.get(IStorageService),
|
||||
instantiationService.get(ITextResourceConfigurationService),
|
||||
instantiationService.get(IThemeService),
|
||||
instantiationService.get(IEditorGroupsService),
|
||||
instantiationService.get(IEditorService),
|
||||
instantiationService.get(IConfigurationService)
|
||||
);
|
||||
const notebookEditorStub = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: new NotebookModelStub() });
|
||||
notebookEditorStub.id = untitledNotebookInput.notebookUri.toString();
|
||||
notebookService.addNotebookEditor(notebookEditorStub);
|
||||
return { instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub };
|
||||
}
|
||||
|
||||
function createNotebookEditor(instantiationService: TestInstantiationService, workbenchThemeService: WorkbenchThemeService, notebookService: NotebookService) {
|
||||
return new NotebookEditor(
|
||||
instantiationService.get(ITelemetryService),
|
||||
instantiationService.get(IThemeService),
|
||||
instantiationService,
|
||||
instantiationService.get(IStorageService),
|
||||
new ContextViewService(instantiationService.get(ILayoutService)),
|
||||
instantiationService.get(IKeybindingService),
|
||||
instantiationService.get(IContextKeyService),
|
||||
workbenchThemeService,
|
||||
notebookService
|
||||
);
|
||||
}
|
||||
|
||||
async function setupNotebookEditor(notebookEditor: NotebookEditor, untitledNotebookInput: UntitledNotebookInput): Promise<void> {
|
||||
createEditor(notebookEditor);
|
||||
await setInputDocument(notebookEditor, untitledNotebookInput);
|
||||
|
||||
Reference in New Issue
Block a user