import assert from "node:assert/strict"; import { test } from "vitest"; import { buildDataGridCellDetail, buildDataGridColumnDetail, dataGridColumnDetailJson, dataGridColumnDetailTsv, buildDataGridRowDetail, dataGridRowDetailJson, dataGridRowDetailTsv, filterDataGridDetailFields, type DataGridCellDetail } from "../../apps/desktop/src/lib/dataGrid/dataGridDetail.ts"; import type { CellValue } from "../../apps/desktop/src/lib/dataGrid/cellValue.ts"; test("buildDataGridCellDetail returns null for an invalid column", () => { const detail = buildDataGridCellDetail({ rowIndex: 0, rowId: 10, row: ["Ada"], columns: ["name"], columnIndex: 2, displayValue: (value) => String(value), isEditable: false, }); assert.equal(detail, null); }); test("buildDataGridCellDetail preserves full value metadata", () => { const typeByColumn = new Map([["payload", "jsonb"]]); const commentByColumn = new Map([["payload", "raw event payload"]]); const detail = buildDataGridCellDetail({ rowIndex: 2, rowId: 42, row: [7, '{"ok":true,"items":[1,2]}'], columns: ["id", "payload"], columnIndex: 1, typeByColumn, commentByColumn, displayValue: (value) => `formatted:${String(value).slice(0, 8)}`, isEditable: true, }); assert.deepEqual(detail, { rowNumber: 3, rowId: 42, colIndex: 1, column: "payload", type: "jsonb", comment: "raw event payload", value: '{"ok":true,"items":[1,2]}', rawValue: '{"ok":true,"items":[1,2]}', rawValuePreview: '{"ok":true,"items":[1,2]}', displayValue: 'formatted:{"ok":tr', displayValuePreview: 'formatted:{"ok":tr', isValuePreviewTruncated: false, imagePreviewUrl: null, length: 25, formattedJson: '{\n "ok": true,\n "items": [\n 1,\n 2\n ]\n}', isEditable: true, }); }); test("buildDataGridCellDetail reports image preview URLs", () => { const detail = buildDataGridCellDetail({ rowIndex: 0, rowId: 1, row: ["https://example.com/avatar.png"], columns: ["avatar"], columnIndex: 0, displayValue: (value) => String(value), isEditable: false, }); assert.equal(detail?.imagePreviewUrl, "https://example.com/avatar.png"); }); test("buildDataGridCellDetail reports binary image preview URLs", () => { const detail = buildDataGridCellDetail({ rowIndex: 0, rowId: 1, row: ["0x89504e470d0a1a0a0000000d49484452"], columns: ["avatar"], columnIndex: 0, typeByColumn: new Map([["avatar", "longblob"]]), displayValue: (value) => String(value), isEditable: false, }); assert.match(detail?.imagePreviewUrl ?? "", /^data:image\/png;base64,/); }); test("buildDataGridCellDetail uses result column types for query results", () => { const detail = buildDataGridCellDetail({ rowIndex: 0, rowId: 1, row: [30001, "0x89504e470d0a1a0a0000000d49484452"], columns: ["id", "image_data"], columnIndex: 1, resultColumnTypes: ["bigint", "longblob"], displayValue: (value) => String(value), isEditable: false, }); assert.equal(detail?.type, "longblob"); assert.match(detail?.imagePreviewUrl ?? "", /^data:image\/png;base64,/); }); test("aggregate details defer binary image preview generation", () => { const rowDetail = buildDataGridRowDetail({ rowIndex: 0, rowId: 1, row: ["0x89504e470d0a1a0a0000000d49484452"], columns: ["avatar"], columnIndexes: [0], resultColumnTypes: ["longblob"], displayValue: (value) => String(value), }); const columnDetail = buildDataGridColumnDetail({ rows: [{ rowIndex: 0, rowId: 1, row: ["0x89504e470d0a1a0a0000000d49484452"] }], columns: ["avatar"], columnIndex: 0, resultColumnTypes: ["longblob"], displayValue: (value) => String(value), }); assert.equal(rowDetail.fields[0]?.type, "longblob"); assert.equal(rowDetail.fields[0]?.imagePreviewUrl, null); assert.equal(columnDetail?.fields[0]?.type, "longblob"); assert.equal(columnDetail?.fields[0]?.imagePreviewUrl, null); }); test("buildDataGridCellDetail limits rendered previews for huge text values", () => { const hugeJson = `{"body":"${"x".repeat(120_000)}"}`; const detail = buildDataGridCellDetail({ rowIndex: 0, rowId: 1, row: [hugeJson], columns: ["payload"], columnIndex: 0, typeByColumn: new Map([["payload", "nvarchar(max)"]]), displayValue: (value) => String(value), isEditable: false, }); assert.ok(detail); assert.equal(detail.rawValue, hugeJson); assert.equal(detail.rawValuePreview.length, 12_000); assert.equal(detail.displayValuePreview.length, 12_000); assert.equal(detail.isValuePreviewTruncated, true); assert.equal(detail.formattedJson, ""); }); test("buildDataGridRowDetail maps requested column indexes in order", () => { const row: CellValue[] = ["Ada", null, true]; const detail = buildDataGridRowDetail({ rowIndex: 4, rowId: 99, row, columns: ["name", "nickname", "active"], columnIndexes: [2, 0, 1], displayValue: (value) => (value === null ? "NULL" : String(value)), isEditableColumn: (columnIndex) => columnIndex !== 2, }); assert.equal(detail.rowNumber, 5); assert.equal(detail.rowId, 99); assert.deepEqual( detail.fields.map((field) => ({ column: field.column, colIndex: field.colIndex, rawValue: field.rawValue, isEditable: field.isEditable, })), [ { column: "active", colIndex: 2, rawValue: "true", isEditable: false }, { column: "name", colIndex: 0, rawValue: "Ada", isEditable: true }, { column: "nickname", colIndex: 1, rawValue: "NULL", isEditable: true }, ], ); }); test("buildDataGridRowDetail keeps duplicate columns as separate fields", () => { const detail = buildDataGridRowDetail({ rowIndex: 0, rowId: 1, row: ["first", "second"], columns: ["name", "name"], columnIndexes: [0, 1], displayValue: (value) => String(value), }); assert.deepEqual( detail.fields.map((field) => [field.colIndex, field.column, field.rawValue]), [ [0, "name", "first"], [1, "name", "second"], ], ); }); test("dataGridRowDetailJson and dataGridRowDetailTsv format copy payloads", () => { const detail = buildDataGridRowDetail({ rowIndex: 0, rowId: 1, row: [1, "Ada", null], columns: ["id", "name", "nickname"], columnIndexes: [0, 1, 2], displayValue: (value) => (value === null ? "NULL" : String(value)), }); assert.equal(dataGridRowDetailJson(detail), '{\n "id": 1,\n "name": "Ada",\n "nickname": null\n}'); assert.equal(dataGridRowDetailTsv(detail), "1\tAda\tNULL"); }); test("buildDataGridColumnDetail maps a whole column across rows", () => { const typeByColumn = new Map([["name", "varchar"]]); const commentByColumn = new Map([["name", "display name"]]); const detail = buildDataGridColumnDetail({ rows: [ { rowIndex: 0, rowId: 10, row: [1, "Ada"], isEditable: true }, { rowIndex: 3, rowId: 13, row: [4, null], isEditable: false }, ], columns: ["id", "name"], columnIndex: 1, typeByColumn, commentByColumn, displayValue: (value) => (value === null ? "NULL" : String(value)), }); assert.equal(detail?.column, "name"); assert.equal(detail?.type, "varchar"); assert.equal(detail?.comment, "display name"); assert.deepEqual( detail?.fields.map((field) => ({ rowNumber: field.rowNumber, rowId: field.rowId, rawValue: field.rawValue, isEditable: field.isEditable, })), [ { rowNumber: 1, rowId: 10, rawValue: "Ada", isEditable: true }, { rowNumber: 4, rowId: 13, rawValue: "NULL", isEditable: false }, ], ); }); test("buildDataGridColumnDetail returns null for an invalid column", () => { const detail = buildDataGridColumnDetail({ rows: [{ rowIndex: 0, rowId: 1, row: ["Ada"] }], columns: ["name"], columnIndex: 3, displayValue: (value) => String(value), }); assert.equal(detail, null); }); test("dataGridColumnDetailJson and dataGridColumnDetailTsv format copy payloads", () => { const detail = buildDataGridColumnDetail({ rows: [ { rowIndex: 0, rowId: 1, row: [1, "Ada"] }, { rowIndex: 1, rowId: 2, row: [2, null] }, ], columns: ["id", "name"], columnIndex: 1, displayValue: (value) => (value === null ? "NULL" : String(value)), }); assert.ok(detail); assert.equal(dataGridColumnDetailJson(detail), '[\n {\n "row": 1,\n "value": "Ada"\n },\n {\n "row": 2,\n "value": null\n }\n]'); assert.equal(dataGridColumnDetailTsv(detail), "Ada\nNULL"); }); const detailFields: DataGridCellDetail[] = [ { rowNumber: 1, column: "id", rawValuePreview: "7", displayValuePreview: "7" } as DataGridCellDetail, { rowNumber: 2, column: "Email", rawValuePreview: "ada@example.com", displayValuePreview: "ada@example.com" } as DataGridCellDetail, { rowNumber: 3, column: "status", rawValuePreview: "ACTIVE", displayValuePreview: "Active" } as DataGridCellDetail, ]; test("filterDataGridDetailFields returns all fields for an empty keyword", () => { const result = filterDataGridDetailFields(detailFields, " "); assert.deepEqual(result, detailFields); assert.notEqual(result, detailFields); }); test("filterDataGridDetailFields matches column names case-insensitively", () => { const result = filterDataGridDetailFields(detailFields, "EMAIL"); assert.deepEqual( result.map((field) => field.column), ["Email"], ); }); test("filterDataGridDetailFields matches raw value previews", () => { const result = filterDataGridDetailFields(detailFields, "example.com"); assert.deepEqual( result.map((field) => field.column), ["Email"], ); }); test("filterDataGridDetailFields matches row numbers", () => { const result = filterDataGridDetailFields(detailFields, "3"); assert.deepEqual( result.map((field) => field.rowNumber), [3], ); }); test("filterDataGridDetailFields returns an empty array when nothing matches", () => { const result = filterDataGridDetailFields(detailFields, "nope"); assert.deepEqual(result, []); }); test("filterDataGridDetailFields does not mutate the input array", () => { const original = [...detailFields]; const result = filterDataGridDetailFields(detailFields, "id"); assert.deepEqual(detailFields, original); assert.notEqual(result, detailFields); });