Notebooks: re-factor grid streaming (#12937)

* refactor grid streaming (convert to data first)

* change convertRowsToHtml method to return value

* remove griddataconversioncomplete checks

* send row data from STS to gridoutput component

* clean up code

* send data updates to cell model

* serialize cell output at the end of cell execution

* remove unused parameters

* update output contents instead of output reference

* remove unnecessary promise

* move azdata changes to proposed

* update comment
This commit is contained in:
Lucy Zhang
2020-10-28 09:08:15 -07:00
committed by GitHub
parent 42e16b1752
commit 86357b45b0
18 changed files with 266 additions and 412 deletions

View File

@@ -40,9 +40,8 @@ export class OutputComponent extends CellView implements OnInit, AfterViewInit {
private _initialized: boolean = false;
private _activeCellId: string;
private _componentInstance: IMimeComponent;
private _batchId?: number;
private _id?: number;
private _queryRunnerUri?: string;
private _batchId: number | undefined;
private _id: number | undefined;
public errorText: string;
constructor(
@@ -104,18 +103,14 @@ export class OutputComponent extends CellView implements OnInit, AfterViewInit {
return this._componentInstance;
}
@Input() set batchId(value: number) {
@Input() set batchId(value: number | undefined) {
this._batchId = value;
}
@Input() set id(value: number) {
@Input() set id(value: number | undefined) {
this._id = value;
}
@Input() set queryRunnerUri(value: string) {
this._queryRunnerUri = value;
}
get trustedMode(): boolean {
return this._trusted;
}
@@ -188,11 +183,8 @@ export class OutputComponent extends CellView implements OnInit, AfterViewInit {
this._componentInstance.cellModel = this.cellModel;
this._componentInstance.cellOutput = this.cellOutput;
this._componentInstance.bundleOptions = options;
if (this._queryRunnerUri) {
this._componentInstance.batchId = this._batchId;
this._componentInstance.id = this._id;
this._componentInstance.queryRunnerUri = this._queryRunnerUri;
}
this._componentInstance.batchId = this._batchId;
this._componentInstance.id = this._id;
this._changeref.detectChanges();
let el = <HTMLElement>componentRef.location.nativeElement;

View File

@@ -6,7 +6,7 @@
-->
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
<div #outputarea link-handler [isTrusted]="isTrusted" [notebookUri]="notebookUri" class="notebook-output" style="flex: 0 0 auto;">
<output-component *ngFor="let output of cellModel.outputs" [cellOutput]="output" [trustedMode] = "cellModel.trustedMode" [cellModel]="cellModel" [activeCellId]="activeCellId" [batchId]="output.batchId" [id]="output.id" [queryRunnerUri]="output.queryRunnerUri">
<output-component *ngFor="let output of cellModel.outputs" [cellOutput]="output" [trustedMode] = "cellModel.trustedMode" [cellModel]="cellModel" [activeCellId]="activeCellId" [batchId]="output.metadata?.resultSet?.batchId" [id]="output.metadata?.resultSet?.id">
</output-component>
</div>
</div>

View File

@@ -38,7 +38,6 @@ export class OutputAreaComponent extends AngularDisposable implements OnInit {
this._register(this.cellModel.onOutputsChanged(e => {
if (!(this._changeRef['destroyed'])) {
this._changeRef.detectChanges();
this._changeRef.detach();
if (e && e.shouldScroll) {
this.setFocusAndScroll(this.outputArea.nativeElement);
}

View File

@@ -10,7 +10,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
import { INotificationService } from 'vs/platform/notification/common/notification';
export class FileNotebookInput extends NotebookInput {
public static ID: string = 'workbench.editorinputs.fileNotebookInput';
@@ -22,10 +21,9 @@ export class FileNotebookInput extends NotebookInput {
@ITextModelService textModelService: ITextModelService,
@IInstantiationService instantiationService: IInstantiationService,
@INotebookService notebookService: INotebookService,
@IExtensionService extensionService: IExtensionService,
@INotificationService notificationService: INotificationService
@IExtensionService extensionService: IExtensionService
) {
super(title, resource, textInput, textModelService, instantiationService, notebookService, extensionService, notificationService);
super(title, resource, textInput, textModelService, instantiationService, notebookService, extensionService);
}
public get textInput(): FileEditorInput {

View File

@@ -33,9 +33,6 @@ import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileE
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
import { NotebookFindModel } from 'sql/workbench/contrib/notebook/browser/find/notebookFindModel';
import { onUnexpectedError } from 'vs/base/common/errors';
import { INotification, INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import * as nls from 'vs/nls';
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
@@ -231,8 +228,7 @@ export abstract class NotebookInput extends EditorInput {
@ITextModelService private textModelService: ITextModelService,
@IInstantiationService private instantiationService: IInstantiationService,
@INotebookService private notebookService: INotebookService,
@IExtensionService private extensionService: IExtensionService,
@INotificationService private notificationService: INotificationService
@IExtensionService private extensionService: IExtensionService
) {
super();
this._standardKernels = [];
@@ -301,16 +297,6 @@ export abstract class NotebookInput extends EditorInput {
}
async save(groupId: number, options?: ITextFileSaveOptions): Promise<IEditorInput | undefined> {
const conversionNotification: INotification = {
severity: Severity.Info,
message: nls.localize('convertingData', "Waiting for table data conversion to complete..."),
progress: {
infinite: true // Keep showing conversion notification until notificationHandle is closed
}
};
const notificationHandle = this.notificationService.notify(conversionNotification);
await this._model.getNotebookModel().gridDataConversionComplete;
notificationHandle.close();
this.updateModel();
let input = await this.textInput.save(groupId, options);
await this.setTrustForNewEditor(input);

View File

@@ -10,7 +10,6 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { INotificationService } from 'vs/platform/notification/common/notification';
export class UntitledNotebookInput extends NotebookInput {
public static ID: string = 'workbench.editorinputs.untitledNotebookInput';
@@ -22,10 +21,9 @@ export class UntitledNotebookInput extends NotebookInput {
@ITextModelService textModelService: ITextModelService,
@IInstantiationService instantiationService: IInstantiationService,
@INotebookService notebookService: INotebookService,
@IExtensionService extensionService: IExtensionService,
@INotificationService notificationService: INotificationService
@IExtensionService extensionService: IExtensionService
) {
super(title, resource, textInput, textModelService, instantiationService, notebookService, extensionService, notificationService);
super(title, resource, textInput, textModelService, instantiationService, notebookService, extensionService);
}
public get textInput(): UntitledTextEditorInput {

View File

@@ -11,10 +11,10 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDataResource, MaxTableRowsConfigName, NotebookConfigSectionName, IDataResourceSchema, IDataResourceFields, MAX_ROWS } from 'sql/workbench/services/notebook/browser/sql/sqlSessionManager';
import { IDataResource } from 'sql/workbench/services/notebook/browser/sql/sqlSessionManager';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import QueryRunner, { getEolString, shouldIncludeHeaders, shouldRemoveNewLines } from 'sql/workbench/services/query/common/queryRunner';
import { ResultSetSummary, ResultSetSubset, ICellValue, BatchSummary } from 'sql/workbench/services/query/common/query';
import { getEolString, shouldIncludeHeaders, shouldRemoveNewLines } from 'sql/workbench/services/query/common/queryRunner';
import { ResultSetSummary, ResultSetSubset, ICellValue } from 'sql/workbench/services/query/common/query';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { attachTableStyler } from 'sql/platform/theme/common/styler';
@@ -37,11 +37,9 @@ import { ToggleableAction } from 'sql/workbench/contrib/notebook/browser/noteboo
import { IInsightOptions } from 'sql/workbench/common/editor/query/chartState';
import { NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { IQueryManagementService } from 'sql/workbench/services/query/common/queryManagement';
import { values } from 'vs/base/common/collections';
import { URI } from 'vs/base/common/uri';
import { assign } from 'vs/base/common/objects';
import { escape } from 'sql/base/common/strings';
@Component({
selector: GridOutputComponent.SELECTOR,
@@ -57,17 +55,13 @@ export class GridOutputComponent extends AngularDisposable implements IMimeCompo
private _cellOutput: azdata.nb.ICellOutput;
private _bundleOptions: MimeModel.IOptions;
private _table: DataResourceTable;
private _batchId: number;
private _id: number;
private _queryRunnerUri: string;
private _queryRunner: QueryRunner;
private _configuredMaxRows: number = MAX_ROWS;
private _batchId: number | undefined;
private _id: number | undefined;
private _layoutCalledOnce: boolean = false;
constructor(
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IThemeService) private readonly themeService: IThemeService,
@Inject(IConfigurationService) private configurationService: IConfigurationService,
@Inject(IQueryManagementService) private queryManagementService: IQueryManagementService
@Inject(IThemeService) private readonly themeService: IThemeService
) {
super();
}
@@ -100,28 +94,22 @@ export class GridOutputComponent extends AngularDisposable implements IMimeCompo
this._cellOutput = value;
}
@Input() set batchId(value: number) {
@Input() set batchId(value: number | undefined) {
this._batchId = value;
}
@Input() set id(value: number) {
@Input() set id(value: number | undefined) {
this._id = value;
}
@Input() set queryRunnerUri(value: string) {
this._queryRunnerUri = value;
}
ngOnInit() {
let config = this.configurationService.getValue(NotebookConfigSectionName);
if (config) {
let maxRows = config[MaxTableRowsConfigName] ? config[MaxTableRowsConfigName] : undefined;
if (maxRows && maxRows > 0) {
this._configuredMaxRows = maxRows;
}
if (this.cellModel) {
this._register(this.cellModel.onTableUpdated(e => {
if (e.resultSet.batchId === this._batchId && e.resultSet.id === this._id) {
this.updateResult(e.resultSet, e.rows);
}
}));
}
// When a saved notebook is opened, there is no query runner
this._queryRunner = this.queryManagementService.getRunner(this._queryRunnerUri);
this.renderGrid();
}
@@ -132,51 +120,30 @@ export class GridOutputComponent extends AngularDisposable implements IMimeCompo
if (!this._table) {
let source = <IDataResource><any>this._bundleOptions.data[this.mimeType];
let state = new GridTableState(0, 0);
this._table = this.instantiationService.createInstance(DataResourceTable, this._batchId, this._id, this._queryRunner, source, this.cellModel, this.cellOutput, state);
this._table = this.instantiationService.createInstance(DataResourceTable, source, this.cellModel, this.cellOutput, state);
let outputElement = <HTMLElement>this.output.nativeElement;
outputElement.appendChild(this._table.element);
this._register(attachTableStyler(this._table, this.themeService));
this._table.onDidInsert();
this.layout();
if (this._queryRunner) {
this._register(this._queryRunner.onResultSetUpdate(resultSet => { this.updateResultSet(resultSet); }));
this._register(this._queryRunner.onBatchEnd(batch => { this.convertData(batch); }));
}
this._initialized = true;
}
}
updateResultSet(resultSet: ResultSetSummary | ResultSetSummary[]): void {
let resultsToUpdate: ResultSetSummary[];
if (!Array.isArray(resultSet)) {
resultsToUpdate = [resultSet];
} else {
resultsToUpdate = resultSet?.splice(0);
}
for (let set of resultsToUpdate) {
if (this._batchId === set.batchId && this._id === set.id) {
set.rowCount = set.rowCount > this._configuredMaxRows ? this._configuredMaxRows : set.rowCount;
this._table.updateResult(set);
this.layout();
}
}
}
convertData(batch: BatchSummary): void {
for (let set of batch.resultSetSummaries) {
if (set.batchId === this._batchId && set.id === this._id) {
set.rowCount = set.rowCount > this._configuredMaxRows ? this._configuredMaxRows : set.rowCount;
this._cellModel.addGridDataConversionPromise(this._table.convertData(set));
}
}
}
layout(): void {
if (this._table) {
let maxSize = Math.min(this._table.maximumSize, 500);
this._table.layout(maxSize);
}
}
updateResult(resultSet: ResultSetSummary, rows: ICellValue[][]): void {
this._table.updateResultSet(resultSet, rows);
if (!this._layoutCalledOnce) {
this.layout();
this._layoutCalledOnce = true;
}
}
}
class DataResourceTable extends GridTableBase<any> {
@@ -184,13 +151,8 @@ class DataResourceTable extends GridTableBase<any> {
private _gridDataProvider: DataResourceDataProvider;
private _chart: ChartView;
private _chartContainer: HTMLElement;
private _batchId: number;
private _id: number;
private _queryRunner: QueryRunner;
constructor(batchId: number,
id: number,
queryRunner: QueryRunner,
constructor(
source: IDataResource,
private cellModel: ICellModel,
private cellOutput: azdata.nb.ICellOutput,
@@ -202,10 +164,7 @@ class DataResourceTable extends GridTableBase<any> {
@IConfigurationService configurationService: IConfigurationService
) {
super(state, createResultSet(source), { actionOrientation: ActionsOrientation.HORIZONTAL }, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService);
this._batchId = batchId;
this._id = id;
this._queryRunner = queryRunner;
this._gridDataProvider = this.instantiationService.createInstance(DataResourceDataProvider, this._batchId, this._id, this._queryRunner, source, this.resultSet, this.cellModel);
this._gridDataProvider = this.instantiationService.createInstance(DataResourceDataProvider, source, this.resultSet, this.cellModel);
this._chart = this.instantiationService.createInstance(ChartView, false);
if (!this.cellOutput.metadata) {
@@ -292,28 +251,17 @@ class DataResourceTable extends GridTableBase<any> {
this.cellModel.sendChangeToNotebook(NotebookChangeType.CellMetadataUpdated);
}
public convertData(set: ResultSetSummary): Promise<void> {
return this._gridDataProvider.convertAllData(set);
}
public updateResult(resultSet: ResultSetSummary): void {
public updateResultSet(resultSet: ResultSetSummary, rows: ICellValue[][]): void {
this._gridDataProvider.updateResultSet(resultSet, rows);
super.updateResult(resultSet);
this._gridDataProvider.updateResultSet(resultSet);
}
}
export class DataResourceDataProvider implements IGridDataProvider {
private _rows: ICellValue[][];
private _documentUri: string;
private _queryRunner: QueryRunner;
private _batchId: number;
private _id: number;
private _resultSet: ResultSetSummary;
private _data: any;
constructor(
batchId: number,
id: number,
queryRunner: QueryRunner,
source: IDataResource,
resultSet: ResultSetSummary,
private cellModel: ICellModel,
@@ -325,42 +273,10 @@ export class DataResourceDataProvider implements IGridDataProvider {
@IInstantiationService private _instantiationService: IInstantiationService,
) {
this._documentUri = this.cellModel.notebookModel.notebookUri.toString();
this._queryRunner = queryRunner;
this._batchId = batchId;
this._id = id;
this._resultSet = resultSet;
this.initializeData();
this.transformSource(source);
}
private initializeData(): void {
// Set up data resource
let columnsResources: IDataResourceSchema[] = [];
this._resultSet.columnInfo.forEach(column => {
columnsResources.push({ name: escape(column.columnName) });
});
let columnsFields: IDataResourceFields = { fields: columnsResources };
let dataResource = {
schema: columnsFields,
data: []
};
// Set up html table string
let htmlTable: string[] = new Array(3);
htmlTable[0] = '<table>';
let columnHeaders = '<tr>';
for (let column of this._resultSet.columnInfo) {
columnHeaders += `<th>${escape(column.columnName)}</th>`;
}
columnHeaders += '</tr>';
htmlTable[1] = columnHeaders;
htmlTable[2] = '</table>';
this._data = {
'application/vnd.dataresource+json': dataResource,
'text/html': htmlTable
};
}
private transformSource(source: IDataResource): void {
this._rows = source.data.map(row => {
let rowData: azdata.DbCellValue[] = [];
@@ -377,46 +293,21 @@ export class DataResourceDataProvider implements IGridDataProvider {
});
}
public updateResultSet(resultSet: ResultSetSummary): void {
public updateResultSet(resultSet: ResultSetSummary, rows: ICellValue[][]): void {
this._resultSet = resultSet;
}
public async convertAllData(result: ResultSetSummary): Promise<void> {
// Querying 100 rows at a time. Querying large amount of rows will be slow and
// affect table rendering since each time the user scrolls, getRowData is called.
let numRows = 100;
for (let i = 0; i < result.rowCount; i += 100) {
if (i + 100 > result.rowCount) {
numRows += result.rowCount - i;
}
let rows = await this._queryRunner.getQueryRows(i, numRows, this._batchId, this._id);
this.convertData(rows);
}
this.cellModel.sendChangeToNotebook(NotebookChangeType.CellOutputUpdated);
}
private convertData(rows: ResultSetSubset): void {
let dataResourceRows = this.convertRowsToDataResource(rows);
let htmlStringArr = this.convertRowsToHtml(rows);
this._data['application/vnd.dataresource+json'].data = this._data['application/vnd.dataresource+json'].data.concat(dataResourceRows);
this._data['text/html'].splice(this._data['text/html'].length - 1, 0, ...htmlStringArr);
this.cellModel.updateOutputData(this._batchId, this._id, this._data);
this._rows = this._rows.concat(rows);
}
getRowData(rowStart: number, numberOfRows: number): Thenable<ResultSetSubset> {
if (this._queryRunner) {
return this._queryRunner.getQueryRows(rowStart, numberOfRows, this._batchId, this._id);
} else {
let rowEnd = rowStart + numberOfRows;
if (rowEnd > this._rows.length) {
rowEnd = this._rows.length;
}
let resultSubset: ResultSetSubset = {
rowCount: rowEnd - rowStart,
rows: this._rows.slice(rowStart, rowEnd)
};
return Promise.resolve(resultSubset);
let rowEnd = rowStart + numberOfRows;
if (rowEnd > this._rows.length) {
rowEnd = this._rows.length;
}
let resultSubset: ResultSetSubset = {
rowCount: rowEnd - rowStart,
rows: this._rows.slice(rowStart, rowEnd)
};
return Promise.resolve(resultSubset);
}
async copyResults(selection: Slick.Range[], includeHeaders?: boolean): Promise<void> {
@@ -454,13 +345,8 @@ export class DataResourceDataProvider implements IGridDataProvider {
}
serializeResults(format: SaveFormat, selection: Slick.Range[]): Thenable<void> {
if (this._queryRunner) {
selection = selection ? selection : [new Slick.Range(0, 0, this._resultSet.rowCount - 1, this._resultSet.columnInfo.length - 1)];
return this._queryRunner.serializeResults(this._batchId, this._id, format, selection);
} else {
let serializer = this._instantiationService.createInstance(ResultSerializer);
return serializer.handleSerialization(this._documentUri, format, (filePath) => this.doSerialize(serializer, filePath, format, selection));
}
let serializer = this._instantiationService.createInstance(ResultSerializer);
return serializer.handleSerialization(this._documentUri, format, (filePath) => this.doSerialize(serializer, filePath, format, selection));
}
private doSerialize(serializer: ResultSerializer, filePath: URI, format: SaveFormat, selection: Slick.Range[]): Promise<SaveResultsResponse | undefined> {
@@ -527,29 +413,6 @@ export class DataResourceDataProvider implements IGridDataProvider {
private isSelected(selection: Slick.Range): boolean {
return (selection && !((selection.fromCell === selection.toCell) && (selection.fromRow === selection.toRow)));
}
private convertRowsToDataResource(subset: ResultSetSubset): any[] {
return subset.rows.map(row => {
let rowObject: { [key: string]: any; } = {};
row.forEach((val, index) => {
rowObject[index] = val.displayValue;
});
return rowObject;
});
}
private convertRowsToHtml(subset: ResultSetSubset): string[] {
let htmlStringArr = [];
for (const row of subset.rows) {
let rowData = '<tr>';
for (let columnIndex = 0; columnIndex < row.length; columnIndex++) {
rowData += `<td>${escape(row[columnIndex].displayValue)}</td>`;
}
rowData += '</tr>';
htmlStringArr.push(rowData);
}
return htmlStringArr;
}
}

View File

@@ -25,7 +25,6 @@ export interface IMimeComponent {
cellOutput?: nb.ICellOutput;
batchId?: number;
id?: number;
queryRunnerUri?: string;
layout(): void;
}

View File

@@ -13,7 +13,7 @@ import * as uuid from 'uuid';
import * as sinon from 'sinon';
import { DataResourceDataProvider } from '../../browser/outputs/gridOutput.component';
import { IDataResource } from 'sql/workbench/services/notebook/browser/sql/sqlSessionManager';
import { ResultSetSubset, ResultSetSummary } from 'sql/workbench/services/query/common/query';
import { ResultSetSummary } from 'sql/workbench/services/query/common/query';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { TestFileDialogService, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices';
@@ -23,7 +23,6 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia
import { URI } from 'vs/base/common/uri';
import { CellModel } from 'sql/workbench/services/notebook/browser/models/cell';
import { createandLoadNotebookModel } from 'sql/workbench/contrib/notebook/test/browser/cellToolbarActions.test';
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
export class TestSerializationProvider implements azdata.SerializationProvider {
providerId: string;
@@ -98,9 +97,6 @@ suite('Data Resource Data Provider', function () {
let tempFolderPath = path.join(os.tmpdir(), `TestDataResourceDataProvider_${uuid.v4()}`);
await fs.mkdir(tempFolderPath);
let dataResourceDataProvider = new DataResourceDataProvider(
0, // batchId
0, // id
undefined, // QueryRunner
source,
resultSet,
cellModel.object,
@@ -131,38 +127,4 @@ suite('Data Resource Data Provider', function () {
const withHeadersResult = await fs.readFile(withHeadersFile.fsPath);
assert.equal(withHeadersResult.toString(), 'col1 col2 \n1 2 \n3 4 \n', 'result data should include headers');
});
test('convertAllData correctly converts row data to mimetype and html', async function (): Promise<void> {
let resultSetSubset: ResultSetSubset = {
rowCount: 2,
rows: [[{ displayValue: '1' }, { displayValue: '2' }], [{ displayValue: '3' }, { displayValue: '4' }]]
};
let queryRunner: TypeMoq.Mock<QueryRunner> = TypeMoq.Mock.ofType(QueryRunner);
queryRunner.setup(x => x.getQueryRows(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(resultSetSubset));
let dataResourceDataProvider = new DataResourceDataProvider(
0, // batchId
0, // id
queryRunner.object,
source,
resultSet,
cellModel.object,
notificationService,
undefined, // IClipboardService
undefined, // IConfigurationService
undefined, // ITextResourcePropertiesService
serializationService,
instantiationService.object
);
let spy = sinon.spy(cellModel.object, 'updateOutputData');
let expectedData = {
'application/vnd.dataresource+json': {
data: [{ 0: '1', 1: '2' }, { 0: '3', 1: '4' }],
schema: { fields: [{ name: 'col1' }, { name: 'col2' }] }
},
'text/html': ['<table>', '<tr><th>col1</th><th>col2</th></tr>', '<tr><td>1</td><td>2</td></tr>', '<tr><td>3</td><td>4</td></tr>', '</table>']
};
await dataResourceDataProvider.convertAllData(resultSet);
sinon.assert.calledOnce(spy);
sinon.assert.calledWithExactly(spy, 0, 0, expectedData);
});
});

View File

@@ -56,8 +56,6 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { IProductService } from 'vs/platform/product/common/productService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { INotificationService } from 'vs/platform/notification/common/notification';
class NotebookModelStub extends stubs.NotebookModelStub {
public contentChangedEmitter = new Emitter<NotebookContentChange>();
@@ -97,11 +95,10 @@ suite.skip('Test class NotebookEditor:', () => {
let queryTextEditor: QueryTextEditor;
let untitledNotebookInput: UntitledNotebookInput;
let notebookEditorStub: NotebookEditorStub;
let notificationService: TypeMoq.Mock<INotificationService>;
setup(async () => {
// setup services
({ instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub, notificationService } = setupServices({ instantiationService, workbenchThemeService }));
({ instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub } = setupServices({ instantiationService, workbenchThemeService }));
// Create notebookEditor
notebookEditor = createNotebookEditor(instantiationService, workbenchThemeService, notebookService);
});
@@ -122,7 +119,7 @@ suite.skip('Test class NotebookEditor:', () => {
const untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, untitledTextEditorService.create({ associatedResource: untitledUri }));
const untitledNotebookInput = new UntitledNotebookInput(
testTitle, untitledUri, untitledTextInput,
undefined, instantiationService, notebookService, extensionService, notificationService.object
undefined, instantiationService, notebookService, extensionService
);
const testNotebookEditor = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: notebookModel, notebookParams: <INotebookParams>{ notebookUri: untitledNotebookInput.notebookUri } });
notebookService.addNotebookEditor(testNotebookEditor);
@@ -670,7 +667,6 @@ function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, ins
const uninstallEvent = new Emitter<IExtensionIdentifier>();
const didUninstallEvent = new Emitter<DidUninstallExtensionEvent>();
const notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose);
const instantiationService = arg.instantiationService ?? <TestInstantiationService>workbenchInstantiationService();
const workbenchThemeService = arg.workbenchThemeService ?? instantiationService.createInstance(WorkbenchThemeService);
instantiationService.stub(IWorkbenchThemeService, workbenchThemeService);
@@ -707,7 +703,7 @@ function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, ins
const untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, untitledTextEditorService.create({ associatedResource: untitledUri }));
const untitledNotebookInput = new UntitledNotebookInput(
testTitle, untitledUri, untitledTextInput,
undefined, instantiationService, notebookService, extensionService, notificationService.object
undefined, instantiationService, notebookService, extensionService
);
const cellTextEditorGuid = generateUuid();
@@ -722,7 +718,7 @@ function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, ins
);
const notebookEditorStub = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: new NotebookModelStub(), notebookParams: <INotebookParams>{ notebookUri: untitledNotebookInput.notebookUri } });
notebookService.addNotebookEditor(notebookEditorStub);
return { instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub, notificationService };
return { instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub };
}
function createNotebookEditor(instantiationService: TestInstantiationService, workbenchThemeService: WorkbenchThemeService, notebookService: NotebookService) {

View File

@@ -20,7 +20,6 @@ import { IExtensionService, NullExtensionService } from 'vs/workbench/services/e
import { INotebookService, IProviderInfo } from 'sql/workbench/services/notebook/browser/notebookService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
suite('Notebook Input', function (): void {
const instantiationService = workbenchInstantiationService();
@@ -45,8 +44,6 @@ suite('Notebook Input', function (): void {
(instantiationService as TestInstantiationService).stub(INotebookService, mockNotebookService.object);
const mockNotificationService = TypeMoq.Mock.ofType(TestNotificationService);
let untitledTextInput: UntitledTextEditorInput;
let untitledNotebookInput: UntitledNotebookInput;
@@ -56,14 +53,14 @@ suite('Notebook Input', function (): void {
untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: untitledUri }));
untitledNotebookInput = new UntitledNotebookInput(
testTitle, untitledUri, untitledTextInput,
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object, mockNotificationService.object);
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object);
});
test('File Notebook Input', async function (): Promise<void> {
let fileUri = URI.from({ scheme: Schemas.file, path: 'TestPath' });
let fileNotebookInput = new FileNotebookInput(
testTitle, fileUri, undefined,
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object, mockNotificationService.object);
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object);
let inputId = fileNotebookInput.getTypeId();
assert.strictEqual(inputId, FileNotebookInput.ID);

View File

@@ -43,9 +43,6 @@ export class NotebookModelStub implements INotebookModel {
get sessionLoadFinished(): Promise<void> {
throw new Error('method not implemented.');
}
get gridDataConversionComplete(): Promise<any[]> {
throw new Error('method not implemented.');
}
get notebookManagers(): INotebookManager[] {
throw new Error('method not implemented.');
}