@simplysm/excel 13.0.69 → 13.0.70

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.
Files changed (74) hide show
  1. package/README.md +19 -516
  2. package/dist/excel-cell.d.ts +28 -28
  3. package/dist/excel-cell.d.ts.map +1 -1
  4. package/dist/excel-cell.js +24 -24
  5. package/dist/excel-cell.js.map +1 -1
  6. package/dist/excel-col.d.ts +4 -4
  7. package/dist/excel-col.d.ts.map +1 -1
  8. package/dist/excel-col.js +3 -3
  9. package/dist/excel-row.d.ts +3 -3
  10. package/dist/excel-row.d.ts.map +1 -1
  11. package/dist/excel-row.js +2 -2
  12. package/dist/excel-workbook.d.ts +23 -23
  13. package/dist/excel-workbook.d.ts.map +1 -1
  14. package/dist/excel-workbook.js +15 -15
  15. package/dist/excel-workbook.js.map +1 -1
  16. package/dist/excel-worksheet.d.ts +32 -32
  17. package/dist/excel-worksheet.d.ts.map +1 -1
  18. package/dist/excel-worksheet.js +32 -32
  19. package/dist/excel-worksheet.js.map +1 -1
  20. package/dist/excel-wrapper.d.ts +7 -7
  21. package/dist/excel-wrapper.js +7 -7
  22. package/dist/excel-wrapper.js.map +1 -1
  23. package/dist/types.d.ts +16 -16
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/utils/excel-utils.d.ts +23 -23
  26. package/dist/utils/excel-utils.d.ts.map +1 -1
  27. package/dist/utils/excel-utils.js +25 -25
  28. package/dist/utils/excel-utils.js.map +1 -1
  29. package/dist/utils/zip-cache.d.ts +6 -6
  30. package/dist/xml/excel-xml-content-type.d.ts +2 -2
  31. package/dist/xml/excel-xml-drawing.d.ts +2 -2
  32. package/dist/xml/excel-xml-relationship.d.ts +2 -2
  33. package/dist/xml/excel-xml-relationship.js +1 -1
  34. package/dist/xml/excel-xml-relationship.js.map +1 -1
  35. package/dist/xml/excel-xml-shared-string.d.ts +2 -2
  36. package/dist/xml/excel-xml-style.d.ts +2 -2
  37. package/dist/xml/excel-xml-style.js +6 -6
  38. package/dist/xml/excel-xml-style.js.map +1 -1
  39. package/dist/xml/excel-xml-unknown.d.ts +2 -2
  40. package/dist/xml/excel-xml-workbook.d.ts +2 -2
  41. package/dist/xml/excel-xml-workbook.js +1 -1
  42. package/dist/xml/excel-xml-workbook.js.map +1 -1
  43. package/dist/xml/excel-xml-worksheet.d.ts +6 -6
  44. package/dist/xml/excel-xml-worksheet.d.ts.map +1 -1
  45. package/dist/xml/excel-xml-worksheet.js +9 -8
  46. package/dist/xml/excel-xml-worksheet.js.map +1 -1
  47. package/package.json +6 -5
  48. package/src/excel-cell.ts +35 -35
  49. package/src/excel-col.ts +4 -4
  50. package/src/excel-row.ts +3 -3
  51. package/src/excel-workbook.ts +30 -30
  52. package/src/excel-worksheet.ts +47 -47
  53. package/src/excel-wrapper.ts +18 -18
  54. package/src/index.ts +3 -3
  55. package/src/types.ts +15 -17
  56. package/src/utils/excel-utils.ts +38 -38
  57. package/src/utils/zip-cache.ts +6 -6
  58. package/src/xml/excel-xml-content-type.ts +3 -3
  59. package/src/xml/excel-xml-drawing.ts +2 -2
  60. package/src/xml/excel-xml-relationship.ts +3 -3
  61. package/src/xml/excel-xml-shared-string.ts +2 -2
  62. package/src/xml/excel-xml-style.ts +11 -11
  63. package/src/xml/excel-xml-unknown.ts +2 -2
  64. package/src/xml/excel-xml-workbook.ts +5 -5
  65. package/src/xml/excel-xml-worksheet.ts +44 -43
  66. package/tests/excel-cell.spec.ts +448 -0
  67. package/tests/excel-col.spec.ts +112 -0
  68. package/tests/excel-row.spec.ts +71 -0
  69. package/tests/excel-workbook.spec.ts +160 -0
  70. package/tests/excel-worksheet.spec.ts +389 -0
  71. package/tests/excel-wrapper.spec.ts +296 -0
  72. package/tests/fixtures/logo.png +0 -0
  73. package/tests/image-insert.spec.ts +190 -0
  74. package/tests/utils/excel-utils.spec.ts +242 -0
@@ -0,0 +1,160 @@
1
+ import { 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.createWorksheet("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.createWorksheet("Sheet1");
19
+ await wb.createWorksheet("Sheet2");
20
+ await wb.createWorksheet("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.createWorksheet("First");
31
+ await wb.createWorksheet("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.createWorksheet("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.createWorksheet("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.createWorksheet("Test");
60
+ await ws.cell(0, 0).setVal("Hello");
61
+
62
+ const bytes: Bytes = await wb.getBytes();
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.createWorksheet("Test");
70
+ await ws.cell(0, 0).setVal("Hello");
71
+
72
+ const blob = await wb.getBlob();
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.createWorksheet("Test");
84
+ await ws1.cell(0, 0).setVal("BlobTest");
85
+ const bytes = await wb1.getBytes();
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).getVal();
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.createWorksheet("RoundTrip");
106
+ await ws1.cell(0, 0).setVal("TestValue");
107
+ await ws1.cell(0, 1).setVal(12345);
108
+
109
+ // Save
110
+ const bytes = await wb1.getBytes();
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).getVal();
120
+ const val2 = await ws2.cell(0, 1).getVal();
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.createWorksheet("Test");
133
+ await wb.close();
134
+
135
+ await expect(wb.getWorksheetNames()).rejects.toThrow();
136
+ });
137
+
138
+ it("Error when calling createWorksheet() after close()", async () => {
139
+ const wb = new ExcelWorkbook();
140
+ await wb.close();
141
+
142
+ await expect(wb.createWorksheet("New")).rejects.toThrow();
143
+ });
144
+
145
+ it("Error when calling getWorksheet() after close()", async () => {
146
+ const wb = new ExcelWorkbook();
147
+ await wb.createWorksheet("Test");
148
+ await wb.close();
149
+
150
+ await expect(wb.getWorksheet(0)).rejects.toThrow();
151
+ });
152
+
153
+ it("Error when calling getBytes() after close()", async () => {
154
+ const wb = new ExcelWorkbook();
155
+ await wb.close();
156
+
157
+ await expect(wb.getBytes()).rejects.toThrow();
158
+ });
159
+ });
160
+ });
@@ -0,0 +1,389 @@
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 get sheet name", async () => {
7
+ const wb = new ExcelWorkbook();
8
+ const ws = await wb.createWorksheet("MySheet");
9
+
10
+ const name = await ws.getName();
11
+ expect(name).toBe("MySheet");
12
+ });
13
+
14
+ it("should change sheet name", async () => {
15
+ const wb = new ExcelWorkbook();
16
+ const ws = await wb.createWorksheet("OldName");
17
+
18
+ await ws.setName("NewName");
19
+ const name = await ws.getName();
20
+ expect(name).toBe("NewName");
21
+ });
22
+
23
+ it("should preserve changed sheet name after roundtrip", async () => {
24
+ const wb = new ExcelWorkbook();
25
+ const ws = await wb.createWorksheet("OldName");
26
+ await ws.setName("NewName");
27
+
28
+ const bytes = await wb.getBytes();
29
+
30
+ const wb2 = new ExcelWorkbook(bytes);
31
+ const names = await wb2.getWorksheetNames();
32
+ expect(names).toContain("NewName");
33
+ expect(names).not.toContain("OldName");
34
+
35
+ const ws2 = await wb2.getWorksheet("NewName");
36
+ const name = await ws2.getName();
37
+ expect(name).toBe("NewName");
38
+ });
39
+ });
40
+
41
+ describe("Row/Column copy", () => {
42
+ it("should copy row", async () => {
43
+ const wb = new ExcelWorkbook();
44
+ const ws = await wb.createWorksheet("Test");
45
+
46
+ // Set source row
47
+ await ws.cell(0, 0).setVal("A");
48
+ await ws.cell(0, 1).setVal("B");
49
+ await ws.cell(0, 2).setVal("C");
50
+
51
+ // Copy row
52
+ await ws.copyRow(0, 2);
53
+
54
+ expect(await ws.cell(2, 0).getVal()).toBe("A");
55
+ expect(await ws.cell(2, 1).getVal()).toBe("B");
56
+ expect(await ws.cell(2, 2).getVal()).toBe("C");
57
+ });
58
+
59
+ it("should copy cell", async () => {
60
+ const wb = new ExcelWorkbook();
61
+ const ws = await wb.createWorksheet("Test");
62
+
63
+ await ws.cell(0, 0).setVal("Original");
64
+ await ws.copyCell({ r: 0, c: 0 }, { r: 1, c: 1 });
65
+
66
+ expect(await ws.cell(1, 1).getVal()).toBe("Original");
67
+ });
68
+
69
+ it("should copy only row style", async () => {
70
+ const wb = new ExcelWorkbook();
71
+ const ws = await wb.createWorksheet("Test");
72
+
73
+ // Set styles
74
+ await ws.cell(0, 0).setVal("Styled");
75
+ await ws.cell(0, 0).setStyle({ background: "00FF0000" });
76
+ await ws.cell(0, 1).setVal("Also Styled");
77
+ await ws.cell(0, 1).setStyle({ background: "0000FF00" });
78
+
79
+ // Copy only styles
80
+ await ws.copyRowStyle(0, 2);
81
+
82
+ // Values should not be copied
83
+ expect(await ws.cell(2, 0).getVal()).toBeUndefined();
84
+ expect(await ws.cell(2, 1).getVal()).toBeUndefined();
85
+
86
+ // Styles should be copied
87
+ const styleId0 = await ws.cell(0, 0).getStyleId();
88
+ const styleId2 = await ws.cell(2, 0).getStyleId();
89
+ expect(styleId2).toBe(styleId0);
90
+ });
91
+
92
+ it("should insert copy row when srcR < targetR", async () => {
93
+ const wb = new ExcelWorkbook();
94
+ const ws = await wb.createWorksheet("Test");
95
+
96
+ await ws.cell(0, 0).setVal("Row0");
97
+ await ws.cell(1, 0).setVal("Row1");
98
+ await ws.cell(2, 0).setVal("Row2");
99
+
100
+ // Insert copy row 0 at position 1 (existing rows are shifted)
101
+ await ws.insertCopyRow(0, 1);
102
+
103
+ expect(await ws.cell(0, 0).getVal()).toBe("Row0");
104
+ expect(await ws.cell(1, 0).getVal()).toBe("Row0"); // copied
105
+ expect(await ws.cell(2, 0).getVal()).toBe("Row1"); // shifted
106
+ expect(await ws.cell(3, 0).getVal()).toBe("Row2"); // shifted
107
+ });
108
+
109
+ it("should insert copy row when srcR > targetR", async () => {
110
+ const wb = new ExcelWorkbook();
111
+ const ws = await wb.createWorksheet("Test");
112
+
113
+ await ws.cell(0, 0).setVal("Row0");
114
+ await ws.cell(1, 0).setVal("Row1");
115
+ await ws.cell(2, 0).setVal("Row2");
116
+ await ws.cell(3, 0).setVal("Row3");
117
+
118
+ // Insert copy row 2 at position 1 (existing rows are shifted)
119
+ await ws.insertCopyRow(2, 1);
120
+
121
+ expect(await ws.cell(0, 0).getVal()).toBe("Row0");
122
+ expect(await ws.cell(1, 0).getVal()).toBe("Row2"); // copied
123
+ expect(await ws.cell(2, 0).getVal()).toBe("Row1"); // shifted
124
+ expect(await ws.cell(3, 0).getVal()).toBe("Row2"); // shifted (original Row2)
125
+ expect(await ws.cell(4, 0).getVal()).toBe("Row3"); // shifted
126
+ });
127
+
128
+ it("should insert copy row when srcR == targetR", async () => {
129
+ const wb = new ExcelWorkbook();
130
+ const ws = await wb.createWorksheet("Test");
131
+
132
+ await ws.cell(0, 0).setVal("Row0");
133
+ await ws.cell(1, 0).setVal("Row1");
134
+ await ws.cell(2, 0).setVal("Row2");
135
+
136
+ // Insert copy row 1 at position 1 (copy itself)
137
+ await ws.insertCopyRow(1, 1);
138
+
139
+ expect(await ws.cell(0, 0).getVal()).toBe("Row0");
140
+ expect(await ws.cell(1, 0).getVal()).toBe("Row1"); // copied
141
+ expect(await ws.cell(2, 0).getVal()).toBe("Row1"); // shifted (original Row1)
142
+ expect(await ws.cell(3, 0).getVal()).toBe("Row2"); // shifted
143
+ });
144
+ });
145
+
146
+ describe("Range and cell access", () => {
147
+ it("should get data range", async () => {
148
+ const wb = new ExcelWorkbook();
149
+ const ws = await wb.createWorksheet("Test");
150
+
151
+ await ws.cell(0, 0).setVal("A");
152
+ await ws.cell(2, 3).setVal("D");
153
+
154
+ const range = await ws.getRange();
155
+ expect(range.s.r).toBe(0);
156
+ expect(range.s.c).toBe(0);
157
+ expect(range.e.r).toBe(2);
158
+ expect(range.e.c).toBe(3);
159
+ });
160
+
161
+ it("should get all cells", async () => {
162
+ const wb = new ExcelWorkbook();
163
+ const ws = await wb.createWorksheet("Test");
164
+
165
+ await ws.cell(0, 0).setVal("A");
166
+ await ws.cell(0, 1).setVal("B");
167
+ await ws.cell(1, 0).setVal("C");
168
+ await ws.cell(1, 1).setVal("D");
169
+
170
+ const cells = await ws.getCells();
171
+ expect(cells.length).toBe(2);
172
+ expect(cells[0].length).toBeGreaterThanOrEqual(2);
173
+ });
174
+ });
175
+
176
+ describe("Data table", () => {
177
+ it("should get data table", async () => {
178
+ const wb = new ExcelWorkbook();
179
+ const ws = await wb.createWorksheet("Test");
180
+
181
+ // Headers
182
+ await ws.cell(0, 0).setVal("Name");
183
+ await ws.cell(0, 1).setVal("Age");
184
+ // Data
185
+ await ws.cell(1, 0).setVal("Alice");
186
+ await ws.cell(1, 1).setVal(30);
187
+ await ws.cell(2, 0).setVal("Bob");
188
+ await ws.cell(2, 1).setVal(25);
189
+
190
+ const data = await ws.getDataTable();
191
+ expect(data.length).toBe(2);
192
+ expect(data[0]["Name"]).toBe("Alice");
193
+ expect(data[0]["Age"]).toBe(30);
194
+ expect(data[1]["Name"]).toBe("Bob");
195
+ expect(data[1]["Age"]).toBe(25);
196
+ });
197
+
198
+ it("should filter specific headers only", async () => {
199
+ const wb = new ExcelWorkbook();
200
+ const ws = await wb.createWorksheet("Test");
201
+
202
+ await ws.cell(0, 0).setVal("Name");
203
+ await ws.cell(0, 1).setVal("Age");
204
+ await ws.cell(0, 2).setVal("Ignore");
205
+ await ws.cell(1, 0).setVal("Alice");
206
+ await ws.cell(1, 1).setVal(30);
207
+ await ws.cell(1, 2).setVal("X");
208
+
209
+ const data = await ws.getDataTable({
210
+ usableHeaderNameFn: (name) => name !== "Ignore",
211
+ });
212
+
213
+ expect(data[0]["Name"]).toBe("Alice");
214
+ expect(data[0]["Age"]).toBe(30);
215
+ expect(data[0]["Ignore"]).toBeUndefined();
216
+ });
217
+
218
+ it("should set data matrix", async () => {
219
+ const wb = new ExcelWorkbook();
220
+ const ws = await wb.createWorksheet("Test");
221
+
222
+ const matrix = [
223
+ ["A", "B", "C"],
224
+ [1, 2, 3],
225
+ [4, 5, 6],
226
+ ];
227
+
228
+ await ws.setDataMatrix(matrix);
229
+
230
+ expect(await ws.cell(0, 0).getVal()).toBe("A");
231
+ expect(await ws.cell(0, 2).getVal()).toBe("C");
232
+ expect(await ws.cell(2, 2).getVal()).toBe(6);
233
+ });
234
+
235
+ it("should set records array", async () => {
236
+ const wb = new ExcelWorkbook();
237
+ const ws = await wb.createWorksheet("Test");
238
+
239
+ const records = [
240
+ { Name: "Alice", Age: 30 },
241
+ { Name: "Bob", Age: 25 },
242
+ ];
243
+
244
+ await ws.setRecords(records);
245
+
246
+ // Check headers
247
+ const headers = [await ws.cell(0, 0).getVal(), await ws.cell(0, 1).getVal()];
248
+ expect(headers).toContain("Name");
249
+ expect(headers).toContain("Age");
250
+
251
+ // Check data (order may vary)
252
+ const data = await ws.getDataTable();
253
+ expect(data.length).toBe(2);
254
+ });
255
+ });
256
+
257
+ describe("View settings", () => {
258
+ it("should set zoom level", async () => {
259
+ const wb = new ExcelWorkbook();
260
+ const ws = await wb.createWorksheet("Test");
261
+
262
+ await ws.setZoom(85);
263
+ // Success if set without error
264
+ });
265
+
266
+ it("should set pane freeze", async () => {
267
+ const wb = new ExcelWorkbook();
268
+ const ws = await wb.createWorksheet("Test");
269
+
270
+ await ws.setFix({ r: 1 }); // Freeze 1 row
271
+ await ws.setFix({ c: 2 }); // Freeze 2 columns
272
+ await ws.setFix({ r: 1, c: 1 }); // Freeze 1 row and 1 column
273
+ // Success if set without error
274
+ });
275
+ });
276
+
277
+ describe("Column width", () => {
278
+ it("should set column width", async () => {
279
+ const wb = new ExcelWorkbook();
280
+ const ws = await wb.createWorksheet("Test");
281
+
282
+ await ws.col(0).setWidth(20);
283
+ // Success if set without error
284
+ });
285
+
286
+ it("should preserve column width after roundtrip", async () => {
287
+ const wb = new ExcelWorkbook();
288
+ const ws = await wb.createWorksheet("Test");
289
+
290
+ await ws.cell(0, 0).setVal("A1");
291
+ await ws.col(0).setWidth(25);
292
+ await ws.col(2).setWidth(30);
293
+
294
+ const bytes = await wb.getBytes();
295
+
296
+ const wb2 = new ExcelWorkbook(bytes);
297
+ await wb2.getWorksheet("Test");
298
+
299
+ // Check cols data in XML structure
300
+ const wsData = await (wb2 as any).zipCache.get("xl/worksheets/sheet1.xml");
301
+ const cols = wsData.data.worksheet.cols?.[0]?.col ?? [];
302
+
303
+ // Check width of column A (index 0, 1-based=1)
304
+ const colA = cols.find((c: any) => c.$.min === "1" && c.$.max === "1");
305
+ expect(colA).toBeDefined();
306
+ expect(colA.$.width).toBe("25");
307
+
308
+ // Check width of column C (index 2, 1-based=3)
309
+ const colC = cols.find((c: any) => c.$.min === "3" && c.$.max === "3");
310
+ expect(colC).toBeDefined();
311
+ expect(colC.$.width).toBe("30");
312
+ });
313
+ });
314
+
315
+ describe("Column access", () => {
316
+ it("should get all cells in column", async () => {
317
+ const wb = new ExcelWorkbook();
318
+ const ws = await wb.createWorksheet("Test");
319
+
320
+ await ws.cell(0, 0).setVal("A1");
321
+ await ws.cell(1, 0).setVal("A2");
322
+ await ws.cell(2, 0).setVal("A3");
323
+
324
+ const cells = await ws.col(0).getCells();
325
+ expect(cells.length).toBe(3);
326
+ expect(await cells[0].getVal()).toBe("A1");
327
+ expect(await cells[1].getVal()).toBe("A2");
328
+ expect(await cells[2].getVal()).toBe("A3");
329
+ });
330
+ });
331
+
332
+ describe("Data table edge cases", () => {
333
+ it("should return empty array when calling getDataTable on empty sheet", async () => {
334
+ const wb = new ExcelWorkbook();
335
+ const ws = await wb.createWorksheet("Empty");
336
+ const data = await ws.getDataTable();
337
+ expect(data).toEqual([]);
338
+ });
339
+
340
+ it("should return empty array when only headers exist without data", async () => {
341
+ const wb = new ExcelWorkbook();
342
+ const ws = await wb.createWorksheet("Test");
343
+ await ws.cell(0, 0).setVal("Header1");
344
+ await ws.cell(0, 1).setVal("Header2");
345
+ const data = await ws.getDataTable();
346
+ expect(data).toEqual([]);
347
+ });
348
+ });
349
+
350
+ describe("Data table options", () => {
351
+ it("should specify header row with headerRowIndex", async () => {
352
+ const wb = new ExcelWorkbook();
353
+ const ws = await wb.createWorksheet("Test");
354
+
355
+ // Row 0 is title
356
+ await ws.cell(0, 0).setVal("Title");
357
+ // Row 1 is header
358
+ await ws.cell(1, 0).setVal("Name");
359
+ await ws.cell(1, 1).setVal("Age");
360
+ // Data starts from row 2
361
+ await ws.cell(2, 0).setVal("Alice");
362
+ await ws.cell(2, 1).setVal(30);
363
+
364
+ const data = await ws.getDataTable({ headerRowIndex: 1 });
365
+ expect(data.length).toBe(1);
366
+ expect(data[0]["Name"]).toBe("Alice");
367
+ expect(data[0]["Age"]).toBe(30);
368
+ });
369
+
370
+ it("should detect data end with checkEndColIndex", async () => {
371
+ const wb = new ExcelWorkbook();
372
+ const ws = await wb.createWorksheet("Test");
373
+
374
+ await ws.cell(0, 0).setVal("Name");
375
+ await ws.cell(0, 1).setVal("Age");
376
+ await ws.cell(1, 0).setVal("Alice");
377
+ await ws.cell(1, 1).setVal(30);
378
+ await ws.cell(2, 0).setVal("Bob");
379
+ await ws.cell(2, 1).setVal(25);
380
+ // Row 3 has empty Name column -> data end
381
+ await ws.cell(3, 1).setVal(999);
382
+
383
+ const data = await ws.getDataTable({ checkEndColIndex: 0 });
384
+ expect(data.length).toBe(2);
385
+ expect(data[0]["Name"]).toBe("Alice");
386
+ expect(data[1]["Name"]).toBe("Bob");
387
+ });
388
+ });
389
+ });