/* eslint-disable sonarjs/no-duplicate-string */
import { GridCellKind, type GridCell, BooleanIndeterminate } from "../src/index.js";
import { decodeHTML, getCopyBufferContents, type CellBuffer } from "../src/data-editor/copy-paste.js";
import { expect, describe, test } from "vitest";
function makeCellBuffer(
rawValue: string | string[],
formatted = rawValue,
format: CellBuffer["format"] = "string"
): CellBuffer {
return {
rawValue,
formatted,
format,
} as CellBuffer;
}
describe("copy-paste", () => {
test("decode html", () => {
const html = `
`;
const decoded = decodeHTML(html);
expect(decoded).toEqual([
[makeCellBuffer("1"), makeCellBuffer("2")],
[makeCellBuffer("3"), makeCellBuffer("4")],
]);
});
test("decode html line breaks", () => {
const html = `
`;
const decoded = decodeHTML(html);
expect(decoded).toEqual([
[makeCellBuffer("1\n1.1"), makeCellBuffer("2\n2.1")],
[makeCellBuffer("3"), makeCellBuffer("4")],
]);
});
test("Simple text cell", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Text,
data: "Hello",
allowOverlay: true,
displayData: "Display Hello",
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe("Display Hello");
expect(result.textHtml).toContain('Display Hello | ');
});
test("Simple text cell with multiple spaces", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Text,
data: "Hello",
allowOverlay: true,
displayData: "Display Hello",
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe("Display Hello");
expect(result.textHtml).toContain(
'Display Hello | '
);
});
test("Simple text cell with special chars", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Text,
data: '"Hello"',
allowOverlay: true,
displayData: 'Display "Hello"',
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe('"Display ""Hello"""');
expect(result.textHtml).toContain(
'Display "Hello" | '
);
});
test("Bubble cell encoding", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Bubble,
data: ["Bubble1", "Bubble2"],
allowOverlay: true,
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe("Bubble1,Bubble2");
expect(result.textHtml).toContain(
'- Bubble1
- Bubble2
| '
);
});
test("format empty bubble cell", () => {
expect(
getCopyBufferContents(
[
[
{
kind: GridCellKind.Bubble,
allowOverlay: true,
data: [],
},
],
],
[0]
).textPlain
).toEqual("");
});
test("format url cell", () => {
expect(
getCopyBufferContents(
[
[
{
kind: GridCellKind.Uri,
allowOverlay: true,
data: "https://www.google.com",
},
],
],
[0]
).textPlain
).toEqual("https://www.google.com");
});
test("format url cell with display value", () => {
expect(
getCopyBufferContents(
[
[
{
kind: GridCellKind.Uri,
allowOverlay: true,
data: "https://www.google.com",
displayData: "Google",
},
],
],
[0]
).textPlain
).toEqual("https://www.google.com");
});
test("format empty bubble cell with comma", () => {
expect(
getCopyBufferContents(
[
[
{
kind: GridCellKind.Bubble,
allowOverlay: true,
data: ["foo, bar", "baz"],
},
],
],
[0]
).textPlain
).toEqual('"foo, bar",baz');
});
test("format respects copyData", () => {
expect(
getCopyBufferContents(
[
[
{
kind: GridCellKind.Bubble,
allowOverlay: true,
data: ["foo, bar", "baz"],
copyData: "override",
},
],
],
[0]
).textPlain
).toEqual("override");
});
test("Custom cell type", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Custom,
copyData: "CustomData",
allowOverlay: true,
data: "data",
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe("CustomData");
expect(result.textHtml).toContain('CustomData | ');
});
test.each([
[true, "TRUE"],
[false, "FALSE"],
[BooleanIndeterminate, "INDETERMINATE"],
[null, ""],
])("Boolean cell type %p", (data, expectedFormatted) => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Boolean,
data,
allowOverlay: false,
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe(expectedFormatted);
expect(result.textHtml).toContain(
`${expectedFormatted} | `
);
});
test("Image cell type", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Image,
data: ["image1.jpg", "image2.jpg"],
allowOverlay: true,
readonly: false,
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe("image1.jpg,image2.jpg");
expect(result.textHtml).toContain(
'- image1.jpg
- image2.jpg
| '
);
});
test.each([
[GridCellKind.Markdown, "markdownContent", "markdownContent", "string"],
[GridCellKind.RowID, "row123", "row123", "string"],
[GridCellKind.Number, 1234, "1234", "number"],
[GridCellKind.Loading, undefined, "#LOADING", "string"],
[GridCellKind.Protected, undefined, "************", "string"],
])("Special cell type %p", (kind, data, expectedFormatted, format) => {
const cells: GridCell[][] = [
[
{
kind,
data,
displayData: data?.toString(),
allowOverlay: true,
} as GridCell,
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe(expectedFormatted);
expect(result.textHtml).toContain(
`${expectedFormatted} | `
);
});
test("decode html with URLs", () => {
const html = `
`;
const decoded = decodeHTML(html);
expect(decoded).toEqual([
[
makeCellBuffer("https://example.com", "Example Link", "url"),
makeCellBuffer(["item1", "item2"], ["Item1", "Item2"], "string-array"),
],
]);
});
});
test("Drilldown cell conversion", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Drilldown,
data: [{ text: "Drill1" }, { text: "Drill2" }],
allowOverlay: true,
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe("Drill1,Drill2");
expect(result.textHtml).toContain(
'- Drill1
- Drill2
| '
);
});
test("decode non-table HTML", () => {
const html = `Non-table content
`;
const decoded = decodeHTML(html);
expect(decoded).toBeUndefined();
});
test("handle cell span", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Text,
data: "Hello",
displayData: "Display Hello",
span: [0, 1],
allowOverlay: true,
},
],
];
const columnIndexes = [1];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe(""); // It should be empty since span doesn't match
});
test("escape string with tab character", () => {
const cells: GridCell[][] = [
[
{
kind: GridCellKind.Text,
data: "Hello\tWorld",
displayData: "Hello\tWorld",
allowOverlay: true,
},
],
];
const columnIndexes = [0];
const result = getCopyBufferContents(cells, columnIndexes);
expect(result.textPlain).toBe('"Hello\tWorld"');
});
test("decode ordered list", () => {
const html = `
`;
const decoded = decodeHTML(html);
expect(decoded).toEqual([
[
{
rawValue: ["test1", "test2"],
formatted: ["Test1", "Test2"],
format: "string-array",
},
],
]);
});
test("decode apple numbers", () => {
const html = `
|
Test
|
This
|
|
Out
|
With a
newline and such
|
`;
const decoded = decodeHTML(html);
expect(decoded).toEqual([
[makeCellBuffer("Test"), makeCellBuffer("This")],
[makeCellBuffer("Out"), makeCellBuffer("With a\nnewline and such")],
]);
});
test("decode html attributes", () => {
const html = `
`;
const decoded = decodeHTML(html);
expect(decoded).toEqual([
[
{
rawValue: '"Hello"',
formatted: "Hello",
format: "string",
},
],
]);
});