From d23ded502317c6cfbf19e89feba223a15e484348 Mon Sep 17 00:00:00 2001 From: Philip Carneiro Date: Wed, 25 Sep 2024 11:17:22 +0100 Subject: [PATCH 1/3] fix bug and tests --- src/services/resultsPanelProvider.ts | 14 +++++++------- test/suite/panels.test.ts | 7 ++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/services/resultsPanelProvider.ts b/src/services/resultsPanelProvider.ts index ab7d1058..4dc32103 100644 --- a/src/services/resultsPanelProvider.ts +++ b/src/services/resultsPanelProvider.ts @@ -176,7 +176,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { } } - convertToGrid(results: any, isInsights: boolean): string { + convertToGrid(results: any, isInsights: boolean): any { const queryResult = isInsights ? results.rows : results; const columnDefs = this.generateCoumnDefs(results, isInsights); @@ -199,7 +199,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { if (rowData.length > 0) { ext.resultPanelCSV = this.convertToCsv(rowData).join("\n"); } - return JSON.stringify({ + return { defaultColDef: { sortable: true, resizable: true, @@ -217,7 +217,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { suppressContextMenu: true, suppressDragLeaveHidesColumns: true, tooltipShowDelay: 200, - }); + }; } isVisible(): boolean { @@ -266,7 +266,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { ]); const nonce = getNonce(); let result = ""; - let gridOptionsString = ""; + let gridOptions = undefined; let isGrid = false; if (typeof queryResult === "string" || typeof queryResult === "number") { @@ -278,11 +278,11 @@ export class KdbResultsViewProvider implements WebviewViewProvider { : "

No results to show

"; } else if (queryResult) { isGrid = true; - gridOptionsString = this.convertToGrid(queryResult, !!isInsights); + gridOptions = this.convertToGrid(queryResult, !!isInsights); } result = - gridOptionsString === "" + gridOptions === undefined ? result !== "" ? result : "

No results to show

" @@ -317,7 +317,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { document.addEventListener('DOMContentLoaded', () => { if(${isGrid}){ const gridDiv = document.getElementById('grid'); - const obj = JSON.parse('${gridOptionsString}'); + const obj = ${JSON.stringify(gridOptions)}; const gridApi = agGrid.createGrid(gridDiv, obj); document.getElementById("results").scrollIntoView(); } diff --git a/test/suite/panels.test.ts b/test/suite/panels.test.ts index 71570fc8..fe769d42 100644 --- a/test/suite/panels.test.ts +++ b/test/suite/panels.test.ts @@ -227,7 +227,7 @@ describe("WebPanels", () => { stub.get(() => insightsConn); const output = resultsPanel.convertToGrid(results, true); - assert.equal(output, expectedOutput); + assert.equal(JSON.stringify(output), expectedOutput); // Restore the stub stub.restore(); @@ -277,7 +277,7 @@ describe("WebPanels", () => { stub.get(() => insightsConn); const output = resultsPanel.convertToGrid(results, true); - assert.equal(output, expectedOutput); + assert.equal(JSON.stringify(output), expectedOutput); // Restore the stub stub.restore(); @@ -416,11 +416,12 @@ describe("WebPanels", () => { { id: 1, test: "test1" }, { id: 2, test: "test2" }, ]; - const expectedOutput = `"rowData":[{"id":1,"test":"test1"},{"id":2,"test":"test2"}],"columnDefs":[{"field":"id","headerName":"id"},{"field":"test","headerName":"test"}]`; + const expectedOutput = `agGrid.createGrid(gridDiv, obj)`; const stub = sinon .stub(resultsPanel, "convertToGrid") .returns(expectedOutput); const actualOutput = resultsPanel["_getWebviewContent"](input); + console.log(actualOutput); assert.strictEqual(typeof actualOutput, "string"); assert.ok(actualOutput.includes(expectedOutput)); stub.restore(); From 7a09ec9cde0f8340059be6a932e93c81051f06da Mon Sep 17 00:00:00 2001 From: Philip Carneiro Date: Mon, 30 Sep 2024 12:52:40 +0100 Subject: [PATCH 2/3] refactory the results panel --- src/commands/serverCommand.ts | 2 +- src/extension.ts | 8 +- src/services/resultsPanelProvider.ts | 195 ++++++++++++++++----------- src/webview/styles/resultsPanel.css | 3 +- test/suite/commands.test.ts | 1 - test/suite/panels.test.ts | 32 +---- 6 files changed, 122 insertions(+), 119 deletions(-) diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 9c030481..571d563c 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -1088,7 +1088,7 @@ export function writeQueryResultsToView( duration?: string, isFromConnTree?: boolean, ): void { - commands.executeCommand("kdb.resultsPanel.update", result, isInsights, type); + commands.executeCommand("kdb.resultsPanel.update", result, isInsights); if (!checkIfIsDatasource(type)) { addQueryHistory( query, diff --git a/src/extension.ts b/src/extension.ts index 973beb1c..05c51176 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -204,12 +204,8 @@ export async function activate(context: ExtensionContext) { ), commands.registerCommand( "kdb.resultsPanel.update", - (results: string, isInsights: boolean, dataSourceType?: string) => { - ext.resultsViewProvider.updateResults( - results, - isInsights, - dataSourceType, - ); + (results: string, isInsights: boolean) => { + ext.resultsViewProvider.updateResults(results, isInsights); }, ), commands.registerCommand("kdb.resultsPanel.clear", () => { diff --git a/src/services/resultsPanelProvider.ts b/src/services/resultsPanelProvider.ts index 4dc32103..eed8071c 100644 --- a/src/services/resultsPanelProvider.ts +++ b/src/services/resultsPanelProvider.ts @@ -23,11 +23,13 @@ import { ext } from "../extensionVariables"; import * as utils from "../utils/execution"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; +import { kdbOutputLog } from "../utils/core"; export class KdbResultsViewProvider implements WebviewViewProvider { public static readonly viewType = "kdb-results"; - private _view?: WebviewView; + public isInsights = false; public _colorTheme: any; + private _view?: WebviewView; private _results: string | string[] = ""; constructor(private readonly _extensionUri: Uri) { @@ -48,10 +50,11 @@ export class KdbResultsViewProvider implements WebviewViewProvider { localResourceRoots: [Uri.joinPath(this._extensionUri, "out")], }; - webviewView.webview.html = this._getWebviewContent(""); + webviewView.webview.html = this._getWebviewContent(); + this.updateWebView(""); webviewView.webview.onDidReceiveMessage((data) => { - webviewView.webview.html = this._getWebviewContent(data); + this.updateWebView(data); }); webviewView.onDidChangeVisibility(() => { ext.isResultsTabVisible = webviewView.visible; @@ -62,19 +65,11 @@ export class KdbResultsViewProvider implements WebviewViewProvider { }); } - public updateResults( - queryResults: any, - isInsights?: boolean, - dataSourceType?: string, - ) { + public updateResults(queryResults: any, isInsights?: boolean) { if (this._view) { this._view.show?.(true); - this._view.webview.postMessage(queryResults); - this._view.webview.html = this._getWebviewContent( - queryResults, - isInsights, - dataSourceType, - ); + this.isInsights = !!isInsights; + this.updateWebView(queryResults); } } @@ -251,13 +246,39 @@ export class KdbResultsViewProvider implements WebviewViewProvider { : ""; } - private _getWebviewContent( - queryResult: any, - isInsights?: boolean, - _dataSourceType?: string, - ) { + public updateWebView(queryResult: any) { ext.resultPanelCSV = ""; this._results = queryResult; + let result = ""; + let gridOptions = undefined; + if (!this._view) { + kdbOutputLog("[Results Tab] No view to update", "ERROR"); + return; + } + if (typeof queryResult === "string" || typeof queryResult === "number") { + result = + queryResult !== "" + ? `

${queryResult + .toString() + .replace(/\n/g, "
")}

` + : "

No results to show

"; + } else if (queryResult) { + gridOptions = this.convertToGrid(queryResult, this.isInsights); + } + if (gridOptions) { + this._view.webview.postMessage({ + command: "setGridOptions", + gridOptions: gridOptions, + }); + } else { + this._view.webview.postMessage({ + command: "setResultsContent", + results: result, + }); + } + } + + private _getWebviewContent() { const agGridTheme = this.defineAgGridTheme(); if (this._view) { const webviewUri = getUri(this._view.webview, this._extensionUri, [ @@ -265,70 +286,82 @@ export class KdbResultsViewProvider implements WebviewViewProvider { "webview.js", ]); const nonce = getNonce(); - let result = ""; - let gridOptions = undefined; + return /*html*/ ` + + + + + + + + + + + Q Results + + + +
+
+
+ +
+ - - -
-
- ${result} -
-
- -
- - - - `; + function restoreColumnWidths(columnWidths) { + if (!gridApi || !columnWidths) return; + columnWidths.forEach(colWidth => { + gridApi.getColumnState().forEach(colState => { + if (colState.colId === colWidth.colId) { + colState.width = colWidth.width; + } + }); + }); + gridApi.setColumnState(gridApi.getColumnState()); + } + + window.addEventListener('message', event => { + const message = event.data; + if (message.command === 'setGridOptions') { + const columnWidths = saveColumnWidths(); + const gridOptions = message.gridOptions; + const gridDiv = document.getElementById('grid'); + const resultsDiv = document.querySelector('#results .content-wrapper'); + resultsDiv.innerHTML = ''; + gridDiv.innerHTML = ''; + gridApi = agGrid.createGrid(gridDiv, gridOptions); + restoreColumnWidths(columnWidths); + document.getElementById("results").scrollIntoView(); + } else if (message.command === 'setResultsContent') { + const resultsContent = message.results; + const resultsDiv = document.querySelector('#results .content-wrapper'); + const gridDiv = document.getElementById('grid'); + gridDiv.innerHTML = ''; + resultsDiv.innerHTML = ''; + resultsDiv.innerHTML = resultsContent; + } + }); + document.addEventListener('contextmenu', (e) => { + e.stopImmediatePropagation(); + }, true); + + + + `; } else { return ""; } diff --git a/src/webview/styles/resultsPanel.css b/src/webview/styles/resultsPanel.css index f4c9a235..c181e93a 100644 --- a/src/webview/styles/resultsPanel.css +++ b/src/webview/styles/resultsPanel.css @@ -1,7 +1,8 @@ html, body { height: 86vh; - width: 100%; + width: 99%; + padding: 0; box-sizing: border-box; -webkit-overflow-scrolling: touch; } diff --git a/test/suite/commands.test.ts b/test/suite/commands.test.ts index 85499472..6d997fa6 100644 --- a/test/suite/commands.test.ts +++ b/test/suite/commands.test.ts @@ -1307,7 +1307,6 @@ describe("serverCommand", () => { "kdb.resultsPanel.update", result, false, - "WORKBOOK", ); executeCommandStub.restore(); diff --git a/test/suite/panels.test.ts b/test/suite/panels.test.ts index fe769d42..7af06678 100644 --- a/test/suite/panels.test.ts +++ b/test/suite/panels.test.ts @@ -411,35 +411,9 @@ describe("WebPanels", () => { resultsPanel["_view"] = view; }); - it("returns a table", () => { - const input = [ - { id: 1, test: "test1" }, - { id: 2, test: "test2" }, - ]; - const expectedOutput = `agGrid.createGrid(gridDiv, obj)`; - const stub = sinon - .stub(resultsPanel, "convertToGrid") - .returns(expectedOutput); - const actualOutput = resultsPanel["_getWebviewContent"](input); - console.log(actualOutput); - assert.strictEqual(typeof actualOutput, "string"); - assert.ok(actualOutput.includes(expectedOutput)); - stub.restore(); - }); - - it("returns string results", () => { - const input = "Test"; - const expectedOutput = `

Test

`; - const actualOutput = resultsPanel["_getWebviewContent"](input); - assert.strictEqual(typeof actualOutput, "string"); - assert.ok(actualOutput.includes(expectedOutput)); - }); - - it("returns no results", () => { - const input = ""; - const expectedOutput = `

No results to show

`; - const actualOutput = resultsPanel["_getWebviewContent"](input); - assert.strictEqual(typeof actualOutput, "string"); + it("should render the results tab", () => { + const expectedOutput = ` id="results" class="results-view-container"`; + const actualOutput = resultsPanel["_getWebviewContent"](); assert.ok(actualOutput.includes(expectedOutput)); }); }); From cd9ad5c3fd829b3ea59b87d2c2c6c8e52572c07c Mon Sep 17 00:00:00 2001 From: Philip Carneiro Date: Tue, 1 Oct 2024 09:37:11 +0100 Subject: [PATCH 3/3] add tests --- test/suite/commands.test.ts | 1 - test/suite/panels.test.ts | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/test/suite/commands.test.ts b/test/suite/commands.test.ts index 6d997fa6..46471e04 100644 --- a/test/suite/commands.test.ts +++ b/test/suite/commands.test.ts @@ -15,7 +15,6 @@ import assert from "assert"; import mock from "mock-fs"; import * as sinon from "sinon"; import * as vscode from "vscode"; -import * as fs from "fs"; import * as dataSourceCommand from "../../src/commands/dataSourceCommand"; import * as installTools from "../../src/commands/installTools"; import * as serverCommand from "../../src/commands/serverCommand"; diff --git a/test/suite/panels.test.ts b/test/suite/panels.test.ts index 7af06678..fe9d4e20 100644 --- a/test/suite/panels.test.ts +++ b/test/suite/panels.test.ts @@ -19,6 +19,7 @@ import { createDefaultDataSourceFile } from "../../src/models/dataSource"; import { DataSourcesPanel } from "../../src/panels/datasource"; import { KdbResultsViewProvider } from "../../src/services/resultsPanelProvider"; import * as utils from "../../src/utils/execution"; +import * as coreUtils from "../../src/utils/core"; import { InsightsNode, KdbNode } from "../../src/services/kdbTreeProvider"; import { TreeItemCollapsibleState } from "vscode"; import { NewConnectionPannel } from "../../src/panels/newConnection"; @@ -383,6 +384,94 @@ describe("WebPanels", () => { }); }); + describe("updateWebView", () => { + const uriTest: vscode.Uri = vscode.Uri.parse("test"); + + let resultsPanel: KdbResultsViewProvider; + let postMessageStub: sinon.SinonStub; + let kdbOutputLogStub: sinon.SinonStub; + let convertToGridStub: sinon.SinonStub; + + beforeEach(() => { + resultsPanel = new KdbResultsViewProvider(uriTest); + resultsPanel["_view"] = { + webview: { + postMessage: sinon.stub(), + }, + } as any; + postMessageStub = resultsPanel["_view"].webview + .postMessage as sinon.SinonStub; + kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + convertToGridStub = sinon.stub(resultsPanel, "convertToGrid"); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should log an error if there is no view to update", () => { + resultsPanel["_view"] = undefined; + resultsPanel.updateWebView("test"); + sinon.assert.calledWith( + kdbOutputLogStub, + "[Results Tab] No view to update", + "ERROR", + ); + }); + + it("should handle string queryResult", () => { + const queryResult = "test string"; + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith(postMessageStub, { + command: "setResultsContent", + results: `

test string

`, + }); + }); + + it("should handle number queryResult", () => { + const queryResult = 123; + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith(postMessageStub, { + command: "setResultsContent", + results: `

123

`, + }); + }); + + it("should handle empty string queryResult", () => { + const queryResult = ""; + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith(postMessageStub, { + command: "setResultsContent", + results: "

No results to show

", + }); + }); + + it("should handle object queryResult and call convertToGrid", () => { + const queryResult = { data: "test" }; + const gridOptions = { columnDefs: [], rowData: [] }; + convertToGridStub.returns(gridOptions); + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith( + convertToGridStub, + queryResult, + resultsPanel.isInsights, + ); + sinon.assert.calledWith(postMessageStub, { + command: "setGridOptions", + gridOptions: gridOptions, + }); + }); + + it("should handle null queryResult", () => { + const queryResult = null; + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith(postMessageStub, { + command: "setResultsContent", + results: "", + }); + }); + }); + describe("_getWebviewContent", () => { const uriTest: vscode.Uri = vscode.Uri.parse("test");