@lotics/xlsx 0.1.0
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/package.json +30 -0
- package/src/auto_sum.test.ts +71 -0
- package/src/auto_sum.ts +83 -0
- package/src/canvas_cf.ts +135 -0
- package/src/canvas_chart.ts +687 -0
- package/src/canvas_fill.ts +156 -0
- package/src/canvas_images.ts +99 -0
- package/src/canvas_layout.test.ts +159 -0
- package/src/canvas_layout.ts +239 -0
- package/src/canvas_renderer.ts +1010 -0
- package/src/canvas_shape.ts +242 -0
- package/src/canvas_sparkline.ts +163 -0
- package/src/canvas_text.ts +454 -0
- package/src/cell_editor.ts +558 -0
- package/src/clipboard.test.ts +145 -0
- package/src/clipboard.ts +341 -0
- package/src/command_history.ts +102 -0
- package/src/csv_parser.test.ts +130 -0
- package/src/csv_parser.ts +172 -0
- package/src/data_validation.test.ts +95 -0
- package/src/data_validation.ts +114 -0
- package/src/editing_layer.test.ts +309 -0
- package/src/excel_cf_evaluate.test.ts +293 -0
- package/src/excel_cf_evaluate.ts +719 -0
- package/src/excel_cf_icons.ts +108 -0
- package/src/excel_cf_types.ts +67 -0
- package/src/excel_numfmt.test.ts +607 -0
- package/src/excel_numfmt.ts +1033 -0
- package/src/excel_parser.test.ts +1061 -0
- package/src/excel_parser.ts +393 -0
- package/src/excel_pattern_fills.ts +64 -0
- package/src/excel_table_styles.ts +160 -0
- package/src/excel_utils.test.ts +108 -0
- package/src/excel_utils.ts +162 -0
- package/src/fast-formula-parser.d.ts +53 -0
- package/src/fill_logic.ts +257 -0
- package/src/fill_patterns.test.ts +90 -0
- package/src/fill_patterns.ts +71 -0
- package/src/flash_fill.test.ts +75 -0
- package/src/flash_fill.ts +221 -0
- package/src/formula_deps.ts +189 -0
- package/src/formula_engine.test.ts +348 -0
- package/src/formula_engine.ts +401 -0
- package/src/formula_highlight.test.ts +81 -0
- package/src/formula_highlight.ts +139 -0
- package/src/formula_suggest.ts +100 -0
- package/src/hidden_sheets.test.ts +49 -0
- package/src/index.ts +149 -0
- package/src/keyboard_nav.test.ts +394 -0
- package/src/keyboard_nav.ts +891 -0
- package/src/move_logic.ts +63 -0
- package/src/numfmt_type.test.ts +158 -0
- package/src/numfmt_type.ts +231 -0
- package/src/ooxml_cell_format.ts +70 -0
- package/src/ooxml_chart.test.ts +85 -0
- package/src/ooxml_chart.ts +347 -0
- package/src/ooxml_chart_types.ts +207 -0
- package/src/ooxml_color.ts +339 -0
- package/src/ooxml_drawing.test.ts +87 -0
- package/src/ooxml_drawing.ts +287 -0
- package/src/ooxml_pivot.test.ts +195 -0
- package/src/ooxml_pivot.ts +468 -0
- package/src/ooxml_pivot_types.ts +165 -0
- package/src/ooxml_shape.ts +355 -0
- package/src/ooxml_sheet.ts +1271 -0
- package/src/ooxml_styles.ts +556 -0
- package/src/ooxml_table.test.ts +70 -0
- package/src/ooxml_table.ts +131 -0
- package/src/ooxml_workbook.test.ts +40 -0
- package/src/ooxml_workbook.ts +259 -0
- package/src/ooxml_zip.ts +33 -0
- package/src/pivot_model.ts +237 -0
- package/src/pivot_recompute.test.ts +210 -0
- package/src/pivot_recompute.ts +413 -0
- package/src/selection_model.ts +364 -0
- package/src/spreadsheet_commands.test.ts +772 -0
- package/src/spreadsheet_commands.ts +1805 -0
- package/src/structured_refs.test.ts +128 -0
- package/src/structured_refs.ts +160 -0
- package/src/style_helpers.test.ts +33 -0
- package/src/style_helpers.ts +30 -0
- package/src/template_builder.test.ts +139 -0
- package/src/template_builder.ts +36 -0
- package/src/types.ts +239 -0
- package/src/workbook_model.test.ts +536 -0
- package/src/workbook_model.ts +911 -0
- package/src/xlsx_round_trip.test.ts +279 -0
- package/src/xlsx_writer.test.ts +488 -0
- package/src/xlsx_writer.ts +1549 -0
- package/src/xml_entities.ts +23 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { DependencyGraph } from "./formula_deps";
|
|
2
|
+
import { FormulaEngine, recomputeAll, recomputeIfStale } from "./formula_engine";
|
|
3
|
+
import { WorkbookModel, SheetModel, StyleRegistry } from "./workbook_model";
|
|
4
|
+
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// DependencyGraph
|
|
7
|
+
// =============================================================================
|
|
8
|
+
|
|
9
|
+
describe("DependencyGraph", () => {
|
|
10
|
+
test("tracks direct dependents", () => {
|
|
11
|
+
const g = new DependencyGraph();
|
|
12
|
+
// B1 depends on A1
|
|
13
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
14
|
+
expect(g.getDirectDependents("Sheet1!A1")).toEqual(["Sheet1!B1"]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("tracks multiple dependents", () => {
|
|
18
|
+
const g = new DependencyGraph();
|
|
19
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
20
|
+
g.setDependencies("Sheet1!C1", ["Sheet1!A1"]);
|
|
21
|
+
const deps = g.getDirectDependents("Sheet1!A1");
|
|
22
|
+
expect(deps).toHaveLength(2);
|
|
23
|
+
expect(deps).toContain("Sheet1!B1");
|
|
24
|
+
expect(deps).toContain("Sheet1!C1");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("removeCell cleans up edges", () => {
|
|
28
|
+
const g = new DependencyGraph();
|
|
29
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
30
|
+
g.removeCell("Sheet1!B1");
|
|
31
|
+
expect(g.getDirectDependents("Sheet1!A1")).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("setDependencies replaces old edges", () => {
|
|
35
|
+
const g = new DependencyGraph();
|
|
36
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
37
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!C1"]);
|
|
38
|
+
expect(g.getDirectDependents("Sheet1!A1")).toEqual([]);
|
|
39
|
+
expect(g.getDirectDependents("Sheet1!C1")).toEqual(["Sheet1!B1"]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("getRecalcOrder returns topological order", () => {
|
|
43
|
+
const g = new DependencyGraph();
|
|
44
|
+
// A1 → B1 → C1 (C1 depends on B1 which depends on A1)
|
|
45
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
46
|
+
g.setDependencies("Sheet1!C1", ["Sheet1!B1"]);
|
|
47
|
+
|
|
48
|
+
const order = g.getRecalcOrder(["Sheet1!A1"]);
|
|
49
|
+
expect(order).toEqual(["Sheet1!B1", "Sheet1!C1"]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("getRecalcOrder handles diamond dependencies", () => {
|
|
53
|
+
const g = new DependencyGraph();
|
|
54
|
+
// A1 → B1, A1 → C1, B1 → D1, C1 → D1
|
|
55
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
56
|
+
g.setDependencies("Sheet1!C1", ["Sheet1!A1"]);
|
|
57
|
+
g.setDependencies("Sheet1!D1", ["Sheet1!B1", "Sheet1!C1"]);
|
|
58
|
+
|
|
59
|
+
const order = g.getRecalcOrder(["Sheet1!A1"]);
|
|
60
|
+
expect(order).toHaveLength(3);
|
|
61
|
+
// D1 must come after both B1 and C1
|
|
62
|
+
expect(order.indexOf("Sheet1!D1")).toBeGreaterThan(order.indexOf("Sheet1!B1"));
|
|
63
|
+
expect(order.indexOf("Sheet1!D1")).toBeGreaterThan(order.indexOf("Sheet1!C1"));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("getRecalcOrder returns empty for no dependents", () => {
|
|
67
|
+
const g = new DependencyGraph();
|
|
68
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
69
|
+
expect(g.getRecalcOrder(["Sheet1!C1"])).toEqual([]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("detectCycle finds simple cycle", () => {
|
|
73
|
+
const g = new DependencyGraph();
|
|
74
|
+
// A1 → B1 → A1 (circular)
|
|
75
|
+
g.setDependencies("Sheet1!A1", ["Sheet1!B1"]);
|
|
76
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
77
|
+
|
|
78
|
+
const cycle = g.detectCycle("Sheet1!A1");
|
|
79
|
+
expect(cycle).not.toBeNull();
|
|
80
|
+
expect(cycle).toContain("Sheet1!A1");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("detectCycle returns null for no cycle", () => {
|
|
84
|
+
const g = new DependencyGraph();
|
|
85
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
86
|
+
expect(g.detectCycle("Sheet1!B1")).toBeNull();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("clear resets the graph", () => {
|
|
90
|
+
const g = new DependencyGraph();
|
|
91
|
+
g.setDependencies("Sheet1!B1", ["Sheet1!A1"]);
|
|
92
|
+
g.clear();
|
|
93
|
+
expect(g.size).toBe(0);
|
|
94
|
+
expect(g.getDirectDependents("Sheet1!A1")).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// =============================================================================
|
|
99
|
+
// FormulaEngine integration
|
|
100
|
+
// =============================================================================
|
|
101
|
+
|
|
102
|
+
function makeWorkbook(): WorkbookModel {
|
|
103
|
+
const styles = new StyleRegistry();
|
|
104
|
+
const sheet = new SheetModel("Sheet1");
|
|
105
|
+
return new WorkbookModel({ sheets: [sheet], activeSheetIndex: 0, styles });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
describe("FormulaEngine", () => {
|
|
109
|
+
test("evaluates SUM formula", () => {
|
|
110
|
+
const wb = makeWorkbook();
|
|
111
|
+
const sheet = wb.sheets[0];
|
|
112
|
+
sheet.set("A1", 10);
|
|
113
|
+
sheet.set("A2", 20);
|
|
114
|
+
sheet.set("A3", null, undefined, undefined, "SUM(A1:A2)");
|
|
115
|
+
|
|
116
|
+
const engine = new FormulaEngine(wb);
|
|
117
|
+
engine.buildGraph();
|
|
118
|
+
engine.evaluateMissing();
|
|
119
|
+
|
|
120
|
+
expect(sheet.getCell("A3")?.value).toBe(30);
|
|
121
|
+
expect(sheet.getCell("A3")?.displayValue).toBe("30");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("evaluates IF formula", () => {
|
|
125
|
+
const wb = makeWorkbook();
|
|
126
|
+
const sheet = wb.sheets[0];
|
|
127
|
+
sheet.set("A1", 10);
|
|
128
|
+
sheet.set("B1", null, undefined, undefined, 'IF(A1>5,"big","small")');
|
|
129
|
+
|
|
130
|
+
const engine = new FormulaEngine(wb);
|
|
131
|
+
engine.buildGraph();
|
|
132
|
+
engine.evaluateMissing();
|
|
133
|
+
|
|
134
|
+
expect(sheet.getCell("B1")?.value).toBe("big");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("recalculates dependents on cell change", () => {
|
|
138
|
+
const wb = makeWorkbook();
|
|
139
|
+
const sheet = wb.sheets[0];
|
|
140
|
+
sheet.set("A1", 10);
|
|
141
|
+
sheet.set("B1", null, undefined, undefined, "A1*2");
|
|
142
|
+
|
|
143
|
+
const engine = new FormulaEngine(wb);
|
|
144
|
+
engine.buildGraph();
|
|
145
|
+
engine.evaluateMissing();
|
|
146
|
+
|
|
147
|
+
expect(sheet.getCell("B1")?.value).toBe(20);
|
|
148
|
+
|
|
149
|
+
// Change A1
|
|
150
|
+
sheet.set("A1", 50);
|
|
151
|
+
engine.onCellChanged("Sheet1", "A1");
|
|
152
|
+
|
|
153
|
+
expect(sheet.getCell("B1")?.value).toBe(100);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("builds dependency graph from formula cells", () => {
|
|
157
|
+
const wb = makeWorkbook();
|
|
158
|
+
const sheet = wb.sheets[0];
|
|
159
|
+
sheet.set("A1", 1);
|
|
160
|
+
sheet.set("A2", 2);
|
|
161
|
+
sheet.set("A3", null, undefined, undefined, "SUM(A1:A2)");
|
|
162
|
+
|
|
163
|
+
const engine = new FormulaEngine(wb);
|
|
164
|
+
engine.buildGraph();
|
|
165
|
+
|
|
166
|
+
expect(engine.deps.size).toBeGreaterThan(0);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("handles formula errors gracefully", () => {
|
|
170
|
+
const wb = makeWorkbook();
|
|
171
|
+
const sheet = wb.sheets[0];
|
|
172
|
+
sheet.set("A1", null, undefined, undefined, "1/0");
|
|
173
|
+
|
|
174
|
+
const engine = new FormulaEngine(wb);
|
|
175
|
+
engine.buildGraph();
|
|
176
|
+
engine.evaluateMissing();
|
|
177
|
+
|
|
178
|
+
// Should not throw. fast-formula-parser returns #DIV/0! FormulaError for 1/0.
|
|
179
|
+
const cell = sheet.getCell("A1");
|
|
180
|
+
expect(cell).toBeDefined();
|
|
181
|
+
expect(cell?.error).toBe("#DIV/0!");
|
|
182
|
+
expect(cell?.displayValue).toBe("#DIV/0!");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("chain recalculation: A1 → B1 → C1", () => {
|
|
186
|
+
const wb = makeWorkbook();
|
|
187
|
+
const sheet = wb.sheets[0];
|
|
188
|
+
sheet.set("A1", 5);
|
|
189
|
+
sheet.set("B1", null, undefined, undefined, "A1+10");
|
|
190
|
+
sheet.set("C1", null, undefined, undefined, "B1*2");
|
|
191
|
+
|
|
192
|
+
const engine = new FormulaEngine(wb);
|
|
193
|
+
engine.buildGraph();
|
|
194
|
+
engine.evaluateMissing();
|
|
195
|
+
|
|
196
|
+
expect(sheet.getCell("B1")?.value).toBe(15);
|
|
197
|
+
expect(sheet.getCell("C1")?.value).toBe(30);
|
|
198
|
+
|
|
199
|
+
// Change A1
|
|
200
|
+
sheet.set("A1", 100);
|
|
201
|
+
engine.onCellChanged("Sheet1", "A1");
|
|
202
|
+
|
|
203
|
+
expect(sheet.getCell("B1")?.value).toBe(110);
|
|
204
|
+
expect(sheet.getCell("C1")?.value).toBe(220);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("new formula cell gets dependency tracking", () => {
|
|
208
|
+
const wb = makeWorkbook();
|
|
209
|
+
const sheet = wb.sheets[0];
|
|
210
|
+
sheet.set("A1", 10);
|
|
211
|
+
|
|
212
|
+
const engine = new FormulaEngine(wb);
|
|
213
|
+
engine.buildGraph();
|
|
214
|
+
|
|
215
|
+
// Add a new formula
|
|
216
|
+
sheet.set("B1", null, undefined, undefined, "A1+5");
|
|
217
|
+
engine.onCellChanged("Sheet1", "B1", "A1+5");
|
|
218
|
+
|
|
219
|
+
expect(sheet.getCell("B1")?.value).toBe(15);
|
|
220
|
+
|
|
221
|
+
// Change A1 — B1 should recalculate
|
|
222
|
+
sheet.set("A1", 20);
|
|
223
|
+
engine.onCellChanged("Sheet1", "A1");
|
|
224
|
+
|
|
225
|
+
expect(sheet.getCell("B1")?.value).toBe(25);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("preserves cached values for cells with values", () => {
|
|
229
|
+
const wb = makeWorkbook();
|
|
230
|
+
const sheet = wb.sheets[0];
|
|
231
|
+
// Cell with both a formula and a cached value
|
|
232
|
+
sheet.set("A1", 10);
|
|
233
|
+
sheet.set("B1", 42, undefined, undefined, "A1+32");
|
|
234
|
+
|
|
235
|
+
const engine = new FormulaEngine(wb);
|
|
236
|
+
engine.buildGraph();
|
|
237
|
+
engine.evaluateMissing(); // Should NOT re-evaluate B1 since it has a cached value
|
|
238
|
+
|
|
239
|
+
// B1 should keep its cached value
|
|
240
|
+
expect(sheet.getCell("B1")?.value).toBe(42);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("evaluateAll overwrites stale cached values — template-generation case", () => {
|
|
244
|
+
// Regression: template placeholder substitution rewrites the cells a
|
|
245
|
+
// formula references but leaves the formula's own `<v>` cache
|
|
246
|
+
// untouched. Real Excel recomputes on open; our renderer trusts the
|
|
247
|
+
// cache and shows the stale value (often a zero rendered via the
|
|
248
|
+
// zero-section of the numFmt, e.g. `"-"`). evaluateAll brings every
|
|
249
|
+
// formula back into sync with current cell values.
|
|
250
|
+
const wb = makeWorkbook();
|
|
251
|
+
const sheet = wb.sheets[0];
|
|
252
|
+
sheet.set("A1", 100);
|
|
253
|
+
sheet.set("A2", 200);
|
|
254
|
+
// Stale cache: the formula reads A1+A2 (=300) but the writer left an
|
|
255
|
+
// old value from a different input set.
|
|
256
|
+
sheet.set("B1", 0, undefined, undefined, "A1+A2");
|
|
257
|
+
|
|
258
|
+
const engine = new FormulaEngine(wb);
|
|
259
|
+
engine.buildGraph();
|
|
260
|
+
engine.evaluateAll();
|
|
261
|
+
|
|
262
|
+
expect(sheet.getCell("B1")?.value).toBe(300);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("recomputeAll always overwrites cached values", () => {
|
|
266
|
+
const wb = makeWorkbook();
|
|
267
|
+
wb.sheets[0].set("A1", 10);
|
|
268
|
+
wb.sheets[0].set("B1", 99, undefined, undefined, "A1*2");
|
|
269
|
+
|
|
270
|
+
// No flag needed — recomputeAll is unconditional.
|
|
271
|
+
recomputeAll(wb);
|
|
272
|
+
expect(wb.sheets[0].getCell("B1")?.value).toBe(20);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("recomputeIfStale fires iff workbook.fullCalcOnLoad is set", () => {
|
|
276
|
+
// No flag → cached stale value preserved (engine never runs).
|
|
277
|
+
const wbA = makeWorkbook();
|
|
278
|
+
wbA.sheets[0].set("A1", 10);
|
|
279
|
+
wbA.sheets[0].set("B1", 99, undefined, undefined, "A1*2");
|
|
280
|
+
wbA.fullCalcOnLoad = false;
|
|
281
|
+
recomputeIfStale(wbA);
|
|
282
|
+
expect(wbA.sheets[0].getCell("B1")?.value).toBe(99);
|
|
283
|
+
|
|
284
|
+
// Flag set → cached value overwritten by live computation.
|
|
285
|
+
const wbB = makeWorkbook();
|
|
286
|
+
wbB.sheets[0].set("A1", 10);
|
|
287
|
+
wbB.sheets[0].set("B1", 99, undefined, undefined, "A1*2");
|
|
288
|
+
wbB.fullCalcOnLoad = true;
|
|
289
|
+
recomputeIfStale(wbB);
|
|
290
|
+
expect(wbB.sheets[0].getCell("B1")?.value).toBe(20);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("evaluateAll recomputes SUM over numeric cells after range expansion", () => {
|
|
294
|
+
// Matches the VN báo giá pipeline: template row `SUM(H8:H8)` is copied
|
|
295
|
+
// to `SUM(H19:H19)` when the items loop expands, then the data cells
|
|
296
|
+
// get numeric values from the agent's payload. The formula's cached
|
|
297
|
+
// value stays `0` until someone evaluates it.
|
|
298
|
+
const wb = makeWorkbook();
|
|
299
|
+
const sheet = wb.sheets[0];
|
|
300
|
+
sheet.set("H19", 15425000);
|
|
301
|
+
sheet.set("H20", 0, undefined, undefined, "SUM(H19:H19)");
|
|
302
|
+
|
|
303
|
+
const engine = new FormulaEngine(wb);
|
|
304
|
+
engine.buildGraph();
|
|
305
|
+
engine.evaluateAll();
|
|
306
|
+
|
|
307
|
+
expect(sheet.getCell("H20")?.value).toBe(15425000);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("evaluateExpression resolves cell references", () => {
|
|
311
|
+
const wb = makeWorkbook();
|
|
312
|
+
const sheet = wb.sheets[0];
|
|
313
|
+
sheet.set("C5", 999);
|
|
314
|
+
|
|
315
|
+
const engine = new FormulaEngine(wb);
|
|
316
|
+
const result = engine.evaluateExpression("C5+1", "Sheet1", 1, 1);
|
|
317
|
+
expect(result).toBe(1000);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("evaluateExpression handles SUM over range", () => {
|
|
321
|
+
const wb = makeWorkbook();
|
|
322
|
+
const sheet = wb.sheets[0];
|
|
323
|
+
sheet.set("A1", 10);
|
|
324
|
+
sheet.set("A2", 20);
|
|
325
|
+
sheet.set("A3", 30);
|
|
326
|
+
|
|
327
|
+
const engine = new FormulaEngine(wb);
|
|
328
|
+
const result = engine.evaluateExpression("SUM(A1:A3)", "Sheet1", 1, 1);
|
|
329
|
+
expect(result).toBe(60);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test("evaluateExpression returns formula result", () => {
|
|
333
|
+
const wb = makeWorkbook();
|
|
334
|
+
const sheet = wb.sheets[0];
|
|
335
|
+
sheet.set("A1", 150);
|
|
336
|
+
|
|
337
|
+
const engine = new FormulaEngine(wb);
|
|
338
|
+
const result = engine.evaluateExpression("A1>100", "Sheet1", 1, 1);
|
|
339
|
+
expect(result).toBe(true);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test("evaluateExpression returns null for errors", () => {
|
|
343
|
+
const wb = makeWorkbook();
|
|
344
|
+
const engine = new FormulaEngine(wb);
|
|
345
|
+
const result = engine.evaluateExpression("NONEXISTENT()", "Sheet1", 1, 1);
|
|
346
|
+
expect(result).toBeNull();
|
|
347
|
+
});
|
|
348
|
+
});
|