Files
t8y2--dbx/packages/app-tests/dataGridDetail.test.ts
T
2026-07-13 13:09:14 +08:00

320 lines
10 KiB
TypeScript
Vendored

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);
});