@simplysm/excel 13.0.100 → 14.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -78
- package/dist/excel-cell.d.ts +28 -28
- package/dist/excel-cell.d.ts.map +1 -1
- package/dist/excel-cell.js +273 -264
- package/dist/excel-cell.js.map +1 -6
- package/dist/excel-col.d.ts +4 -4
- package/dist/excel-col.d.ts.map +1 -1
- package/dist/excel-col.js +33 -35
- package/dist/excel-col.js.map +1 -6
- package/dist/excel-row.d.ts +3 -3
- package/dist/excel-row.d.ts.map +1 -1
- package/dist/excel-row.js +28 -30
- package/dist/excel-row.js.map +1 -6
- package/dist/excel-workbook.d.ts +23 -23
- package/dist/excel-workbook.d.ts.map +1 -1
- package/dist/excel-workbook.js +151 -125
- package/dist/excel-workbook.js.map +1 -6
- package/dist/excel-worksheet.d.ts +32 -32
- package/dist/excel-worksheet.d.ts.map +1 -1
- package/dist/excel-worksheet.js +281 -253
- package/dist/excel-worksheet.js.map +1 -6
- package/dist/excel-wrapper.d.ts +7 -7
- package/dist/excel-wrapper.d.ts.map +1 -1
- package/dist/excel-wrapper.js +190 -226
- package/dist/excel-wrapper.js.map +1 -6
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -6
- package/dist/types.d.ts +13 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -6
- package/dist/utils/excel-utils.d.ts +23 -23
- package/dist/utils/excel-utils.d.ts.map +1 -1
- package/dist/utils/excel-utils.js +183 -151
- package/dist/utils/excel-utils.js.map +1 -6
- package/dist/utils/zip-cache.d.ts +6 -6
- package/dist/utils/zip-cache.js +78 -60
- package/dist/utils/zip-cache.js.map +1 -6
- package/dist/xml/excel-xml-content-type.d.ts +2 -2
- package/dist/xml/excel-xml-content-type.js +55 -53
- package/dist/xml/excel-xml-content-type.js.map +1 -6
- package/dist/xml/excel-xml-drawing.d.ts +2 -2
- package/dist/xml/excel-xml-drawing.js +74 -73
- package/dist/xml/excel-xml-drawing.js.map +1 -6
- package/dist/xml/excel-xml-relationship.d.ts +2 -2
- package/dist/xml/excel-xml-relationship.js +67 -67
- package/dist/xml/excel-xml-relationship.js.map +1 -6
- package/dist/xml/excel-xml-shared-string.d.ts +2 -2
- package/dist/xml/excel-xml-shared-string.js +57 -55
- package/dist/xml/excel-xml-shared-string.js.map +1 -6
- package/dist/xml/excel-xml-style.d.ts +2 -2
- package/dist/xml/excel-xml-style.js +311 -295
- package/dist/xml/excel-xml-style.js.map +1 -6
- package/dist/xml/excel-xml-unknown.d.ts +2 -2
- package/dist/xml/excel-xml-unknown.js +11 -10
- package/dist/xml/excel-xml-unknown.js.map +1 -6
- package/dist/xml/excel-xml-workbook.d.ts +3 -2
- package/dist/xml/excel-xml-workbook.d.ts.map +1 -1
- package/dist/xml/excel-xml-workbook.js +95 -90
- package/dist/xml/excel-xml-workbook.js.map +1 -6
- package/dist/xml/excel-xml-worksheet.d.ts +6 -6
- package/dist/xml/excel-xml-worksheet.js +450 -393
- package/dist/xml/excel-xml-worksheet.js.map +1 -6
- package/docs/core.md +147 -0
- package/docs/types.md +201 -212
- package/docs/wrapper.md +32 -62
- package/package.json +8 -6
- package/src/excel-cell.ts +36 -36
- package/src/excel-col.ts +4 -4
- package/src/excel-row.ts +3 -3
- package/src/excel-workbook.ts +38 -38
- package/src/excel-worksheet.ts +69 -51
- package/src/excel-wrapper.ts +55 -50
- package/src/index.ts +3 -3
- package/src/types.ts +17 -17
- package/src/utils/excel-utils.ts +47 -41
- package/src/utils/zip-cache.ts +6 -6
- package/src/xml/excel-xml-content-type.ts +3 -3
- package/src/xml/excel-xml-drawing.ts +2 -2
- package/src/xml/excel-xml-relationship.ts +3 -3
- package/src/xml/excel-xml-shared-string.ts +2 -2
- package/src/xml/excel-xml-style.ts +13 -13
- package/src/xml/excel-xml-unknown.ts +2 -2
- package/src/xml/excel-xml-workbook.ts +14 -6
- package/src/xml/excel-xml-worksheet.ts +43 -43
- package/docs/core-classes.md +0 -541
- package/docs/utilities.md +0 -128
- package/tests/excel-cell.spec.ts +0 -393
- package/tests/excel-col.spec.ts +0 -81
- package/tests/excel-row.spec.ts +0 -61
- package/tests/excel-workbook.spec.ts +0 -205
- package/tests/excel-worksheet.spec.ts +0 -469
- package/tests/excel-wrapper.spec.ts +0 -273
- package/tests/fixtures/logo.png +0 -0
- package/tests/fixtures//354/264/210/352/270/260/355/231/224.xlsx +0 -0
- package/tests/image-insert.spec.ts +0 -190
- package/tests/utils/excel-utils.spec.ts +0 -198
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
2
|
-
import { ExcelWorkbook } from "../src/excel-workbook";
|
|
3
|
-
import type { Bytes } from "@simplysm/core-common";
|
|
4
|
-
|
|
5
|
-
describe("ExcelWorkbook", () => {
|
|
6
|
-
describe("Creating empty workbook", () => {
|
|
7
|
-
it("Can create a worksheet", async () => {
|
|
8
|
-
const wb = new ExcelWorkbook();
|
|
9
|
-
const ws = await wb.addWorksheet("TestSheet");
|
|
10
|
-
|
|
11
|
-
expect(ws).toBeDefined();
|
|
12
|
-
const name = await ws.getName();
|
|
13
|
-
expect(name).toBe("TestSheet");
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("Can create multiple worksheets", async () => {
|
|
17
|
-
const wb = new ExcelWorkbook();
|
|
18
|
-
await wb.addWorksheet("Sheet1");
|
|
19
|
-
await wb.addWorksheet("Sheet2");
|
|
20
|
-
await wb.addWorksheet("Sheet3");
|
|
21
|
-
|
|
22
|
-
const names = await wb.getWorksheetNames();
|
|
23
|
-
expect(names).toEqual(["Sheet1", "Sheet2", "Sheet3"]);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe("Accessing worksheets", () => {
|
|
28
|
-
it("Can get worksheet by index", async () => {
|
|
29
|
-
const wb = new ExcelWorkbook();
|
|
30
|
-
await wb.addWorksheet("First");
|
|
31
|
-
await wb.addWorksheet("Second");
|
|
32
|
-
|
|
33
|
-
const ws = await wb.getWorksheet(1);
|
|
34
|
-
const name = await ws.getName();
|
|
35
|
-
expect(name).toBe("Second");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("Can get worksheet by name", async () => {
|
|
39
|
-
const wb = new ExcelWorkbook();
|
|
40
|
-
await wb.addWorksheet("MySheet");
|
|
41
|
-
|
|
42
|
-
const ws = await wb.getWorksheet("MySheet");
|
|
43
|
-
const name = await ws.getName();
|
|
44
|
-
expect(name).toBe("MySheet");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("Error when accessing non-existent sheet", async () => {
|
|
48
|
-
const wb = new ExcelWorkbook();
|
|
49
|
-
await wb.addWorksheet("Sheet1");
|
|
50
|
-
|
|
51
|
-
await expect(wb.getWorksheet("NotExist")).rejects.toThrow();
|
|
52
|
-
await expect(wb.getWorksheet(10)).rejects.toThrow();
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe("Bytes/Blob export", () => {
|
|
57
|
-
it("Can export as Bytes", async () => {
|
|
58
|
-
const wb = new ExcelWorkbook();
|
|
59
|
-
const ws = await wb.addWorksheet("Test");
|
|
60
|
-
await ws.cell(0, 0).setValue("Hello");
|
|
61
|
-
|
|
62
|
-
const bytes: Bytes = await wb.toBytes();
|
|
63
|
-
expect(bytes).toBeInstanceOf(Uint8Array);
|
|
64
|
-
expect(bytes.length).toBeGreaterThan(0);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("Can export as Blob", async () => {
|
|
68
|
-
const wb = new ExcelWorkbook();
|
|
69
|
-
const ws = await wb.addWorksheet("Test");
|
|
70
|
-
await ws.cell(0, 0).setValue("Hello");
|
|
71
|
-
|
|
72
|
-
const blob = await wb.toBlob();
|
|
73
|
-
expect(blob).toBeInstanceOf(Blob);
|
|
74
|
-
expect(blob.type).toBe("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
75
|
-
expect(blob.size).toBeGreaterThan(0);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe("Workbook read/write round-trip", () => {
|
|
80
|
-
it("Can create workbook from Blob", async () => {
|
|
81
|
-
// First create workbook with Bytes
|
|
82
|
-
const wb1 = new ExcelWorkbook();
|
|
83
|
-
const ws1 = await wb1.addWorksheet("Test");
|
|
84
|
-
await ws1.cell(0, 0).setValue("BlobTest");
|
|
85
|
-
const bytes = await wb1.toBytes();
|
|
86
|
-
await wb1.close();
|
|
87
|
-
|
|
88
|
-
// Convert to Blob
|
|
89
|
-
const blob = new Blob([new Uint8Array(bytes)], {
|
|
90
|
-
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// Read workbook from Blob
|
|
94
|
-
const wb2 = new ExcelWorkbook(blob);
|
|
95
|
-
const ws2 = await wb2.getWorksheet(0);
|
|
96
|
-
const val = await ws2.cell(0, 0).getValue();
|
|
97
|
-
|
|
98
|
-
expect(val).toBe("BlobTest");
|
|
99
|
-
await wb2.close();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("Can save created workbook as Bytes and read again", async () => {
|
|
103
|
-
// Create
|
|
104
|
-
const wb1 = new ExcelWorkbook();
|
|
105
|
-
const ws1 = await wb1.addWorksheet("RoundTrip");
|
|
106
|
-
await ws1.cell(0, 0).setValue("TestValue");
|
|
107
|
-
await ws1.cell(0, 1).setValue(12345);
|
|
108
|
-
|
|
109
|
-
// Save
|
|
110
|
-
const bytes = await wb1.toBytes();
|
|
111
|
-
await wb1.close();
|
|
112
|
-
|
|
113
|
-
// Read again
|
|
114
|
-
const wb2 = new ExcelWorkbook(bytes);
|
|
115
|
-
const names = await wb2.getWorksheetNames();
|
|
116
|
-
expect(names).toContain("RoundTrip");
|
|
117
|
-
|
|
118
|
-
const ws2 = await wb2.getWorksheet("RoundTrip");
|
|
119
|
-
const val1 = await ws2.cell(0, 0).getValue();
|
|
120
|
-
const val2 = await ws2.cell(0, 1).getValue();
|
|
121
|
-
|
|
122
|
-
expect(val1).toBe("TestValue");
|
|
123
|
-
expect(val2).toBe(12345);
|
|
124
|
-
|
|
125
|
-
await wb2.close();
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe("Error after resource cleanup", () => {
|
|
130
|
-
it("Error when calling getWorksheetNames() after close()", async () => {
|
|
131
|
-
const wb = new ExcelWorkbook();
|
|
132
|
-
await wb.addWorksheet("Test");
|
|
133
|
-
await wb.close();
|
|
134
|
-
|
|
135
|
-
await expect(wb.getWorksheetNames()).rejects.toThrow();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("Error when calling getWorksheet() after close()", async () => {
|
|
139
|
-
const wb = new ExcelWorkbook();
|
|
140
|
-
await wb.addWorksheet("Test");
|
|
141
|
-
await wb.close();
|
|
142
|
-
|
|
143
|
-
await expect(wb.getWorksheet(0)).rejects.toThrow();
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe("Reading real xlsx file", () => {
|
|
148
|
-
let wb: ExcelWorkbook;
|
|
149
|
-
|
|
150
|
-
beforeAll(async () => {
|
|
151
|
-
const url = new URL("./fixtures/초기화.xlsx", import.meta.url);
|
|
152
|
-
|
|
153
|
-
if (!("window" in globalThis)) {
|
|
154
|
-
const fs = await import("node:fs" as string);
|
|
155
|
-
const { fileURLToPath } = await import("node:url" as string);
|
|
156
|
-
const buffer = fs.readFileSync(fileURLToPath(url));
|
|
157
|
-
wb = new ExcelWorkbook(new Uint8Array(buffer));
|
|
158
|
-
} else {
|
|
159
|
-
const response = await fetch(url);
|
|
160
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
161
|
-
wb = new ExcelWorkbook(new Uint8Array(arrayBuffer));
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
afterAll(async () => {
|
|
166
|
-
await wb.close();
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("Can read worksheet names", async () => {
|
|
170
|
-
const names = await wb.getWorksheetNames();
|
|
171
|
-
expect(names).toEqual(["권한그룹", "권한그룹권한", "직원"]);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("Can read permission group sheet data", async () => {
|
|
175
|
-
const ws = await wb.getWorksheet("권한그룹");
|
|
176
|
-
const data = await ws.getDataTable();
|
|
177
|
-
expect(data).toEqual([
|
|
178
|
-
{ "ID": 1, "명칭": "관리자" },
|
|
179
|
-
]);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("Can read permission group permission sheet data", async () => {
|
|
183
|
-
const ws = await wb.getWorksheet("권한그룹권한");
|
|
184
|
-
const data = await ws.getDataTable();
|
|
185
|
-
expect(data).toEqual([
|
|
186
|
-
{ "권한그룹.ID": 1, "코드": "ALL", "값": true },
|
|
187
|
-
]);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("Can read employee sheet data", async () => {
|
|
191
|
-
const ws = await wb.getWorksheet("직원");
|
|
192
|
-
const data = await ws.getDataTable();
|
|
193
|
-
expect(data).toEqual([
|
|
194
|
-
{
|
|
195
|
-
"ID": 1,
|
|
196
|
-
"이름": "관리자",
|
|
197
|
-
"이메일": "admin@test.com",
|
|
198
|
-
"비밀번호": "1234",
|
|
199
|
-
"권한그룹.ID": 1,
|
|
200
|
-
"삭제": false,
|
|
201
|
-
},
|
|
202
|
-
]);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
});
|
|
@@ -1,469 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { ExcelWorkbook } from "../src/excel-workbook";
|
|
3
|
-
|
|
4
|
-
describe("ExcelWorksheet", () => {
|
|
5
|
-
describe("Sheet name", () => {
|
|
6
|
-
it("should change sheet name", async () => {
|
|
7
|
-
const wb = new ExcelWorkbook();
|
|
8
|
-
const ws = await wb.addWorksheet("OldName");
|
|
9
|
-
|
|
10
|
-
await ws.setName("NewName");
|
|
11
|
-
const name = await ws.getName();
|
|
12
|
-
expect(name).toBe("NewName");
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("should preserve changed sheet name after roundtrip", async () => {
|
|
16
|
-
const wb = new ExcelWorkbook();
|
|
17
|
-
const ws = await wb.addWorksheet("OldName");
|
|
18
|
-
await ws.setName("NewName");
|
|
19
|
-
|
|
20
|
-
const bytes = await wb.toBytes();
|
|
21
|
-
|
|
22
|
-
const wb2 = new ExcelWorkbook(bytes);
|
|
23
|
-
const names = await wb2.getWorksheetNames();
|
|
24
|
-
expect(names).toContain("NewName");
|
|
25
|
-
expect(names).not.toContain("OldName");
|
|
26
|
-
|
|
27
|
-
const ws2 = await wb2.getWorksheet("NewName");
|
|
28
|
-
const name = await ws2.getName();
|
|
29
|
-
expect(name).toBe("NewName");
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe("Row/Column copy", () => {
|
|
34
|
-
it("should copy row", async () => {
|
|
35
|
-
const wb = new ExcelWorkbook();
|
|
36
|
-
const ws = await wb.addWorksheet("Test");
|
|
37
|
-
|
|
38
|
-
// Set source row
|
|
39
|
-
await ws.cell(0, 0).setValue("A");
|
|
40
|
-
await ws.cell(0, 1).setValue("B");
|
|
41
|
-
await ws.cell(0, 2).setValue("C");
|
|
42
|
-
|
|
43
|
-
// Copy row
|
|
44
|
-
await ws.copyRow(0, 2);
|
|
45
|
-
|
|
46
|
-
expect(await ws.cell(2, 0).getValue()).toBe("A");
|
|
47
|
-
expect(await ws.cell(2, 1).getValue()).toBe("B");
|
|
48
|
-
expect(await ws.cell(2, 2).getValue()).toBe("C");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("should copy cell", async () => {
|
|
52
|
-
const wb = new ExcelWorkbook();
|
|
53
|
-
const ws = await wb.addWorksheet("Test");
|
|
54
|
-
|
|
55
|
-
await ws.cell(0, 0).setValue("Original");
|
|
56
|
-
await ws.copyCell({ r: 0, c: 0 }, { r: 1, c: 1 });
|
|
57
|
-
|
|
58
|
-
expect(await ws.cell(1, 1).getValue()).toBe("Original");
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("should copy only row style", async () => {
|
|
62
|
-
const wb = new ExcelWorkbook();
|
|
63
|
-
const ws = await wb.addWorksheet("Test");
|
|
64
|
-
|
|
65
|
-
// Set styles
|
|
66
|
-
await ws.cell(0, 0).setValue("Styled");
|
|
67
|
-
await ws.cell(0, 0).setStyle({ background: "00FF0000" });
|
|
68
|
-
await ws.cell(0, 1).setValue("Also Styled");
|
|
69
|
-
await ws.cell(0, 1).setStyle({ background: "0000FF00" });
|
|
70
|
-
|
|
71
|
-
// Copy only styles
|
|
72
|
-
await ws.copyRowStyle(0, 2);
|
|
73
|
-
|
|
74
|
-
// Values should not be copied
|
|
75
|
-
expect(await ws.cell(2, 0).getValue()).toBeUndefined();
|
|
76
|
-
expect(await ws.cell(2, 1).getValue()).toBeUndefined();
|
|
77
|
-
|
|
78
|
-
// Styles should be copied
|
|
79
|
-
const styleId0 = await ws.cell(0, 0).getStyleId();
|
|
80
|
-
const styleId2 = await ws.cell(2, 0).getStyleId();
|
|
81
|
-
expect(styleId2).toBe(styleId0);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("should insert copy row when srcR < targetR", async () => {
|
|
85
|
-
const wb = new ExcelWorkbook();
|
|
86
|
-
const ws = await wb.addWorksheet("Test");
|
|
87
|
-
|
|
88
|
-
await ws.cell(0, 0).setValue("Row0");
|
|
89
|
-
await ws.cell(1, 0).setValue("Row1");
|
|
90
|
-
await ws.cell(2, 0).setValue("Row2");
|
|
91
|
-
|
|
92
|
-
// Insert copy row 0 at position 1 (existing rows are shifted)
|
|
93
|
-
await ws.insertCopyRow(0, 1);
|
|
94
|
-
|
|
95
|
-
expect(await ws.cell(0, 0).getValue()).toBe("Row0");
|
|
96
|
-
expect(await ws.cell(1, 0).getValue()).toBe("Row0"); // copied
|
|
97
|
-
expect(await ws.cell(2, 0).getValue()).toBe("Row1"); // shifted
|
|
98
|
-
expect(await ws.cell(3, 0).getValue()).toBe("Row2"); // shifted
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should insert copy row when srcR > targetR", async () => {
|
|
102
|
-
const wb = new ExcelWorkbook();
|
|
103
|
-
const ws = await wb.addWorksheet("Test");
|
|
104
|
-
|
|
105
|
-
await ws.cell(0, 0).setValue("Row0");
|
|
106
|
-
await ws.cell(1, 0).setValue("Row1");
|
|
107
|
-
await ws.cell(2, 0).setValue("Row2");
|
|
108
|
-
await ws.cell(3, 0).setValue("Row3");
|
|
109
|
-
|
|
110
|
-
// Insert copy row 2 at position 1 (existing rows are shifted)
|
|
111
|
-
await ws.insertCopyRow(2, 1);
|
|
112
|
-
|
|
113
|
-
expect(await ws.cell(0, 0).getValue()).toBe("Row0");
|
|
114
|
-
expect(await ws.cell(1, 0).getValue()).toBe("Row2"); // copied
|
|
115
|
-
expect(await ws.cell(2, 0).getValue()).toBe("Row1"); // shifted
|
|
116
|
-
expect(await ws.cell(3, 0).getValue()).toBe("Row2"); // shifted (original Row2)
|
|
117
|
-
expect(await ws.cell(4, 0).getValue()).toBe("Row3"); // shifted
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("should insert copy row when srcR == targetR", async () => {
|
|
121
|
-
const wb = new ExcelWorkbook();
|
|
122
|
-
const ws = await wb.addWorksheet("Test");
|
|
123
|
-
|
|
124
|
-
await ws.cell(0, 0).setValue("Row0");
|
|
125
|
-
await ws.cell(1, 0).setValue("Row1");
|
|
126
|
-
await ws.cell(2, 0).setValue("Row2");
|
|
127
|
-
|
|
128
|
-
// Insert copy row 1 at position 1 (copy itself)
|
|
129
|
-
await ws.insertCopyRow(1, 1);
|
|
130
|
-
|
|
131
|
-
expect(await ws.cell(0, 0).getValue()).toBe("Row0");
|
|
132
|
-
expect(await ws.cell(1, 0).getValue()).toBe("Row1"); // copied
|
|
133
|
-
expect(await ws.cell(2, 0).getValue()).toBe("Row1"); // shifted (original Row1)
|
|
134
|
-
expect(await ws.cell(3, 0).getValue()).toBe("Row2"); // shifted
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("should skip merge handling when skipMerge is true", async () => {
|
|
138
|
-
const wb = new ExcelWorkbook();
|
|
139
|
-
const ws = await wb.addWorksheet("Test");
|
|
140
|
-
|
|
141
|
-
await ws.cell(0, 0).setValue("Row0");
|
|
142
|
-
await ws.cell(1, 0).setValue("Row1");
|
|
143
|
-
await ws.cell(0, 0).merge(0, 1); // Merge A1:B1
|
|
144
|
-
|
|
145
|
-
const wsData = await ws["_getWsData"]();
|
|
146
|
-
|
|
147
|
-
// Copy row 0 to row 1, but skip merge handling
|
|
148
|
-
wsData.copyRow(0, 1, { skipMerge: true });
|
|
149
|
-
|
|
150
|
-
// Row 1 should have the data but merge should not be copied
|
|
151
|
-
const merges = wsData.getMergeCells();
|
|
152
|
-
expect(merges).toEqual([
|
|
153
|
-
{ s: { r: 0, c: 0 }, e: { r: 0, c: 1 } }, // original merge, unchanged
|
|
154
|
-
]);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe("Range and cell access", () => {
|
|
159
|
-
it("should get data range", async () => {
|
|
160
|
-
const wb = new ExcelWorkbook();
|
|
161
|
-
const ws = await wb.addWorksheet("Test");
|
|
162
|
-
|
|
163
|
-
await ws.cell(0, 0).setValue("A");
|
|
164
|
-
await ws.cell(2, 3).setValue("D");
|
|
165
|
-
|
|
166
|
-
const range = await ws.getRange();
|
|
167
|
-
expect(range.s.r).toBe(0);
|
|
168
|
-
expect(range.s.c).toBe(0);
|
|
169
|
-
expect(range.e.r).toBe(2);
|
|
170
|
-
expect(range.e.c).toBe(3);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("should get all cells", async () => {
|
|
174
|
-
const wb = new ExcelWorkbook();
|
|
175
|
-
const ws = await wb.addWorksheet("Test");
|
|
176
|
-
|
|
177
|
-
await ws.cell(0, 0).setValue("A");
|
|
178
|
-
await ws.cell(0, 1).setValue("B");
|
|
179
|
-
await ws.cell(1, 0).setValue("C");
|
|
180
|
-
await ws.cell(1, 1).setValue("D");
|
|
181
|
-
|
|
182
|
-
const cells = await ws.getCells();
|
|
183
|
-
expect(cells.length).toBe(2);
|
|
184
|
-
expect(cells[0].length).toBeGreaterThanOrEqual(2);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
describe("Data table", () => {
|
|
189
|
-
it("should get data table", async () => {
|
|
190
|
-
const wb = new ExcelWorkbook();
|
|
191
|
-
const ws = await wb.addWorksheet("Test");
|
|
192
|
-
|
|
193
|
-
// Headers
|
|
194
|
-
await ws.cell(0, 0).setValue("Name");
|
|
195
|
-
await ws.cell(0, 1).setValue("Age");
|
|
196
|
-
// Data
|
|
197
|
-
await ws.cell(1, 0).setValue("Alice");
|
|
198
|
-
await ws.cell(1, 1).setValue(30);
|
|
199
|
-
await ws.cell(2, 0).setValue("Bob");
|
|
200
|
-
await ws.cell(2, 1).setValue(25);
|
|
201
|
-
|
|
202
|
-
const data = await ws.getDataTable();
|
|
203
|
-
expect(data.length).toBe(2);
|
|
204
|
-
expect(data[0]["Name"]).toBe("Alice");
|
|
205
|
-
expect(data[0]["Age"]).toBe(30);
|
|
206
|
-
expect(data[1]["Name"]).toBe("Bob");
|
|
207
|
-
expect(data[1]["Age"]).toBe(25);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it("should filter specific headers only", async () => {
|
|
211
|
-
const wb = new ExcelWorkbook();
|
|
212
|
-
const ws = await wb.addWorksheet("Test");
|
|
213
|
-
|
|
214
|
-
await ws.cell(0, 0).setValue("Name");
|
|
215
|
-
await ws.cell(0, 1).setValue("Age");
|
|
216
|
-
await ws.cell(0, 2).setValue("Ignore");
|
|
217
|
-
await ws.cell(1, 0).setValue("Alice");
|
|
218
|
-
await ws.cell(1, 1).setValue(30);
|
|
219
|
-
await ws.cell(1, 2).setValue("X");
|
|
220
|
-
|
|
221
|
-
const data = await ws.getDataTable({
|
|
222
|
-
usableHeaderNameFn: (name) => name !== "Ignore",
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
expect(data[0]["Name"]).toBe("Alice");
|
|
226
|
-
expect(data[0]["Age"]).toBe(30);
|
|
227
|
-
expect(data[0]["Ignore"]).toBeUndefined();
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it("should set data matrix", async () => {
|
|
231
|
-
const wb = new ExcelWorkbook();
|
|
232
|
-
const ws = await wb.addWorksheet("Test");
|
|
233
|
-
|
|
234
|
-
const matrix = [
|
|
235
|
-
["A", "B", "C"],
|
|
236
|
-
[1, 2, 3],
|
|
237
|
-
[4, 5, 6],
|
|
238
|
-
];
|
|
239
|
-
|
|
240
|
-
await ws.setDataMatrix(matrix);
|
|
241
|
-
|
|
242
|
-
expect(await ws.cell(0, 0).getValue()).toBe("A");
|
|
243
|
-
expect(await ws.cell(0, 2).getValue()).toBe("C");
|
|
244
|
-
expect(await ws.cell(2, 2).getValue()).toBe(6);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it("should set records array", async () => {
|
|
248
|
-
const wb = new ExcelWorkbook();
|
|
249
|
-
const ws = await wb.addWorksheet("Test");
|
|
250
|
-
|
|
251
|
-
const records = [
|
|
252
|
-
{ Name: "Alice", Age: 30 },
|
|
253
|
-
{ Name: "Bob", Age: 25 },
|
|
254
|
-
];
|
|
255
|
-
|
|
256
|
-
await ws.setRecords(records);
|
|
257
|
-
|
|
258
|
-
// Check headers
|
|
259
|
-
const headers = [await ws.cell(0, 0).getValue(), await ws.cell(0, 1).getValue()];
|
|
260
|
-
expect(headers).toContain("Name");
|
|
261
|
-
expect(headers).toContain("Age");
|
|
262
|
-
|
|
263
|
-
// Check data (order may vary)
|
|
264
|
-
const data = await ws.getDataTable();
|
|
265
|
-
expect(data.length).toBe(2);
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
describe("Column width", () => {
|
|
270
|
-
it("should preserve column width after roundtrip", async () => {
|
|
271
|
-
const wb = new ExcelWorkbook();
|
|
272
|
-
const ws = await wb.addWorksheet("Test");
|
|
273
|
-
|
|
274
|
-
await ws.cell(0, 0).setValue("A1");
|
|
275
|
-
await ws.col(0).setWidth(25);
|
|
276
|
-
await ws.col(2).setWidth(30);
|
|
277
|
-
|
|
278
|
-
const bytes = await wb.toBytes();
|
|
279
|
-
|
|
280
|
-
const wb2 = new ExcelWorkbook(bytes);
|
|
281
|
-
await wb2.getWorksheet("Test");
|
|
282
|
-
|
|
283
|
-
// Check cols data in XML structure
|
|
284
|
-
const wsData = await (wb2 as any).zipCache.get("xl/worksheets/sheet1.xml");
|
|
285
|
-
const cols = wsData.data.worksheet.cols?.[0]?.col ?? [];
|
|
286
|
-
|
|
287
|
-
// Check width of column A (index 0, 1-based=1)
|
|
288
|
-
const colA = cols.find((c: any) => c.$.min === "1" && c.$.max === "1");
|
|
289
|
-
expect(colA).toBeDefined();
|
|
290
|
-
expect(colA.$.width).toBe("25");
|
|
291
|
-
|
|
292
|
-
// Check width of column C (index 2, 1-based=3)
|
|
293
|
-
const colC = cols.find((c: any) => c.$.min === "3" && c.$.max === "3");
|
|
294
|
-
expect(colC).toBeDefined();
|
|
295
|
-
expect(colC.$.width).toBe("30");
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe("Column access", () => {
|
|
300
|
-
it("should get all cells in column", async () => {
|
|
301
|
-
const wb = new ExcelWorkbook();
|
|
302
|
-
const ws = await wb.addWorksheet("Test");
|
|
303
|
-
|
|
304
|
-
await ws.cell(0, 0).setValue("A1");
|
|
305
|
-
await ws.cell(1, 0).setValue("A2");
|
|
306
|
-
await ws.cell(2, 0).setValue("A3");
|
|
307
|
-
|
|
308
|
-
const cells = await ws.col(0).getCells();
|
|
309
|
-
expect(cells.length).toBe(3);
|
|
310
|
-
expect(await cells[0].getValue()).toBe("A1");
|
|
311
|
-
expect(await cells[1].getValue()).toBe("A2");
|
|
312
|
-
expect(await cells[2].getValue()).toBe("A3");
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
describe("Data table edge cases", () => {
|
|
317
|
-
it("should return empty array when calling getDataTable on empty sheet", async () => {
|
|
318
|
-
const wb = new ExcelWorkbook();
|
|
319
|
-
const ws = await wb.addWorksheet("Empty");
|
|
320
|
-
const data = await ws.getDataTable();
|
|
321
|
-
expect(data).toEqual([]);
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
it("should return empty array when only headers exist without data", async () => {
|
|
325
|
-
const wb = new ExcelWorkbook();
|
|
326
|
-
const ws = await wb.addWorksheet("Test");
|
|
327
|
-
await ws.cell(0, 0).setValue("Header1");
|
|
328
|
-
await ws.cell(0, 1).setValue("Header2");
|
|
329
|
-
const data = await ws.getDataTable();
|
|
330
|
-
expect(data).toEqual([]);
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
describe("Data table options", () => {
|
|
335
|
-
it("should specify header row with headerRowIndex", async () => {
|
|
336
|
-
const wb = new ExcelWorkbook();
|
|
337
|
-
const ws = await wb.addWorksheet("Test");
|
|
338
|
-
|
|
339
|
-
// Row 0 is title
|
|
340
|
-
await ws.cell(0, 0).setValue("Title");
|
|
341
|
-
// Row 1 is header
|
|
342
|
-
await ws.cell(1, 0).setValue("Name");
|
|
343
|
-
await ws.cell(1, 1).setValue("Age");
|
|
344
|
-
// Data starts from row 2
|
|
345
|
-
await ws.cell(2, 0).setValue("Alice");
|
|
346
|
-
await ws.cell(2, 1).setValue(30);
|
|
347
|
-
|
|
348
|
-
const data = await ws.getDataTable({ headerRowIndex: 1 });
|
|
349
|
-
expect(data.length).toBe(1);
|
|
350
|
-
expect(data[0]["Name"]).toBe("Alice");
|
|
351
|
-
expect(data[0]["Age"]).toBe(30);
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
it("should detect data end with checkEndColIndex", async () => {
|
|
355
|
-
const wb = new ExcelWorkbook();
|
|
356
|
-
const ws = await wb.addWorksheet("Test");
|
|
357
|
-
|
|
358
|
-
await ws.cell(0, 0).setValue("Name");
|
|
359
|
-
await ws.cell(0, 1).setValue("Age");
|
|
360
|
-
await ws.cell(1, 0).setValue("Alice");
|
|
361
|
-
await ws.cell(1, 1).setValue(30);
|
|
362
|
-
await ws.cell(2, 0).setValue("Bob");
|
|
363
|
-
await ws.cell(2, 1).setValue(25);
|
|
364
|
-
// Row 3 has empty Name column -> data end
|
|
365
|
-
await ws.cell(3, 1).setValue(999);
|
|
366
|
-
|
|
367
|
-
const data = await ws.getDataTable({ checkEndColIndex: 0 });
|
|
368
|
-
expect(data.length).toBe(2);
|
|
369
|
-
expect(data[0]["Name"]).toBe("Alice");
|
|
370
|
-
expect(data[1]["Name"]).toBe("Bob");
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
describe("Merge cells", () => {
|
|
375
|
-
it("should shift merge cells when inserting row", async () => {
|
|
376
|
-
const wb = new ExcelWorkbook();
|
|
377
|
-
const ws = await wb.addWorksheet("Test");
|
|
378
|
-
|
|
379
|
-
// Create merged cells: A1:B2 and C3:D4
|
|
380
|
-
await ws.cell(0, 0).merge(1, 1); // A1:B2
|
|
381
|
-
await ws.cell(2, 2).merge(3, 3); // C3:D4
|
|
382
|
-
|
|
383
|
-
const wsData = await ws["_getWsData"](); // Access private method for testing
|
|
384
|
-
wsData.shiftMergeCells(2, 1); // Shift rows >= 2 by +1
|
|
385
|
-
|
|
386
|
-
const merges = wsData.getMergeCells();
|
|
387
|
-
expect(merges).toEqual([
|
|
388
|
-
{ s: { r: 0, c: 0 }, e: { r: 1, c: 1 } }, // unchanged
|
|
389
|
-
{ s: { r: 3, c: 2 }, e: { r: 4, c: 3 } }, // shifted down by 1
|
|
390
|
-
]);
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it("should shift merge cells when inserting row with merges", async () => {
|
|
394
|
-
const wb = new ExcelWorkbook();
|
|
395
|
-
const ws = await wb.addWorksheet("Test");
|
|
396
|
-
|
|
397
|
-
await ws.cell(0, 0).setValue("Row0");
|
|
398
|
-
await ws.cell(1, 0).setValue("Row1");
|
|
399
|
-
await ws.cell(2, 0).setValue("Row2");
|
|
400
|
-
|
|
401
|
-
// Create merge A3:B4 (rows 2-3, well below insertion point)
|
|
402
|
-
await ws.cell(2, 0).merge(3, 1);
|
|
403
|
-
|
|
404
|
-
// Insert copy of row 0 at position 1
|
|
405
|
-
await ws.insertCopyRow(0, 1);
|
|
406
|
-
|
|
407
|
-
// Check that merge was shifted correctly
|
|
408
|
-
const wsData = await ws["_getWsData"]();
|
|
409
|
-
const merges = wsData.getMergeCells();
|
|
410
|
-
|
|
411
|
-
// Merge at rows 2-3 should shift to rows 3-4
|
|
412
|
-
expect(merges).toEqual([
|
|
413
|
-
{ s: { r: 3, c: 0 }, e: { r: 4, c: 1 } },
|
|
414
|
-
]);
|
|
415
|
-
|
|
416
|
-
expect(await ws.cell(0, 0).getValue()).toBe("Row0");
|
|
417
|
-
expect(await ws.cell(1, 0).getValue()).toBe("Row0"); // copied
|
|
418
|
-
expect(await ws.cell(2, 0).getValue()).toBe("Row1"); // shifted
|
|
419
|
-
expect(await ws.cell(3, 0).getValue()).toBe("Row2"); // shifted
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
it("should handle multi-row merge when inserting row", async () => {
|
|
423
|
-
const wb = new ExcelWorkbook();
|
|
424
|
-
const ws = await wb.addWorksheet("Test");
|
|
425
|
-
|
|
426
|
-
await ws.cell(0, 0).setValue("Row0");
|
|
427
|
-
await ws.cell(1, 0).setValue("Row1");
|
|
428
|
-
await ws.cell(2, 0).setValue("Row2");
|
|
429
|
-
await ws.cell(3, 0).setValue("Row3");
|
|
430
|
-
|
|
431
|
-
// Create merge A3:B4 (rows 2-3, below insertion point)
|
|
432
|
-
await ws.cell(2, 0).merge(3, 1);
|
|
433
|
-
|
|
434
|
-
// Insert copy of row 0 at position 1
|
|
435
|
-
await ws.insertCopyRow(0, 1);
|
|
436
|
-
|
|
437
|
-
const wsData = await ws["_getWsData"]();
|
|
438
|
-
const merges = wsData.getMergeCells();
|
|
439
|
-
|
|
440
|
-
// Merge at rows 2-3 should shift to rows 3-4 (rows >= 1 shift by 1)
|
|
441
|
-
expect(merges).toHaveLength(1);
|
|
442
|
-
expect(merges[0].s).toEqual({ r: 3, c: 0 });
|
|
443
|
-
expect(merges[0].e).toEqual({ r: 4, c: 1 }); // shifted from 2-3 to 3-4
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
it("should not shift merge above insertion point", async () => {
|
|
447
|
-
const wb = new ExcelWorkbook();
|
|
448
|
-
const ws = await wb.addWorksheet("Test");
|
|
449
|
-
|
|
450
|
-
await ws.cell(0, 0).setValue("Row0");
|
|
451
|
-
await ws.cell(1, 0).setValue("Row1");
|
|
452
|
-
await ws.cell(2, 0).setValue("Row2");
|
|
453
|
-
|
|
454
|
-
// Create merge A1:B1 (row 0)
|
|
455
|
-
await ws.cell(0, 0).merge(0, 1);
|
|
456
|
-
|
|
457
|
-
// Insert copy of row 1 at position 2
|
|
458
|
-
await ws.insertCopyRow(1, 2);
|
|
459
|
-
|
|
460
|
-
const wsData = await ws["_getWsData"]();
|
|
461
|
-
const merges = wsData.getMergeCells();
|
|
462
|
-
|
|
463
|
-
// Merge should remain unchanged at A1:B1
|
|
464
|
-
expect(merges).toEqual([
|
|
465
|
-
{ s: { r: 0, c: 0 }, e: { r: 0, c: 1 } },
|
|
466
|
-
]);
|
|
467
|
-
});
|
|
468
|
-
});
|
|
469
|
-
});
|