mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-29 01:25:37 -05:00
move code from parts to contrib (#8319)
This commit is contained in:
@@ -0,0 +1,917 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as os from 'os';
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { ConnectionManagementService } from 'sql/workbench/services/connection/browser/connectionManagementService';
|
||||
import { CellModel } from 'sql/workbench/contrib/notebook/browser/models/cell';
|
||||
import { CellTypes, NotebookChangeType } from 'sql/workbench/contrib/notebook/common/models/contracts';
|
||||
import { ModelFactory } from 'sql/workbench/contrib/notebook/browser/models/modelFactory';
|
||||
import { INotebookModelOptions, NotebookContentChange, ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookEditorModel } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { NotebookService } from 'sql/workbench/services/notebook/browser/notebookServiceImpl';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { toResource } from 'vs/base/test/common/utils';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { TestEnvironmentService, TestLifecycleService, TestStorageService, TestTextFileService, workbenchInstantiationService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { nb } from 'azdata';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { INotebookEditor, INotebookManager } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(
|
||||
@IEditorService public editorService: IEditorService,
|
||||
@ITextFileService public textFileService: TestTextFileService,
|
||||
@IModelService public modelService: IModelService
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
class NotebookManagerStub implements INotebookManager {
|
||||
providerId: string;
|
||||
contentManager: nb.ContentManager;
|
||||
sessionManager: nb.SessionManager;
|
||||
serverManager: nb.ServerManager;
|
||||
}
|
||||
|
||||
let defaultUri = URI.file('/some/path.ipynb');
|
||||
|
||||
// Note: these tests are intentionally written to be extremely brittle and break on any changes to notebook/cell serialization changes.
|
||||
// If any of these tests fail, it is likely that notebook editor rehydration will fail with cryptic JSON messages.
|
||||
suite('Notebook Editor Model', function (): void {
|
||||
let notebookManagers = [new NotebookManagerStub()];
|
||||
let notebookModel: NotebookModel;
|
||||
const instantiationService: IInstantiationService = workbenchInstantiationService();
|
||||
let accessor: ServiceAccessor;
|
||||
let defaultModelOptions: INotebookModelOptions;
|
||||
const logService = new NullLogService();
|
||||
const notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose);
|
||||
let memento = TypeMoq.Mock.ofType(Memento, TypeMoq.MockBehavior.Loose, '');
|
||||
memento.setup(x => x.getMemento(TypeMoq.It.isAny())).returns(() => void 0);
|
||||
const queryConnectionService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined, new TestStorageService());
|
||||
queryConnectionService.callBase = true;
|
||||
const capabilitiesService = TypeMoq.Mock.ofType(TestCapabilitiesService);
|
||||
const configurationService = new TestConfigurationService();
|
||||
const testResourcePropertiesService = new TestTextResourcePropertiesService(configurationService);
|
||||
let mockModelFactory = TypeMoq.Mock.ofType(ModelFactory);
|
||||
mockModelFactory.callBase = true;
|
||||
mockModelFactory.setup(f => f.createCell(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {
|
||||
return new CellModel({
|
||||
cell_type: CellTypes.Code,
|
||||
source: '',
|
||||
outputs: [
|
||||
<nb.IDisplayData>{
|
||||
output_type: 'display_data',
|
||||
data: {
|
||||
'text/html': [
|
||||
'<div>',
|
||||
'</div>'
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}, undefined, undefined);
|
||||
});
|
||||
|
||||
let mockNotebookService: TypeMoq.Mock<NotebookService>;
|
||||
mockNotebookService = TypeMoq.Mock.ofType(NotebookService, undefined, new TestLifecycleService(), undefined, undefined, undefined, instantiationService, new MockContextKeyService(),
|
||||
undefined, undefined, undefined, undefined, undefined, undefined, TestEnvironmentService);
|
||||
|
||||
mockNotebookService.setup(s => s.findNotebookEditor(TypeMoq.It.isAny())).returns(() => {
|
||||
return {
|
||||
cells: undefined,
|
||||
id: '0',
|
||||
notebookParams: undefined,
|
||||
modelReady: undefined,
|
||||
model: notebookModel,
|
||||
isDirty: undefined,
|
||||
isActive: undefined,
|
||||
isVisible: undefined,
|
||||
runAllCells: undefined,
|
||||
runCell: undefined,
|
||||
clearAllOutputs: undefined,
|
||||
clearOutput: undefined,
|
||||
executeEdits: undefined,
|
||||
getSections: undefined,
|
||||
navigateToSection: undefined
|
||||
};
|
||||
});
|
||||
|
||||
let mockOnNotebookEditorAddEvent = new Emitter<INotebookEditor>();
|
||||
mockNotebookService.setup(s => s.onNotebookEditorAdd).returns(() => mockOnNotebookEditorAddEvent.event);
|
||||
|
||||
setup(() => {
|
||||
accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
|
||||
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
|
||||
};
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
if (accessor && accessor.textFileService && accessor.textFileService.models) {
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).clear();
|
||||
}
|
||||
});
|
||||
|
||||
test('should replace entire text model if NotebookChangeType is undefined', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineCount(), 6);
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(5), ' "cells": []');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(2), ' "metadata": {},');
|
||||
});
|
||||
|
||||
test('should replace entire text model for add cell (0 -> 1 cells)', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(26), ' }');
|
||||
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for execution count change', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 0');
|
||||
|
||||
newCell.executionCount = 1;
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellExecuted,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellExecuted);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 1');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(26), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
newCell.executionCount = 10;
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellExecuted,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellExecuted);
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 10');
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
newCell.executionCount = 15;
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellExecuted,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellExecuted);
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 15');
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
newCell.executionCount = 105;
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellExecuted,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellExecuted);
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 105');
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for clear output', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellOutputCleared,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputCleared);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(15), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(16), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for multiline source change', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellSourceUpdated,
|
||||
cells: [newCell],
|
||||
cellIndex: 0,
|
||||
modelContentChangedEvent: {
|
||||
changes: [{ range: new Range(1, 1, 1, 1), rangeLength: 0, rangeOffset: 0, text: 'This is a test' + os.EOL + 'Line 2 test' + os.EOL + 'Line 3 test' }],
|
||||
eol: os.EOL,
|
||||
isFlush: false,
|
||||
isRedoing: false,
|
||||
isUndoing: false,
|
||||
versionId: 2
|
||||
}
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellSourceUpdated);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "This is a test\\n",');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' "Line 2 test\\n",');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(11), ' "Line 3 test"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(16), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(27), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(28), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for single line source change', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellSourceUpdated,
|
||||
cells: [newCell],
|
||||
cellIndex: 0,
|
||||
modelContentChangedEvent: {
|
||||
changes: [{ range: new Range(1, 1, 1, 1), rangeLength: 0, rangeOffset: 0, text: 'This is a test' }],
|
||||
eol: '\n',
|
||||
isFlush: false,
|
||||
isRedoing: false,
|
||||
isUndoing: false,
|
||||
versionId: 2
|
||||
}
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellSourceUpdated);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "This is a test"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(26), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for single line source change then delete', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' ""');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellSourceUpdated,
|
||||
cells: [newCell],
|
||||
cellIndex: 0,
|
||||
modelContentChangedEvent: {
|
||||
changes: [{ range: new Range(1, 1, 1, 1), rangeLength: 0, rangeOffset: 0, text: 'This is a test' }],
|
||||
eol: '\n',
|
||||
isFlush: false,
|
||||
isRedoing: false,
|
||||
isUndoing: false,
|
||||
versionId: 2
|
||||
}
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellSourceUpdated);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellSourceUpdated,
|
||||
cells: [newCell],
|
||||
cellIndex: 0,
|
||||
modelContentChangedEvent: {
|
||||
changes: [{ range: new Range(1, 1, 1, 15), rangeLength: 14, rangeOffset: 0, text: '' }],
|
||||
eol: '\n',
|
||||
isFlush: false,
|
||||
isRedoing: false,
|
||||
isUndoing: false,
|
||||
versionId: 3
|
||||
}
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellSourceUpdated);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' ""');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(26), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for multiline source delete', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellSourceUpdated,
|
||||
cells: [newCell],
|
||||
cellIndex: 0,
|
||||
modelContentChangedEvent: {
|
||||
changes: [{ range: new Range(1, 1, 1, 1), rangeLength: 0, rangeOffset: 0, text: 'This is a test' + os.EOL + 'Line 2 test' + os.EOL + 'Line 3 test' }],
|
||||
eol: os.EOL,
|
||||
isFlush: false,
|
||||
isRedoing: false,
|
||||
isUndoing: false,
|
||||
versionId: 2
|
||||
}
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellSourceUpdated);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "This is a test\\n",');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' "Line 2 test\\n",');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(11), ' "Line 3 test"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellSourceUpdated,
|
||||
cells: [newCell],
|
||||
cellIndex: 0,
|
||||
modelContentChangedEvent: {
|
||||
changes: [{ range: new Range(1, 2, 3, 11), rangeLength: 36, rangeOffset: 1, text: '' }],
|
||||
eol: '\n',
|
||||
isFlush: false,
|
||||
isRedoing: false,
|
||||
isUndoing: false,
|
||||
versionId: 3
|
||||
}
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellSourceUpdated);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "Tt"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
});
|
||||
|
||||
test('should not replace entire text model and affect only edited cell', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell;
|
||||
let contentChange: NotebookContentChange;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let cell;
|
||||
if (i === 7) {
|
||||
newCell = notebookModel.addCell(CellTypes.Code);
|
||||
cell = newCell;
|
||||
} else {
|
||||
cell = notebookModel.addCell(CellTypes.Code);
|
||||
}
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [cell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
}
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellSourceUpdated,
|
||||
cells: [newCell],
|
||||
modelContentChangedEvent: {
|
||||
changes: [{ range: new Range(1, 1, 1, 1), rangeLength: 0, rangeOffset: 0, text: 'This is a test' }],
|
||||
eol: '\n',
|
||||
isFlush: false,
|
||||
isRedoing: false,
|
||||
isUndoing: false,
|
||||
versionId: 2
|
||||
}
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellSourceUpdated);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8 + i * 21), ' "source": [');
|
||||
if (i === 7) {
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9 + i * 21), ' "This is a test"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12 + i * 21), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
} else {
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9 + i * 21), ' ""');
|
||||
}
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10 + i * 21), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14 + i * 21), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25 + i * 21), ' "execution_count": 0');
|
||||
assert(startsWith(notebookEditorModel.editorModel.textEditorModel.getLineContent(26 + i * 21), ' }'));
|
||||
}
|
||||
});
|
||||
|
||||
test('should not replace entire text model for output changes', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
|
||||
newCell[<any>'_outputs'] = newCell.outputs.concat(newCell.outputs);
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellOutputUpdated,
|
||||
cells: [newCell]
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputUpdated);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(23), ' }, {');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(31), '}');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(32), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(33), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(34), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
test('should not insert update at incorrect location', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
|
||||
// First update the model with unmatched brackets
|
||||
let newUnmatchedBracketOutput: nb.IStreamResult = { output_type: 'stream', name: 'stdout', text: '[0em' };
|
||||
newCell[<any>'_outputs'] = newCell.outputs.concat(newUnmatchedBracketOutput);
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellOutputUpdated,
|
||||
cells: [newCell]
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputUpdated);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(26), ' "text": "[0em"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(27), '}');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(28), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(29), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(30), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
// Now test updating the model after an unmatched bracket was previously output
|
||||
let newBracketlessOutput: nb.IStreamResult = { output_type: 'stream', name: 'stdout', text: 'test test test' };
|
||||
newCell[<any>'_outputs'] = newCell[<any>'_outputs'].concat(newBracketlessOutput);
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellOutputUpdated,
|
||||
cells: [newCell]
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputUpdated);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(32), ' "text": "test test test"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(33), ' }');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(34), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(35), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(36), ' }');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(37), ' ]');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(38), '}');
|
||||
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for output changes (1st update)', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
let previousOutputs = newCell.outputs;
|
||||
// clear outputs
|
||||
newCell[<any>'_outputs'] = [];
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [],');
|
||||
|
||||
// add output
|
||||
newCell[<any>'_outputs'] = previousOutputs;
|
||||
|
||||
contentChange = {
|
||||
changeType: NotebookChangeType.CellOutputUpdated,
|
||||
cells: [newCell]
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputUpdated);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(23), '}');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(25), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(26), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with double quotes', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '"This text is in quotes"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "\\"This text is in quotes\\""');
|
||||
|
||||
ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel, newCell);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with many double quotes', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '""""""""""');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "\\"\\"\\"\\"\\"\\"\\"\\"\\"\\""');
|
||||
|
||||
ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel, newCell);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with many backslashes', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '\\\\\\\\\\');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "\\\\\\\\\\\\\\\\\\\\\"');
|
||||
|
||||
ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel, newCell);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with many backslashes and double quotes', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '\"\"\"\"\"\"\"\"\"\"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\""');
|
||||
|
||||
ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel, newCell);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with no special characters', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, 'this is a long line in a cell test. Everything should serialize correctly! # Comments here: adding more tests is fun?');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "this is a long line in a cell test. Everything should serialize correctly! # Comments here: adding more tests is fun?"');
|
||||
|
||||
ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel, newCell);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with variety of characters', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '`~1!2@3#4$5%6^7&8*9(0)-_=+[{]}\\|;:",<.>/?\'');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "`~1!2@3#4$5%6^7&8*9(0)-_=+[{]}\\\\|;:\\",<.>/?\'"');
|
||||
|
||||
ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel, newCell);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with single quotes', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '\'\'\'\'');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "\'\'\'\'"');
|
||||
|
||||
ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel, newCell);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with empty content', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' ""');
|
||||
|
||||
ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel, newCell);
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with multiline content', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '"test"' + os.EOL + 'test""');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "\\"test\\"\\n",');
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' "test\\"\\""');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(11), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(13), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(15), ' "outputs": [],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(16), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(17), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
test('should not replace entire text model for content change with multiline content different escaped characters', async function (): Promise<void> {
|
||||
await createNewNotebookModel();
|
||||
let notebookEditorModel = await createTextEditorModel(this);
|
||||
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
|
||||
|
||||
let newCell = notebookModel.addCell(CellTypes.Code);
|
||||
setupTextEditorModelWithEmptyOutputs(notebookEditorModel, newCell);
|
||||
|
||||
addTextToBeginningOfTextEditorModel(notebookEditorModel, newCell, '"""""test"' + os.EOL + '"""""""test\\""');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(9), ' "\\"\\"\\"\\"\\"test\\"\\n",');
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' "\\"\\"\\"\\"\\"\\"\\"test\\\\\\"\\""');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(11), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(13), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(15), ' "outputs": [],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(16), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(17), ' }');
|
||||
|
||||
assert(!notebookEditorModel.lastEditFullReplacement);
|
||||
});
|
||||
|
||||
async function createNewNotebookModel() {
|
||||
let options: INotebookModelOptions = assign({}, defaultModelOptions, <Partial<INotebookModelOptions>><unknown>{
|
||||
factory: mockModelFactory.object
|
||||
});
|
||||
notebookModel = new NotebookModel(options, undefined, logService, undefined, undefined);
|
||||
await notebookModel.loadContents();
|
||||
}
|
||||
|
||||
async function createTextEditorModel(self: Mocha.ITestCallbackContext): Promise<NotebookEditorModel> {
|
||||
let textFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(self, defaultUri.toString()), 'utf8', undefined);
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(textFileEditorModel.getResource(), textFileEditorModel);
|
||||
await textFileEditorModel.load();
|
||||
return new NotebookEditorModel(defaultUri, textFileEditorModel, mockNotebookService.object, accessor.textFileService, testResourcePropertiesService);
|
||||
}
|
||||
|
||||
function setupTextEditorModelWithEmptyOutputs(notebookEditorModel: NotebookEditorModel, newCell: ICellModel) {
|
||||
// clear outputs
|
||||
newCell[<any>'_outputs'] = [];
|
||||
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellsModified,
|
||||
cells: [newCell],
|
||||
cellIndex: 0
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
|
||||
assert(notebookEditorModel.lastEditFullReplacement);
|
||||
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [],');
|
||||
}
|
||||
|
||||
function addTextToBeginningOfTextEditorModel(notebookEditorModel: NotebookEditorModel, newCell: ICellModel, textToAdd: string) {
|
||||
let contentChange: NotebookContentChange = {
|
||||
changeType: NotebookChangeType.CellSourceUpdated,
|
||||
cells: [newCell],
|
||||
cellIndex: 0,
|
||||
modelContentChangedEvent: {
|
||||
changes: [{ range: new Range(1, 1, 1, 1), rangeLength: 0, rangeOffset: 0, text: textToAdd }],
|
||||
eol: '\n',
|
||||
isFlush: false,
|
||||
isRedoing: false,
|
||||
isUndoing: false,
|
||||
versionId: 2
|
||||
}
|
||||
};
|
||||
|
||||
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellSourceUpdated);
|
||||
}
|
||||
|
||||
function ensureStaticContentInOneLineCellIsCorrect(notebookEditorModel: NotebookEditorModel, newCell: ICellModel) {
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(8), ' "source": [');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(10), ' ],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(12), ' "azdata_cell_guid": "' + newCell.cellGuid + '"');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(14), ' "outputs": [],');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(15), ' "execution_count": 0');
|
||||
assert.equal(notebookEditorModel.editorModel.textEditorModel.getLineContent(16), ' }');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,593 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as objects from 'vs/base/common/objects';
|
||||
|
||||
import { CellTypes } from 'sql/workbench/contrib/notebook/common/models/contracts';
|
||||
import { ModelFactory } from 'sql/workbench/contrib/notebook/browser/models/modelFactory';
|
||||
import { NotebookModelStub } from './common';
|
||||
import { EmptyFuture } from 'sql/workbench/services/notebook/browser/sessionManager';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
|
||||
let instantiationService: IInstantiationService;
|
||||
|
||||
suite('Cell Model', function (): void {
|
||||
let serviceCollection = new ServiceCollection();
|
||||
instantiationService = new InstantiationService(serviceCollection, true);
|
||||
|
||||
let factory = new ModelFactory(instantiationService);
|
||||
test('Should set default values if none defined', async function (): Promise<void> {
|
||||
let cell = factory.createCell(undefined, undefined);
|
||||
assert.equal(cell.cellType, CellTypes.Code);
|
||||
assert.equal(cell.source, '');
|
||||
});
|
||||
|
||||
test('Should update values', async function (): Promise<void> {
|
||||
let cell = factory.createCell(undefined, undefined);
|
||||
cell.setOverrideLanguage('sql');
|
||||
assert.equal(cell.language, 'sql');
|
||||
cell.source = 'abcd';
|
||||
assert.equal(JSON.stringify(cell.source), JSON.stringify(['abcd']));
|
||||
});
|
||||
|
||||
test('Should match ICell values if defined', async function (): Promise<void> {
|
||||
let output: nb.IStreamResult = {
|
||||
output_type: 'stream',
|
||||
text: 'Some output',
|
||||
name: 'stdout'
|
||||
};
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Markdown,
|
||||
source: 'some *markdown*',
|
||||
outputs: [output],
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1
|
||||
};
|
||||
let cell = factory.createCell(cellData, undefined);
|
||||
assert.equal(cell.cellType, cellData.cell_type);
|
||||
assert.equal(JSON.stringify(cell.source), JSON.stringify([cellData.source]));
|
||||
assert.equal(cell.outputs.length, 1);
|
||||
assert.equal(cell.outputs[0].output_type, 'stream');
|
||||
assert.equal((<nb.IStreamResult>cell.outputs[0]).text, 'Some output');
|
||||
});
|
||||
|
||||
|
||||
test('Should set cell language to python if defined as python in languageInfo', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'print(\'1\')',
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: 'python',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert.equal(cell.language, 'python');
|
||||
});
|
||||
|
||||
test('Should set cell language to python if defined as pyspark in languageInfo', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'print(\'1\')',
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: 'pyspark',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert.equal(cell.language, 'python');
|
||||
});
|
||||
|
||||
test('Should keep cell language as python if cell has language override', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'print(\'1\')',
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: 'scala',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert.equal(cell.language, 'python');
|
||||
});
|
||||
|
||||
test('Should set cell language to python if no language defined', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'print(\'1\')',
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert.equal(cell.language, 'python');
|
||||
});
|
||||
|
||||
test('Should allow source of type string[] with length 1', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: ['print(1)'],
|
||||
metadata: { language: 'sql' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert(Array.isArray(cell.source));
|
||||
assert.equal(cell.source.length, 1);
|
||||
assert.equal(cell.source[0], 'print(1)');
|
||||
});
|
||||
|
||||
test('Should allow source of type string', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'print(1)',
|
||||
metadata: { language: 'sql' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert(Array.isArray(cell.source));
|
||||
assert.equal(JSON.stringify(cell.source), JSON.stringify(['print(1)']));
|
||||
});
|
||||
|
||||
test('Should allow source of type string with newline and split it', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'print(1)\nprint(2)',
|
||||
metadata: { language: 'sql' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert(Array.isArray(cell.source));
|
||||
assert.equal(cell.source.length, 2);
|
||||
assert.equal(cell.source[0], 'print(1)\n');
|
||||
assert.equal(cell.source[1], 'print(2)');
|
||||
});
|
||||
|
||||
test('Should allow source of type string with Windows style newline and split it', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'print(1)\r\nprint(2)',
|
||||
metadata: { language: 'sql' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert(Array.isArray(cell.source));
|
||||
assert.equal(cell.source.length, 2);
|
||||
assert.equal(cell.source[0], 'print(1)\r\n');
|
||||
assert.equal(cell.source[1], 'print(2)');
|
||||
});
|
||||
|
||||
test('Should allow source of type string[] with length 2', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: ['print(1)\n', 'print(2)'],
|
||||
metadata: { language: 'sql' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert(Array.isArray(cell.source));
|
||||
assert.equal(cell.source.length, 2);
|
||||
assert.equal(cell.source[0], 'print(1)\n');
|
||||
assert.equal(cell.source[1], 'print(2)');
|
||||
});
|
||||
|
||||
test('Should allow empty string source', async function (): Promise<void> {
|
||||
let cellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: '',
|
||||
metadata: { language: 'sql' },
|
||||
execution_count: 1
|
||||
};
|
||||
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
|
||||
assert(Array.isArray(cell.source));
|
||||
assert.equal(JSON.stringify(cell.source), JSON.stringify(['']));
|
||||
});
|
||||
|
||||
test('Should parse metadata\'s hide_input tag correctly', async function (): Promise<void> {
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let contents: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: ''
|
||||
};
|
||||
let model = factory.createCell(contents, { notebook: notebookModel, isTrusted: false });
|
||||
|
||||
assert(!model.isCollapsed);
|
||||
model.isCollapsed = true;
|
||||
assert(model.isCollapsed);
|
||||
model.isCollapsed = false;
|
||||
assert(!model.isCollapsed);
|
||||
|
||||
let modelJson = model.toJSON();
|
||||
assert(!isUndefinedOrNull(modelJson.metadata.tags));
|
||||
assert(!modelJson.metadata.tags.some(x => x === 'hide_input'));
|
||||
|
||||
contents.metadata = {
|
||||
tags: ['hide_input']
|
||||
};
|
||||
model = factory.createCell(contents, { notebook: notebookModel, isTrusted: false });
|
||||
|
||||
assert(model.isCollapsed);
|
||||
model.isCollapsed = false;
|
||||
assert(!model.isCollapsed);
|
||||
model.isCollapsed = true;
|
||||
assert(model.isCollapsed);
|
||||
|
||||
modelJson = model.toJSON();
|
||||
assert(!isUndefinedOrNull(modelJson.metadata.tags));
|
||||
assert(modelJson.metadata.tags.some(x => x === 'hide_input'));
|
||||
|
||||
contents.metadata = {
|
||||
tags: ['not_a_real_tag']
|
||||
};
|
||||
model = factory.createCell(contents, { notebook: notebookModel, isTrusted: false });
|
||||
modelJson = model.toJSON();
|
||||
assert(!isUndefinedOrNull(modelJson.metadata.tags));
|
||||
assert(!modelJson.metadata.tags.some(x => x === 'hide_input'));
|
||||
|
||||
contents.metadata = {
|
||||
tags: ['not_a_real_tag', 'hide_input']
|
||||
};
|
||||
model = factory.createCell(contents, { notebook: notebookModel, isTrusted: false });
|
||||
modelJson = model.toJSON();
|
||||
assert(!isUndefinedOrNull(modelJson.metadata.tags));
|
||||
assert(modelJson.metadata.tags.some(x => x === 'hide_input'));
|
||||
});
|
||||
|
||||
test('Should emit event after collapsing cell', async function (): Promise<void> {
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
let contents: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: ''
|
||||
};
|
||||
let model = factory.createCell(contents, { notebook: notebookModel, isTrusted: false });
|
||||
assert(!model.isCollapsed);
|
||||
|
||||
let createCollapsePromise = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => reject(), 2000);
|
||||
model.onCollapseStateChanged(isCollapsed => {
|
||||
resolve(isCollapsed);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
assert(!model.isCollapsed);
|
||||
let collapsePromise = createCollapsePromise();
|
||||
model.isCollapsed = true;
|
||||
let isCollapsed = await collapsePromise;
|
||||
assert(isCollapsed);
|
||||
|
||||
collapsePromise = createCollapsePromise();
|
||||
model.isCollapsed = false;
|
||||
isCollapsed = await collapsePromise;
|
||||
assert(!isCollapsed);
|
||||
});
|
||||
|
||||
test('Should not allow markdown cells to be collapsible', async function (): Promise<void> {
|
||||
let mdCellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Markdown,
|
||||
source: 'some *markdown*',
|
||||
outputs: [],
|
||||
metadata: { language: 'python' }
|
||||
};
|
||||
let cell = factory.createCell(mdCellData, undefined);
|
||||
assert(cell.isCollapsed === false);
|
||||
cell.isCollapsed = true;
|
||||
// The typescript compiler will complain if we don't ignore the error from the following line,
|
||||
// claiming that cell.isCollapsed will return true. It doesn't.
|
||||
// @ts-ignore
|
||||
assert(cell.isCollapsed === false);
|
||||
|
||||
let codeCellData: nb.ICellContents = {
|
||||
cell_type: CellTypes.Code,
|
||||
source: '1+1',
|
||||
outputs: [],
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1
|
||||
};
|
||||
cell = factory.createCell(codeCellData, undefined);
|
||||
assert(cell.isCollapsed === false);
|
||||
cell.isCollapsed = true;
|
||||
assert(cell.isCollapsed === true);
|
||||
});
|
||||
|
||||
suite('Model Future handling', function (): void {
|
||||
let future: TypeMoq.Mock<EmptyFuture>;
|
||||
let cell: ICellModel;
|
||||
const stdInDefaultMessage: nb.IStdinMessage = {
|
||||
channel: 'stdin',
|
||||
type: 'stdin',
|
||||
parent_header: undefined,
|
||||
metadata: undefined,
|
||||
header: <nb.IHeader>{
|
||||
msg_type: 'stream'
|
||||
},
|
||||
content: {
|
||||
prompt: 'Prompt',
|
||||
password: false
|
||||
}
|
||||
};
|
||||
setup(() => {
|
||||
future = TypeMoq.Mock.ofType(EmptyFuture);
|
||||
cell = factory.createCell({
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'print "Hello"',
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1
|
||||
}, {
|
||||
notebook: new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: 'x-scala'
|
||||
}),
|
||||
isTrusted: false
|
||||
});
|
||||
});
|
||||
|
||||
test('should send and handle incoming messages', async () => {
|
||||
// Given a future
|
||||
let onReply: nb.MessageHandler<nb.IShellMessage>;
|
||||
let onIopub: nb.MessageHandler<nb.IIOPubMessage>;
|
||||
future.setup(f => f.setReplyHandler(TypeMoq.It.isAny())).callback((handler) => onReply = handler);
|
||||
future.setup(f => f.setIOPubHandler(TypeMoq.It.isAny())).callback((handler) => onIopub = handler);
|
||||
let outputs: ReadonlyArray<nb.ICellOutput> = undefined;
|
||||
cell.onOutputsChanged((o => outputs = o.outputs));
|
||||
|
||||
// When I set it on the cell
|
||||
cell.setFuture(future.object);
|
||||
|
||||
// Then I expect outputs to have been cleared
|
||||
assert.equal(outputs.length, 0);
|
||||
assert(!isUndefinedOrNull(onReply));
|
||||
// ... And when I send an IoPub message
|
||||
let message: nb.IIOPubMessage = {
|
||||
channel: 'iopub',
|
||||
type: 'iopub',
|
||||
parent_header: undefined,
|
||||
metadata: undefined,
|
||||
header: <nb.IHeader>{
|
||||
msg_type: 'stream'
|
||||
},
|
||||
content: {
|
||||
text: 'Printed hello world'
|
||||
}
|
||||
};
|
||||
onIopub.handle(message);
|
||||
// Then I expect an output to be added
|
||||
assert.equal(outputs.length, 1);
|
||||
assert.equal(outputs[0].output_type, 'stream');
|
||||
|
||||
message = objects.deepClone(message);
|
||||
message.header.msg_type = 'display_data';
|
||||
onIopub.handle(message);
|
||||
assert.equal(outputs[1].output_type, 'display_data');
|
||||
});
|
||||
|
||||
test('stdin should return void if no handler registered', async () => {
|
||||
// Given stdIn does not have a request handler setup
|
||||
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
|
||||
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
|
||||
|
||||
// When I set it on the cell
|
||||
cell.setFuture(future.object);
|
||||
|
||||
// Then I expect stdIn to have been hooked up
|
||||
assert(!isUndefinedOrNull(onStdIn));
|
||||
// ... And when I send a stdIn request message
|
||||
let result = onStdIn.handle(stdInDefaultMessage);
|
||||
// Then I expect the promise to resolve
|
||||
await result;
|
||||
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
});
|
||||
|
||||
test('stdin should wait on handler if handler registered', async () => {
|
||||
// Given stdIn has a handler set up
|
||||
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
|
||||
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
|
||||
|
||||
let deferred = new Deferred<void>();
|
||||
let stdInMessage: nb.IStdinMessage = undefined;
|
||||
cell.setStdInHandler({
|
||||
handle: (msg: nb.IStdinMessage) => {
|
||||
stdInMessage = msg;
|
||||
return deferred.promise;
|
||||
}
|
||||
});
|
||||
|
||||
// When I send a stdIn request message
|
||||
cell.setFuture(future.object);
|
||||
let result = onStdIn.handle(stdInDefaultMessage);
|
||||
deferred.resolve();
|
||||
// Then I expect promise to resolve since it should wait on upstream handling
|
||||
await result;
|
||||
// And I expect message to have been passed upstream and no message sent from the cell
|
||||
assert(!isUndefinedOrNull(stdInMessage));
|
||||
assert.equal(stdInMessage.content.prompt, stdInDefaultMessage.content.prompt);
|
||||
assert.equal(stdInMessage.content.password, stdInDefaultMessage.content.password);
|
||||
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
});
|
||||
test('stdin should send default response if there is upstream error', async () => {
|
||||
// Given stdIn has a handler set up
|
||||
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
|
||||
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
|
||||
|
||||
let deferred = new Deferred<void>();
|
||||
cell.setStdInHandler({
|
||||
handle: (msg: nb.IStdinMessage) => {
|
||||
return deferred.promise;
|
||||
}
|
||||
});
|
||||
|
||||
// When I send a stdIn request message
|
||||
cell.setFuture(future.object);
|
||||
let result = onStdIn.handle(stdInDefaultMessage);
|
||||
deferred.reject('Something went wrong');
|
||||
// Then I expect promise to resolve since it should wait on upstream handling
|
||||
await result;
|
||||
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('should delete transient tag while handling incoming messages', async () => {
|
||||
// Given a future
|
||||
let onIopub: nb.MessageHandler<nb.IIOPubMessage>;
|
||||
future.setup(f => f.setIOPubHandler(TypeMoq.It.isAny())).callback((handler) => onIopub = handler);
|
||||
let outputs: ReadonlyArray<nb.ICellOutput> = undefined;
|
||||
cell.onOutputsChanged((o => outputs = o.outputs));
|
||||
|
||||
//Set the future
|
||||
cell.setFuture(future.object);
|
||||
|
||||
// ... And when I send an IoPub message
|
||||
let message: nb.IIOPubMessage = {
|
||||
channel: 'iopub',
|
||||
type: 'iopub',
|
||||
parent_header: undefined,
|
||||
metadata: undefined,
|
||||
header: <nb.IHeader>{
|
||||
msg_type: 'display_data'
|
||||
},
|
||||
content: {
|
||||
text: 'Printed hello world',
|
||||
transient: 'transient data'
|
||||
}
|
||||
};
|
||||
onIopub.handle(message);
|
||||
//Output array's length should be 1
|
||||
//'transient' tag should no longer exist in the output
|
||||
assert.equal(outputs.length, 1);
|
||||
assert(isUndefinedOrNull(outputs[0]['transient']));
|
||||
});
|
||||
|
||||
test('should dispose old future', async () => {
|
||||
let oldFuture = TypeMoq.Mock.ofType(EmptyFuture);
|
||||
cell.setFuture(oldFuture.object);
|
||||
|
||||
cell.setFuture(future.object);
|
||||
|
||||
oldFuture.verify(f => f.dispose(), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('should include cellGuid', async () => {
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
|
||||
let cell = factory.createCell(undefined, { notebook: notebookModel, isTrusted: false });
|
||||
assert(!isUndefinedOrNull(cell.cellGuid));
|
||||
assert.equal(cell.cellGuid.length, 36);
|
||||
let cellJson = cell.toJSON();
|
||||
assert(!isUndefinedOrNull(cellJson.metadata.azdata_cell_guid));
|
||||
});
|
||||
|
||||
test('should include azdata_cell_guid in metadata', async () => {
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
|
||||
let cell = factory.createCell(undefined, { notebook: notebookModel, isTrusted: false });
|
||||
let cellJson = cell.toJSON();
|
||||
assert(!isUndefinedOrNull(cellJson.metadata.azdata_cell_guid));
|
||||
});
|
||||
|
||||
// This is critical for the notebook editor model to parse changes correctly
|
||||
// If this test fails, please ensure that the notebookEditorModel tests still pass
|
||||
test('should stringify in the correct order', async () => {
|
||||
let notebookModel = new NotebookModelStub({
|
||||
name: '',
|
||||
version: '',
|
||||
mimetype: ''
|
||||
});
|
||||
|
||||
let cell = factory.createCell(undefined, { notebook: notebookModel, isTrusted: false });
|
||||
let content = JSON.stringify(cell.toJSON(), undefined, ' ');
|
||||
let contentSplit = content.split('\n');
|
||||
assert.equal(contentSplit.length, 9);
|
||||
assert(startsWith(contentSplit[0].trim(), '{'));
|
||||
assert(startsWith(contentSplit[1].trim(), '"cell_type": "code",'));
|
||||
assert(startsWith(contentSplit[2].trim(), '"source": ""'));
|
||||
assert(startsWith(contentSplit[3].trim(), '"metadata": {'));
|
||||
assert(startsWith(contentSplit[4].trim(), '"azdata_cell_guid": "'));
|
||||
assert(startsWith(contentSplit[5].trim(), '}'));
|
||||
assert(startsWith(contentSplit[6].trim(), '"outputs": []'));
|
||||
assert(startsWith(contentSplit[7].trim(), '"execution_count": 0'));
|
||||
assert(startsWith(contentSplit[8].trim(), '}'));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
import { ClientSession } from 'sql/workbench/contrib/notebook/browser/models/clientSession';
|
||||
import { SessionManager } from 'sql/workbench/services/notebook/browser/sessionManager';
|
||||
import { NotebookManagerStub, ServerManagerStub } from './common';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
|
||||
suite('Client Session', function (): void {
|
||||
let path = URI.file('my/notebook.ipynb');
|
||||
let notebookManager: NotebookManagerStub;
|
||||
let serverManager: ServerManagerStub;
|
||||
let mockSessionManager: TypeMoq.Mock<nb.SessionManager>;
|
||||
let notificationService: TypeMoq.Mock<INotificationService>;
|
||||
let session: ClientSession;
|
||||
|
||||
setup(() => {
|
||||
serverManager = new ServerManagerStub();
|
||||
mockSessionManager = TypeMoq.Mock.ofType(SessionManager);
|
||||
notebookManager = new NotebookManagerStub();
|
||||
notebookManager.serverManager = serverManager;
|
||||
notebookManager.sessionManager = mockSessionManager.object;
|
||||
notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose);
|
||||
|
||||
session = new ClientSession({
|
||||
notebookManager: notebookManager,
|
||||
notebookUri: path,
|
||||
notificationService: notificationService.object,
|
||||
kernelSpec: undefined
|
||||
});
|
||||
|
||||
let serverlessNotebookManager = new NotebookManagerStub();
|
||||
serverlessNotebookManager.sessionManager = mockSessionManager.object;
|
||||
});
|
||||
|
||||
test('Should set path, isReady and ready on construction', function (): void {
|
||||
assert.equal(session.notebookUri, path);
|
||||
assert(!isUndefinedOrNull(session.ready));
|
||||
assert(!session.isReady);
|
||||
assert.equal(session.status, 'starting');
|
||||
assert(!session.isInErrorState);
|
||||
assert(isUndefinedOrNull(session.errorMessage));
|
||||
});
|
||||
|
||||
test('Should call on serverManager startup if set', async function (): Promise<void> {
|
||||
// Given I have a serverManager that starts successfully
|
||||
serverManager.result = Promise.resolve();
|
||||
assert(!session.isReady);
|
||||
|
||||
// When I kick off initialization
|
||||
await session.initialize();
|
||||
|
||||
// Then I expect ready to be completed too
|
||||
await session.ready;
|
||||
assert(serverManager.calledStart);
|
||||
assert(session.isReady);
|
||||
});
|
||||
|
||||
test('Should go to error state if serverManager startup fails', async function (): Promise<void> {
|
||||
// Given I have a serverManager that fails to start
|
||||
serverManager.result = Promise.reject('error');
|
||||
assert(!session.isInErrorState);
|
||||
|
||||
// When I initialize
|
||||
await session.initialize();
|
||||
|
||||
// Then I expect ready to complete, but isInErrorState to be true
|
||||
await session.ready;
|
||||
assert(session.isReady);
|
||||
assert(serverManager.calledStart);
|
||||
assert(session.isInErrorState);
|
||||
assert.equal(session.errorMessage, 'error');
|
||||
});
|
||||
|
||||
test('Should be ready when session manager is ready', async function (): Promise<void> {
|
||||
serverManager.result = new Promise((resolve) => {
|
||||
serverManager.isStarted = true;
|
||||
resolve();
|
||||
});
|
||||
mockSessionManager.setup(s => s.ready).returns(() => Promise.resolve());
|
||||
|
||||
// When I call initialize
|
||||
await session.initialize();
|
||||
|
||||
// Then
|
||||
assert(session.isReady);
|
||||
assert(!session.isInErrorState);
|
||||
await session.ready;
|
||||
});
|
||||
|
||||
test('Should be in error state if server fails to start', async function (): Promise<void> {
|
||||
serverManager.result = new Promise((resolve) => {
|
||||
serverManager.isStarted = false;
|
||||
resolve();
|
||||
});
|
||||
mockSessionManager.setup(s => s.ready).returns(() => Promise.resolve());
|
||||
|
||||
// When I call initialize
|
||||
await session.initialize();
|
||||
|
||||
// Then
|
||||
await session.ready;
|
||||
assert(session.isReady);
|
||||
assert(session.isInErrorState);
|
||||
});
|
||||
|
||||
test('Should go to error state if sessionManager fails', async function (): Promise<void> {
|
||||
serverManager.isStarted = true;
|
||||
mockSessionManager.setup(s => s.isReady).returns(() => false);
|
||||
mockSessionManager.setup(s => s.ready).returns(() => Promise.reject('error'));
|
||||
|
||||
// When I call initialize
|
||||
await session.initialize();
|
||||
|
||||
// Then
|
||||
assert(session.isReady);
|
||||
assert(session.isInErrorState);
|
||||
assert.equal(session.errorMessage, 'error');
|
||||
});
|
||||
|
||||
// test('Should start session automatically if kernel preference requests it', async function (): Promise<void> {
|
||||
// serverManager.isStarted = true;
|
||||
// mockSessionManager.setup(s => s.ready).returns(() => Promise.resolve());
|
||||
// let sessionMock = TypeMoq.Mock.ofType(EmptySession);
|
||||
// let startOptions: nb.ISessionOptions = undefined;
|
||||
// mockSessionManager.setup(s => s.startNew(TypeMoq.It.isAny())).returns((options) => {
|
||||
// startOptions = options;
|
||||
// return Promise.resolve(sessionMock.object);
|
||||
// });
|
||||
|
||||
// // When I call initialize after defining kernel preferences
|
||||
// session.kernelPreference = {
|
||||
// shouldStart: true,
|
||||
// name: 'python'
|
||||
// };
|
||||
// await session.initialize();
|
||||
|
||||
// // Then
|
||||
// should(session.isReady).be.true();
|
||||
// should(session.isInErrorState).be.false();
|
||||
// should(startOptions.kernelName).equal('python');
|
||||
// should(startOptions.path).equal(path.fsPath);
|
||||
// });
|
||||
|
||||
// test('Should shutdown session even if no serverManager is set', async function (): Promise<void> {
|
||||
// // Given a session against a remote server
|
||||
// let expectedId = 'abc';
|
||||
// mockSessionManager.setup(s => s.isReady).returns(() => true);
|
||||
// mockSessionManager.setup(s => s.shutdown(TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||
// let sessionMock = TypeMoq.Mock.ofType(EmptySession);
|
||||
// sessionMock.setup(s => s.id).returns(() => expectedId);
|
||||
// mockSessionManager.setup(s => s.startNew(TypeMoq.It.isAny())).returns(() => Promise.resolve(sessionMock.object));
|
||||
|
||||
// remoteSession.kernelPreference = {
|
||||
// shouldStart: true,
|
||||
// name: 'python'
|
||||
// };
|
||||
// await remoteSession.initialize();
|
||||
|
||||
// // When I call shutdown
|
||||
// await remoteSession.shutdown();
|
||||
|
||||
// // Then
|
||||
// mockSessionManager.verify(s => s.shutdown(TypeMoq.It.isValue(expectedId)), TypeMoq.Times.once());
|
||||
// });
|
||||
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { nb, IConnectionProfile } from 'azdata';
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { INotebookModel, ICellModel, IClientSession, IDefaultConnection, NotebookContentChange } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookChangeType, CellType } from 'sql/workbench/contrib/notebook/common/models/contracts';
|
||||
import { INotebookManager } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IStandardKernelWithProvider } from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
|
||||
export class NotebookModelStub implements INotebookModel {
|
||||
constructor(private _languageInfo?: nb.ILanguageInfo) {
|
||||
}
|
||||
public trustedMode: boolean;
|
||||
language: string;
|
||||
standardKernels: IStandardKernelWithProvider[];
|
||||
|
||||
public get languageInfo(): nb.ILanguageInfo {
|
||||
return this._languageInfo;
|
||||
}
|
||||
onCellChange(cell: ICellModel, change: NotebookChangeType): void {
|
||||
// Default: do nothing
|
||||
}
|
||||
get cells(): ReadonlyArray<ICellModel> {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get activeCell(): ICellModel {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get clientSession(): IClientSession {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get notebookManagers(): INotebookManager[] {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get kernelChanged(): Event<nb.IKernelChangedArgs> {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get kernelsChanged(): Event<nb.IKernelSpec> {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get layoutChanged(): Event<void> {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get defaultKernel(): nb.IKernelSpec {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get contextsChanged(): Event<void> {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get contextsLoading(): Event<void> {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get contentChanged(): Event<NotebookContentChange> {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get specs(): nb.IAllKernels {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get contexts(): IDefaultConnection {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get providerId(): string {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get applicableConnectionProviderIds(): string[] {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
getStandardKernelFromName(name: string): IStandardKernelWithProvider {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
changeKernel(displayName: string): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
changeContext(host: string, connection?: IConnectionProfile, hideErrorMessage?: boolean): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
findCellIndex(cellModel: ICellModel): number {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
addCell(cellType: CellType, index?: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
deleteCell(cellModel: ICellModel): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
pushEditOperations(edits: ISingleNotebookEditOperation[]): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getApplicableConnectionProviderIds(kernelName: string): string[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
get onValidConnectionSelected(): Event<boolean> {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get onProviderIdChange(): Event<string> {
|
||||
throw new Error('method not impelemented.');
|
||||
}
|
||||
toJSON(): nb.INotebookContents {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
serializationStateChanged(changeType: NotebookChangeType): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
get onActiveCellChanged(): Event<ICellModel> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
updateActiveCell(cell: ICellModel) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class NotebookManagerStub implements INotebookManager {
|
||||
providerId: string;
|
||||
contentManager: nb.ContentManager;
|
||||
sessionManager: nb.SessionManager;
|
||||
serverManager: nb.ServerManager;
|
||||
}
|
||||
|
||||
export class ServerManagerStub implements nb.ServerManager {
|
||||
public onServerStartedEmitter = new Emitter<void>();
|
||||
onServerStarted: Event<void> = this.onServerStartedEmitter.event;
|
||||
isStarted: boolean = false;
|
||||
calledStart: boolean = false;
|
||||
calledEnd: boolean = false;
|
||||
public result: Promise<void> = undefined;
|
||||
|
||||
startServer(): Promise<void> {
|
||||
this.calledStart = true;
|
||||
return this.result;
|
||||
}
|
||||
stopServer(): Promise<void> {
|
||||
this.calledEnd = true;
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { nb } from 'azdata';
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as tempWrite from 'temp-write';
|
||||
import { LocalContentManager } from 'sql/workbench/services/notebook/common/localContentManager';
|
||||
import * as testUtils from '../../../../../base/test/common/async';
|
||||
import { CellTypes } from 'sql/workbench/contrib/notebook/common/models/contracts';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { TestFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IFileService, IReadFileOptions, IFileContent, IWriteFileOptions, IFileStatWithMetadata } from 'vs/platform/files/common/files';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
|
||||
let expectedNotebookContent: nb.INotebookContents = {
|
||||
cells: [{
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'insert into t1 values (c1, c2)',
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1
|
||||
}],
|
||||
metadata: {
|
||||
kernelspec: {
|
||||
name: 'mssql',
|
||||
language: 'sql'
|
||||
}
|
||||
},
|
||||
nbformat: 4,
|
||||
nbformat_minor: 2
|
||||
};
|
||||
let notebookContentString = JSON.stringify(expectedNotebookContent);
|
||||
|
||||
function verifyMatchesExpectedNotebook(notebook: nb.INotebookContents): void {
|
||||
assert.equal(notebook.cells.length, 1, 'Expected 1 cell');
|
||||
assert.equal(notebook.cells[0].cell_type, CellTypes.Code);
|
||||
assert.equal(notebook.cells[0].source, expectedNotebookContent.cells[0].source);
|
||||
assert.equal(notebook.metadata.kernelspec.name, expectedNotebookContent.metadata.kernelspec.name);
|
||||
assert.equal(notebook.nbformat, expectedNotebookContent.nbformat);
|
||||
assert.equal(notebook.nbformat_minor, expectedNotebookContent.nbformat_minor);
|
||||
}
|
||||
|
||||
suite('Local Content Manager', function (): void {
|
||||
let contentManager: LocalContentManager;
|
||||
|
||||
setup(() => {
|
||||
const instantiationService = new TestInstantiationService();
|
||||
const fileService = new class extends TestFileService {
|
||||
async readFile(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileContent> {
|
||||
const content = await pfs.readFile(resource.fsPath);
|
||||
return { name: ',', size: 0, etag: '', mtime: 0, value: VSBuffer.fromString(content.toString()), resource };
|
||||
}
|
||||
async writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable, options?: IWriteFileOptions): Promise<IFileStatWithMetadata> {
|
||||
await pfs.writeFile(resource.fsPath, bufferOrReadable.toString());
|
||||
return { resource: resource, mtime: 0, etag: '', size: 0, name: '', isDirectory: false };
|
||||
}
|
||||
};
|
||||
instantiationService.set(IFileService, fileService);
|
||||
contentManager = instantiationService.createInstance(LocalContentManager);
|
||||
});
|
||||
|
||||
test('Should return undefined if path is undefined', async function (): Promise<void> {
|
||||
let content = await contentManager.getNotebookContents(undefined);
|
||||
assert(isUndefinedOrNull(content));
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
content = await contentManager.getNotebookContents(null);
|
||||
assert(isUndefinedOrNull(content));
|
||||
});
|
||||
|
||||
test('Should throw if file does not exist', async function (): Promise<void> {
|
||||
await testUtils.assertThrowsAsync(async () => await contentManager.getNotebookContents(URI.file('/path/doesnot/exist.ipynb')), undefined);
|
||||
});
|
||||
test('Should return notebook contents parsed as INotebook when valid notebook file parsed', async function (): Promise<void> {
|
||||
// Given a file containing a valid notebook
|
||||
let localFile = tempWrite.sync(notebookContentString, 'notebook.ipynb');
|
||||
// when I read the content
|
||||
let notebook = await contentManager.getNotebookContents(URI.file(localFile));
|
||||
// then I expect notebook format to match
|
||||
verifyMatchesExpectedNotebook(notebook);
|
||||
});
|
||||
test('Should ignore invalid content in the notebook file', async function (): Promise<void> {
|
||||
// Given a file containing a notebook with some garbage properties
|
||||
let invalidContent = notebookContentString + '\\nasddfdsafasdf';
|
||||
let localFile = tempWrite.sync(invalidContent, 'notebook.ipynb');
|
||||
// when I read the content
|
||||
let notebook = await contentManager.getNotebookContents(URI.file(localFile));
|
||||
// then I expect notebook format to still be valid
|
||||
verifyMatchesExpectedNotebook(notebook);
|
||||
});
|
||||
test('Should inline mime data into a single string', async function (): Promise<void> {
|
||||
let mimeNotebook: nb.INotebookContents = {
|
||||
cells: [{
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'insert into t1 values (c1, c2)',
|
||||
metadata: { language: 'python' },
|
||||
execution_count: 1,
|
||||
outputs: [
|
||||
<nb.IDisplayData>{
|
||||
output_type: 'display_data',
|
||||
data: {
|
||||
'text/html': [
|
||||
'<div>',
|
||||
'</div>'
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}],
|
||||
metadata: {
|
||||
kernelspec: {
|
||||
name: 'mssql',
|
||||
language: 'sql'
|
||||
}
|
||||
},
|
||||
nbformat: 4,
|
||||
nbformat_minor: 2
|
||||
};
|
||||
let mimeContentString = JSON.stringify(mimeNotebook);
|
||||
// Given a file containing a valid notebook with multiline mime type
|
||||
let localFile = tempWrite.sync(mimeContentString, 'notebook.ipynb');
|
||||
// when I read the content
|
||||
let notebook = await contentManager.getNotebookContents(URI.file(localFile));
|
||||
// then I expect output to have been normalized into a single string
|
||||
let displayOutput = <nb.IDisplayData>notebook.cells[0].outputs[0];
|
||||
assert.equal(displayOutput.data['text/html'], '<div></div>');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,292 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
import { LocalContentManager } from 'sql/workbench/services/notebook/common/localContentManager';
|
||||
import { NotebookManagerStub } from './common';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ModelFactory } from 'sql/workbench/contrib/notebook/browser/models/modelFactory';
|
||||
import { IClientSession, INotebookModelOptions, NotebookContentChange } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ClientSession } from 'sql/workbench/contrib/notebook/browser/models/clientSession';
|
||||
import { CellTypes, NotebookChangeType } from 'sql/workbench/contrib/notebook/common/models/contracts';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
|
||||
let expectedNotebookContent: nb.INotebookContents = {
|
||||
cells: [{
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'insert into t1 values (c1, c2)',
|
||||
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 expectedNotebookContentOneCell: nb.INotebookContents = {
|
||||
cells: [{
|
||||
cell_type: CellTypes.Code,
|
||||
source: 'insert into t1 values (c1, c2)',
|
||||
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 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;
|
||||
|
||||
suite('notebook model', function (): void {
|
||||
let notebookManagers = [new NotebookManagerStub()];
|
||||
let memento: TypeMoq.Mock<Memento>;
|
||||
let queryConnectionService: TypeMoq.Mock<TestConnectionManagementService>;
|
||||
let defaultModelOptions: INotebookModelOptions;
|
||||
const logService = new NullLogService();
|
||||
setup(() => {
|
||||
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;
|
||||
let serviceCollection = new ServiceCollection();
|
||||
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;
|
||||
});
|
||||
});
|
||||
|
||||
test('Should create no cells if model has no contents', async function (): Promise<void> {
|
||||
// Given an empty notebook
|
||||
let emptyNotebook: nb.INotebookContents = {
|
||||
cells: [],
|
||||
metadata: {
|
||||
kernelspec: {
|
||||
name: 'mssql',
|
||||
language: 'sql'
|
||||
}
|
||||
},
|
||||
nbformat: 4,
|
||||
nbformat_minor: 5
|
||||
};
|
||||
|
||||
let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
|
||||
mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(emptyNotebook));
|
||||
notebookManagers[0].contentManager = mockContentManager.object;
|
||||
// When I initialize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
|
||||
await model.loadContents();
|
||||
|
||||
// Then I expect to have 0 code cell as the contents
|
||||
assert.equal(model.cells.length, 0);
|
||||
// And Trust should be false by default
|
||||
assert(!model.trustedMode);
|
||||
});
|
||||
|
||||
test('Should use trusted state set in model load', async function (): Promise<void> {
|
||||
// Given a notebook
|
||||
let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
|
||||
mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContent));
|
||||
notebookManagers[0].contentManager = mockContentManager.object;
|
||||
// When I initialize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
|
||||
await model.loadContents(true);
|
||||
await model.requestModelLoad();
|
||||
|
||||
// Then Trust should be true
|
||||
assert(model.trustedMode);
|
||||
});
|
||||
|
||||
// test('Should throw if model load fails', async function(): Promise<void> {
|
||||
// // Given a call to get Contents fails
|
||||
// let error = new Error('File not found');
|
||||
// let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
|
||||
// mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).throws(error);
|
||||
// notebookManagers[0].contentManager = mockContentManager.object;
|
||||
|
||||
// // When I initalize the model
|
||||
// // Then it should throw
|
||||
// let model = new NotebookModel(defaultModelOptions);
|
||||
// should(model.inErrorState).be.false();
|
||||
// await testUtils.assertThrowsAsync(() => model.requestModelLoad(), error.message);
|
||||
// should(model.inErrorState).be.true();
|
||||
// });
|
||||
|
||||
// test('Should convert cell info to CellModels', async function(): Promise<void> {
|
||||
// // Given a notebook with 2 cells
|
||||
// let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
|
||||
// mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContent));
|
||||
// notebookManagers[0].contentManager = mockContentManager.object;
|
||||
|
||||
// // When I initalize the model
|
||||
// let model = new NotebookModel(defaultModelOptions);
|
||||
// await model.requestModelLoad();
|
||||
|
||||
// // Then I expect all cells to be in the model
|
||||
// should(model.cells).have.length(2);
|
||||
// should(model.cells[0].source).be.equal(expectedNotebookContent.cells[0].source);
|
||||
// should(model.cells[1].source).be.equal(expectedNotebookContent.cells[1].source);
|
||||
// });
|
||||
|
||||
// test('Should load contents but then go to error state if client session startup fails', async function(): Promise<void> {
|
||||
// let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
|
||||
// mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContentOneCell));
|
||||
// notebookManagers[0].contentManager = mockContentManager.object;
|
||||
|
||||
// // Given I have a session that fails to start
|
||||
// mockClientSession.setup(c => c.isInErrorState).returns(() => true);
|
||||
// mockClientSession.setup(c => c.errorMessage).returns(() => 'Error');
|
||||
// sessionReady.resolve();
|
||||
// let sessionFired = false;
|
||||
|
||||
// let options: INotebookModelOptions = Object.assign({}, defaultModelOptions, <Partial<INotebookModelOptions>> {
|
||||
// factory: mockModelFactory.object
|
||||
// });
|
||||
// let model = new NotebookModel(options);
|
||||
// model.onClientSessionReady((session) => sessionFired = true);
|
||||
// await model.requestModelLoad();
|
||||
// model.startSession(notebookManagers[0]);
|
||||
|
||||
// // Then I expect load to succeed
|
||||
// shouldHaveOneCell(model);
|
||||
// should(model.clientSession).not.be.undefined();
|
||||
// // but on server load completion I expect error state to be set
|
||||
// // Note: do not expect serverLoad event to throw even if failed
|
||||
// await model.sessionLoadFinished;
|
||||
// should(model.inErrorState).be.true();
|
||||
// should(sessionFired).be.false();
|
||||
// });
|
||||
|
||||
test('Should not be in error state if client session initialization succeeds', async function (): Promise<void> {
|
||||
let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
|
||||
mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContentOneCell));
|
||||
notebookManagers[0].contentManager = mockContentManager.object;
|
||||
let kernelChangedEmitter: Emitter<nb.IKernelChangedArgs> = new Emitter<nb.IKernelChangedArgs>();
|
||||
let statusChangedEmitter: Emitter<nb.ISession> = new Emitter<nb.ISession>();
|
||||
|
||||
mockClientSession.setup(c => c.isInErrorState).returns(() => false);
|
||||
mockClientSession.setup(c => c.isReady).returns(() => true);
|
||||
mockClientSession.setup(c => c.kernelChanged).returns(() => kernelChangedEmitter.event);
|
||||
mockClientSession.setup(c => c.statusChanged).returns(() => statusChangedEmitter.event);
|
||||
|
||||
queryConnectionService.setup(c => c.getActiveConnections(TypeMoq.It.isAny())).returns(() => null);
|
||||
|
||||
sessionReady.resolve();
|
||||
let actualSession: IClientSession = undefined;
|
||||
|
||||
let options: INotebookModelOptions = assign({}, defaultModelOptions, <Partial<INotebookModelOptions>>{
|
||||
factory: mockModelFactory.object
|
||||
});
|
||||
let model = new NotebookModel(options, undefined, logService, undefined, undefined);
|
||||
model.onClientSessionReady((session) => actualSession = session);
|
||||
await model.requestModelLoad();
|
||||
await model.startSession(notebookManagers[0]);
|
||||
|
||||
// Then I expect load to succeed
|
||||
assert(!isUndefinedOrNull(model.clientSession));
|
||||
// but on server load completion I expect error state to be set
|
||||
// Note: do not expect serverLoad event to throw even if failed
|
||||
await model.sessionLoadFinished;
|
||||
assert(!model.inErrorState);
|
||||
assert.equal(actualSession, mockClientSession.object);
|
||||
assert.equal(model.clientSession, mockClientSession.object);
|
||||
});
|
||||
|
||||
test('Should sanitize kernel display name when IP is included', async function (): Promise<void> {
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
|
||||
let displayName = 'PySpark (1.1.1.1)';
|
||||
let sanitizedDisplayName = model.sanitizeDisplayName(displayName);
|
||||
assert.equal(sanitizedDisplayName, 'PySpark');
|
||||
});
|
||||
|
||||
test('Should sanitize kernel display name properly when IP is not included', async function (): Promise<void> {
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
|
||||
let displayName = 'PySpark';
|
||||
let sanitizedDisplayName = model.sanitizeDisplayName(displayName);
|
||||
assert.equal(sanitizedDisplayName, 'PySpark');
|
||||
});
|
||||
|
||||
test('Should notify on trust set', async function () {
|
||||
// Given a notebook that's been loaded
|
||||
let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
|
||||
mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContent));
|
||||
notebookManagers[0].contentManager = mockContentManager.object;
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
|
||||
await model.requestModelLoad();
|
||||
|
||||
let actualChanged: NotebookContentChange;
|
||||
model.contentChanged((changed) => actualChanged = changed);
|
||||
// When I change trusted state
|
||||
model.trustedMode = true;
|
||||
|
||||
// Then content changed notification should be sent
|
||||
assert(model.trustedMode);
|
||||
assert(!isUndefinedOrNull(actualChanged));
|
||||
assert.equal(actualChanged.changeType, NotebookChangeType.TrustChanged);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConnectionProfile } from 'azdata';
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { formatServerNameWithDatabaseNameForAttachTo, getServerFromFormattedAttachToName, getDatabaseFromFormattedAttachToName } from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
|
||||
suite('notebookUtils', function (): void {
|
||||
let conn: IConnectionProfile = {
|
||||
connectionName: '',
|
||||
serverName: '',
|
||||
databaseName: '',
|
||||
userName: '',
|
||||
password: '',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: '',
|
||||
groupId: '',
|
||||
providerName: mssqlProviderName,
|
||||
saveProfile: true,
|
||||
id: '',
|
||||
options: {},
|
||||
azureTenantId: undefined
|
||||
};
|
||||
|
||||
test('Should format server and database name correctly for attach to', async function (): Promise<void> {
|
||||
let capabilitiesService = new TestCapabilitiesService();
|
||||
let connProfile = new ConnectionProfile(capabilitiesService, conn);
|
||||
connProfile.serverName = 'serverName';
|
||||
connProfile.databaseName = 'databaseName';
|
||||
let attachToNameFormatted = formatServerNameWithDatabaseNameForAttachTo(connProfile);
|
||||
assert.equal(attachToNameFormatted, 'serverName (databaseName)');
|
||||
});
|
||||
|
||||
test('Should format server name correctly for attach to', async function (): Promise<void> {
|
||||
let capabilitiesService = new TestCapabilitiesService();
|
||||
let connProfile = new ConnectionProfile(capabilitiesService, conn);
|
||||
connProfile.serverName = 'serverName';
|
||||
let attachToNameFormatted = formatServerNameWithDatabaseNameForAttachTo(connProfile);
|
||||
assert.equal(attachToNameFormatted, 'serverName');
|
||||
});
|
||||
|
||||
test('Should format server name correctly for attach to when database is undefined', async function (): Promise<void> {
|
||||
let capabilitiesService = new TestCapabilitiesService();
|
||||
let connProfile = new ConnectionProfile(capabilitiesService, conn);
|
||||
connProfile.serverName = 'serverName';
|
||||
connProfile.databaseName = undefined;
|
||||
let attachToNameFormatted = formatServerNameWithDatabaseNameForAttachTo(connProfile);
|
||||
assert.equal(attachToNameFormatted, 'serverName');
|
||||
});
|
||||
|
||||
test('Should format server name as empty string when server/database are undefined', async function (): Promise<void> {
|
||||
let capabilitiesService = new TestCapabilitiesService();
|
||||
let connProfile = new ConnectionProfile(capabilitiesService, conn);
|
||||
connProfile.serverName = undefined;
|
||||
connProfile.databaseName = undefined;
|
||||
let attachToNameFormatted = formatServerNameWithDatabaseNameForAttachTo(connProfile);
|
||||
assert.equal(attachToNameFormatted, '');
|
||||
});
|
||||
|
||||
test('Should extract server name when no database specified', async function (): Promise<void> {
|
||||
let serverName = getServerFromFormattedAttachToName('serverName');
|
||||
let databaseName = getDatabaseFromFormattedAttachToName('serverName');
|
||||
assert.equal(serverName, 'serverName');
|
||||
assert.equal(databaseName, '');
|
||||
});
|
||||
|
||||
test('Should extract server and database name', async function (): Promise<void> {
|
||||
let serverName = getServerFromFormattedAttachToName('serverName (databaseName)');
|
||||
let databaseName = getDatabaseFromFormattedAttachToName('serverName (databaseName)');
|
||||
assert.equal(serverName, 'serverName');
|
||||
assert.equal(databaseName, 'databaseName');
|
||||
});
|
||||
|
||||
test('Should extract server and database name with other parentheses', async function (): Promise<void> {
|
||||
let serverName = getServerFromFormattedAttachToName('serv()erName (databaseName)');
|
||||
let databaseName = getDatabaseFromFormattedAttachToName('serv()erName (databaseName)');
|
||||
assert.equal(serverName, 'serv()erName');
|
||||
assert.equal(databaseName, 'databaseName');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user