@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.
Files changed (97) hide show
  1. package/README.md +66 -78
  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 +273 -264
  5. package/dist/excel-cell.js.map +1 -6
  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 +33 -35
  9. package/dist/excel-col.js.map +1 -6
  10. package/dist/excel-row.d.ts +3 -3
  11. package/dist/excel-row.d.ts.map +1 -1
  12. package/dist/excel-row.js +28 -30
  13. package/dist/excel-row.js.map +1 -6
  14. package/dist/excel-workbook.d.ts +23 -23
  15. package/dist/excel-workbook.d.ts.map +1 -1
  16. package/dist/excel-workbook.js +151 -125
  17. package/dist/excel-workbook.js.map +1 -6
  18. package/dist/excel-worksheet.d.ts +32 -32
  19. package/dist/excel-worksheet.d.ts.map +1 -1
  20. package/dist/excel-worksheet.js +281 -253
  21. package/dist/excel-worksheet.js.map +1 -6
  22. package/dist/excel-wrapper.d.ts +7 -7
  23. package/dist/excel-wrapper.d.ts.map +1 -1
  24. package/dist/excel-wrapper.js +190 -226
  25. package/dist/excel-wrapper.js.map +1 -6
  26. package/dist/index.js +4 -1
  27. package/dist/index.js.map +1 -6
  28. package/dist/types.d.ts +13 -13
  29. package/dist/types.d.ts.map +1 -1
  30. package/dist/types.js +3 -1
  31. package/dist/types.js.map +1 -6
  32. package/dist/utils/excel-utils.d.ts +23 -23
  33. package/dist/utils/excel-utils.d.ts.map +1 -1
  34. package/dist/utils/excel-utils.js +183 -151
  35. package/dist/utils/excel-utils.js.map +1 -6
  36. package/dist/utils/zip-cache.d.ts +6 -6
  37. package/dist/utils/zip-cache.js +78 -60
  38. package/dist/utils/zip-cache.js.map +1 -6
  39. package/dist/xml/excel-xml-content-type.d.ts +2 -2
  40. package/dist/xml/excel-xml-content-type.js +55 -53
  41. package/dist/xml/excel-xml-content-type.js.map +1 -6
  42. package/dist/xml/excel-xml-drawing.d.ts +2 -2
  43. package/dist/xml/excel-xml-drawing.js +74 -73
  44. package/dist/xml/excel-xml-drawing.js.map +1 -6
  45. package/dist/xml/excel-xml-relationship.d.ts +2 -2
  46. package/dist/xml/excel-xml-relationship.js +67 -67
  47. package/dist/xml/excel-xml-relationship.js.map +1 -6
  48. package/dist/xml/excel-xml-shared-string.d.ts +2 -2
  49. package/dist/xml/excel-xml-shared-string.js +57 -55
  50. package/dist/xml/excel-xml-shared-string.js.map +1 -6
  51. package/dist/xml/excel-xml-style.d.ts +2 -2
  52. package/dist/xml/excel-xml-style.js +311 -295
  53. package/dist/xml/excel-xml-style.js.map +1 -6
  54. package/dist/xml/excel-xml-unknown.d.ts +2 -2
  55. package/dist/xml/excel-xml-unknown.js +11 -10
  56. package/dist/xml/excel-xml-unknown.js.map +1 -6
  57. package/dist/xml/excel-xml-workbook.d.ts +3 -2
  58. package/dist/xml/excel-xml-workbook.d.ts.map +1 -1
  59. package/dist/xml/excel-xml-workbook.js +95 -90
  60. package/dist/xml/excel-xml-workbook.js.map +1 -6
  61. package/dist/xml/excel-xml-worksheet.d.ts +6 -6
  62. package/dist/xml/excel-xml-worksheet.js +450 -393
  63. package/dist/xml/excel-xml-worksheet.js.map +1 -6
  64. package/docs/core.md +147 -0
  65. package/docs/types.md +201 -212
  66. package/docs/wrapper.md +32 -62
  67. package/package.json +8 -6
  68. package/src/excel-cell.ts +36 -36
  69. package/src/excel-col.ts +4 -4
  70. package/src/excel-row.ts +3 -3
  71. package/src/excel-workbook.ts +38 -38
  72. package/src/excel-worksheet.ts +69 -51
  73. package/src/excel-wrapper.ts +55 -50
  74. package/src/index.ts +3 -3
  75. package/src/types.ts +17 -17
  76. package/src/utils/excel-utils.ts +47 -41
  77. package/src/utils/zip-cache.ts +6 -6
  78. package/src/xml/excel-xml-content-type.ts +3 -3
  79. package/src/xml/excel-xml-drawing.ts +2 -2
  80. package/src/xml/excel-xml-relationship.ts +3 -3
  81. package/src/xml/excel-xml-shared-string.ts +2 -2
  82. package/src/xml/excel-xml-style.ts +13 -13
  83. package/src/xml/excel-xml-unknown.ts +2 -2
  84. package/src/xml/excel-xml-workbook.ts +14 -6
  85. package/src/xml/excel-xml-worksheet.ts +43 -43
  86. package/docs/core-classes.md +0 -541
  87. package/docs/utilities.md +0 -128
  88. package/tests/excel-cell.spec.ts +0 -393
  89. package/tests/excel-col.spec.ts +0 -81
  90. package/tests/excel-row.spec.ts +0 -61
  91. package/tests/excel-workbook.spec.ts +0 -205
  92. package/tests/excel-worksheet.spec.ts +0 -469
  93. package/tests/excel-wrapper.spec.ts +0 -273
  94. package/tests/fixtures/logo.png +0 -0
  95. package/tests/fixtures//354/264/210/352/270/260/355/231/224.xlsx +0 -0
  96. package/tests/image-insert.spec.ts +0 -190
  97. 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
- });