@veltra/sheet 2.0.0 → 2.1.1
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/dist/index.d.ts +4 -25
- package/dist/index.js +3 -22
- package/dist/tools/builtin.js +279 -8
- package/dist/tools/context.d.ts +54 -11
- package/dist/tools/context.js +28 -5
- package/dist/tools/download.js +68 -0
- package/dist/tools/export.worker.d.ts +16 -0
- package/dist/tools/export.worker.js +29 -0
- package/dist/tools/registry.d.ts +7 -4
- package/dist/tools/registry.js +1 -3
- package/dist/types/index.d.ts +1 -0
- package/dist/types/sheet.d.ts +6 -5
- package/dist/vue/formula-bar.js +393 -0
- package/dist/vue/formula-suggest-list.js +66 -0
- package/dist/vue/import-file.js +140 -0
- package/dist/vue/insert-count-menu-item.js +68 -0
- package/dist/vue/insert-image.js +79 -0
- package/dist/vue/popup-helpers.js +73 -0
- package/dist/vue/popups/border-popup.js +75 -0
- package/dist/vue/popups/border-preset-glyph.js +106 -0
- package/dist/vue/popups/export-popup.js +51 -0
- package/dist/vue/popups/fill-color-popup.js +37 -0
- package/dist/vue/popups/find-popup.js +113 -0
- package/dist/vue/popups/font-color-popup.js +37 -0
- package/dist/vue/popups/font-size-popup.js +63 -0
- package/dist/vue/popups/import.worker.d.ts +21 -0
- package/dist/vue/popups/import.worker.js +41 -0
- package/dist/vue/popups/insert-image-popup.js +38 -0
- package/dist/vue/sheet-context-menu.js +200 -0
- package/dist/vue/sheet-tabs.js +162 -0
- package/dist/vue/sheet-toolbar.js +106 -0
- package/dist/vue/sheet.js +144 -138
- package/dist/vue/style.d.ts +11 -1
- package/dist/vue/style.js +11 -0
- package/dist/vue/style2.css +564 -3
- package/dist/vue/use-find-replace.js +161 -0
- package/dist/vue/use-formula-ref-select.js +37 -0
- package/dist/vue/use-formula-suggest.js +126 -0
- package/dist/vue/use-sheet-grid.js +180 -0
- package/dist/vue/use-sheet-state.js +122 -0
- package/dist/vue/use-sheet-tabs-bar.js +90 -0
- package/dist/vue/use-tool-groups.js +42 -0
- package/dist/vue/use-tool-popup.js +123 -0
- package/dist/vue/use-toolbar-scroll.js +72 -0
- package/package.json +12 -6
- package/src/env.d.ts +14 -0
- package/src/index.ts +203 -13
- package/src/tools/__test__/builtin.test.ts +208 -66
- package/src/tools/__test__/context.test.ts +62 -2
- package/src/tools/builtin.ts +350 -6
- package/src/tools/context.ts +89 -11
- package/src/tools/download.ts +82 -0
- package/src/tools/export.worker.ts +61 -0
- package/src/tools/registry.ts +16 -1
- package/src/types/index.ts +6 -0
- package/src/types/sheet.ts +5 -3
- package/src/vue/__test__/formula-bar.test.ts +455 -0
- package/src/vue/__test__/import-file.test.ts +183 -0
- package/src/vue/__test__/insert-count-menu-item.test.ts +123 -0
- package/src/vue/__test__/insert-image-popup.test.ts +89 -0
- package/src/vue/__test__/insert-image.test.ts +100 -0
- package/src/vue/__test__/sheet-component.test.ts +716 -27
- package/src/vue/__test__/sheet-context-menu.test.ts +206 -0
- package/src/vue/__test__/use-formula-ref-select.test.ts +35 -0
- package/src/vue/__test__/use-formula-suggest.test.ts +83 -0
- package/src/vue/formula-bar.vue +473 -0
- package/src/vue/formula-suggest-list.vue +64 -0
- package/src/vue/import-file.ts +173 -0
- package/src/vue/insert-count-menu-item.vue +69 -0
- package/src/vue/insert-image.ts +90 -0
- package/src/vue/parsing.ts +11 -0
- package/src/vue/popup-helpers.ts +50 -0
- package/src/vue/popups/border-popup.vue +93 -0
- package/src/vue/popups/border-preset-glyph.vue +111 -0
- package/src/vue/popups/export-popup.vue +52 -0
- package/src/vue/popups/fill-color-popup.vue +39 -0
- package/src/vue/popups/find-popup.vue +111 -0
- package/src/vue/popups/font-color-popup.vue +39 -0
- package/src/vue/popups/font-size-popup.vue +52 -0
- package/src/vue/popups/import.worker.ts +66 -0
- package/src/vue/popups/insert-image-popup.vue +39 -0
- package/src/vue/sheet-context-menu.ts +229 -0
- package/src/vue/sheet-tabs.vue +162 -0
- package/src/vue/sheet-toolbar.vue +91 -0
- package/src/vue/sheet.vue +163 -148
- package/src/vue/style.scss +646 -3
- package/src/vue/style.ts +12 -0
- package/src/vue/use-find-replace.ts +200 -0
- package/src/vue/use-formula-ref-select.ts +34 -0
- package/src/vue/use-formula-suggest.ts +150 -0
- package/src/vue/use-sheet-grid.ts +243 -0
- package/src/vue/use-sheet-state.ts +161 -0
- package/src/vue/use-sheet-tabs-bar.ts +115 -0
- package/src/vue/use-tool-groups.ts +61 -0
- package/src/vue/use-tool-popup.ts +150 -0
- package/src/vue/use-toolbar-scroll.ts +95 -0
- package/dist/core/address.d.ts +0 -60
- package/dist/core/address.js +0 -142
- package/dist/core/address.js.map +0 -1
- package/dist/core/cell-store.d.ts +0 -61
- package/dist/core/cell-store.js +0 -135
- package/dist/core/cell-store.js.map +0 -1
- package/dist/core/command/default-registry.d.ts +0 -10
- package/dist/core/command/default-registry.js +0 -18
- package/dist/core/command/default-registry.js.map +0 -1
- package/dist/core/command/history.d.ts +0 -64
- package/dist/core/command/history.js +0 -127
- package/dist/core/command/history.js.map +0 -1
- package/dist/core/command/merge-cells.d.ts +0 -21
- package/dist/core/command/merge-cells.js +0 -74
- package/dist/core/command/merge-cells.js.map +0 -1
- package/dist/core/command/registry.d.ts +0 -17
- package/dist/core/command/registry.js +0 -26
- package/dist/core/command/registry.js.map +0 -1
- package/dist/core/command/set-cell-formula.d.ts +0 -17
- package/dist/core/command/set-cell-formula.js +0 -29
- package/dist/core/command/set-cell-formula.js.map +0 -1
- package/dist/core/command/set-cell-value.d.ts +0 -22
- package/dist/core/command/set-cell-value.js +0 -35
- package/dist/core/command/set-cell-value.js.map +0 -1
- package/dist/core/command/types.d.ts +0 -68
- package/dist/core/events.js +0 -29
- package/dist/core/events.js.map +0 -1
- package/dist/core/formula/ast.d.ts +0 -58
- package/dist/core/formula/ast.js +0 -38
- package/dist/core/formula/ast.js.map +0 -1
- package/dist/core/formula/dependency-graph.d.ts +0 -70
- package/dist/core/formula/dependency-graph.js +0 -269
- package/dist/core/formula/dependency-graph.js.map +0 -1
- package/dist/core/formula/errors.d.ts +0 -25
- package/dist/core/formula/errors.js +0 -36
- package/dist/core/formula/errors.js.map +0 -1
- package/dist/core/formula/evaluator.d.ts +0 -41
- package/dist/core/formula/evaluator.js +0 -154
- package/dist/core/formula/evaluator.js.map +0 -1
- package/dist/core/formula/functions.d.ts +0 -30
- package/dist/core/formula/functions.js +0 -209
- package/dist/core/formula/functions.js.map +0 -1
- package/dist/core/formula/parser.d.ts +0 -7
- package/dist/core/formula/parser.js +0 -208
- package/dist/core/formula/parser.js.map +0 -1
- package/dist/core/formula/tokenizer.d.ts +0 -37
- package/dist/core/formula/tokenizer.js +0 -146
- package/dist/core/formula/tokenizer.js.map +0 -1
- package/dist/core/merge-manager.d.ts +0 -64
- package/dist/core/merge-manager.js +0 -111
- package/dist/core/merge-manager.js.map +0 -1
- package/dist/core/selection.d.ts +0 -31
- package/dist/core/selection.js +0 -62
- package/dist/core/selection.js.map +0 -1
- package/dist/core/sheet.d.ts +0 -105
- package/dist/core/sheet.js +0 -202
- package/dist/core/sheet.js.map +0 -1
- package/dist/core/workbook.d.ts +0 -43
- package/dist/core/workbook.js +0 -77
- package/dist/core/workbook.js.map +0 -1
- package/dist/grid/sheet-grid.d.ts +0 -59
- package/dist/grid/sheet-grid.js +0 -230
- package/dist/grid/sheet-grid.js.map +0 -1
- package/dist/tools/builtin.js.map +0 -1
- package/dist/tools/context.js.map +0 -1
- package/dist/tools/registry.js.map +0 -1
- package/dist/vue/index.d.ts +0 -2
- package/dist/vue/sheet.js.map +0 -1
- package/dist/vue/sheet.vue.d.ts +0 -24
- package/src/core/__test__/address.test.ts +0 -113
- package/src/core/__test__/cell-store.test.ts +0 -97
- package/src/core/__test__/command.test.ts +0 -377
- package/src/core/__test__/formula-adversarial.test.ts +0 -265
- package/src/core/__test__/formula-engine.test.ts +0 -470
- package/src/core/__test__/formula-functions.test.ts +0 -178
- package/src/core/__test__/formula-parser.test.ts +0 -160
- package/src/core/__test__/history.test.ts +0 -225
- package/src/core/__test__/merge-manager.test.ts +0 -106
- package/src/core/__test__/sheet.test.ts +0 -142
- package/src/core/address.ts +0 -166
- package/src/core/cell-store.ts +0 -173
- package/src/core/command/default-registry.ts +0 -15
- package/src/core/command/history.ts +0 -167
- package/src/core/command/index.ts +0 -7
- package/src/core/command/merge-cells.ts +0 -74
- package/src/core/command/registry.ts +0 -30
- package/src/core/command/set-cell-formula.ts +0 -29
- package/src/core/command/set-cell-value.ts +0 -38
- package/src/core/command/types.ts +0 -73
- package/src/core/events.ts +0 -37
- package/src/core/formula/ast.ts +0 -66
- package/src/core/formula/dependency-graph.ts +0 -329
- package/src/core/formula/errors.ts +0 -41
- package/src/core/formula/evaluator.ts +0 -219
- package/src/core/formula/functions.ts +0 -266
- package/src/core/formula/index.ts +0 -7
- package/src/core/formula/parser.ts +0 -198
- package/src/core/formula/tokenizer.ts +0 -160
- package/src/core/merge-manager.ts +0 -158
- package/src/core/selection.ts +0 -66
- package/src/core/sheet.ts +0 -257
- package/src/core/workbook.ts +0 -99
- package/src/grid/__test__/canvas-mock.ts +0 -49
- package/src/grid/__test__/setup.ts +0 -3
- package/src/grid/__test__/sheet-grid-formula.test.ts +0 -140
- package/src/grid/__test__/sheet-grid.test.ts +0 -177
- package/src/grid/sheet-grid.ts +0 -283
|
@@ -1,470 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { parseRange, type CellAddress } from '../address'
|
|
4
|
-
import { Sheet } from '../sheet'
|
|
5
|
-
import { Workbook } from '../workbook'
|
|
6
|
-
|
|
7
|
-
const A1 = { row: 0, col: 0 }
|
|
8
|
-
const B1 = { row: 0, col: 1 }
|
|
9
|
-
const C1 = { row: 0, col: 2 }
|
|
10
|
-
const D1 = { row: 0, col: 3 }
|
|
11
|
-
|
|
12
|
-
describe('依赖重算', () => {
|
|
13
|
-
it('改 A1 → 依赖它的 =A1*2 重算', () => {
|
|
14
|
-
const sheet = new Sheet()
|
|
15
|
-
sheet.setCellValue(A1, 3)
|
|
16
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
17
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 6 })
|
|
18
|
-
|
|
19
|
-
sheet.setCellValue(A1, 10)
|
|
20
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 20 })
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('间接依赖按拓扑序重算:A1 → B1(=A1*2) → C1(=B1+1)', () => {
|
|
24
|
-
const sheet = new Sheet()
|
|
25
|
-
sheet.setCellValue(A1, 1)
|
|
26
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
27
|
-
sheet.setCellFormula(C1, '=B1+1')
|
|
28
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 3 })
|
|
29
|
-
|
|
30
|
-
sheet.setCellValue(A1, 5)
|
|
31
|
-
// 若顺序错误(C1 先于 B1 重算)会拿到旧 B1 的 2+1=3
|
|
32
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 10 })
|
|
33
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 11 })
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('菱形依赖(D1=B1+C1,B1/C1 同依赖 A1)只算一次且结果正确', () => {
|
|
37
|
-
const sheet = new Sheet()
|
|
38
|
-
sheet.setCellValue(A1, 2)
|
|
39
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
40
|
-
sheet.setCellFormula(C1, '=A1*3')
|
|
41
|
-
sheet.setCellFormula(D1, '=B1+C1')
|
|
42
|
-
expect(sheet.getCellData(D1)).toMatchObject({ v: 10 })
|
|
43
|
-
|
|
44
|
-
sheet.setCellValue(A1, 4)
|
|
45
|
-
expect(sheet.getCellData(D1)).toMatchObject({ v: 20 })
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('连续改值,每次重算结果一致', () => {
|
|
49
|
-
const sheet = new Sheet()
|
|
50
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
51
|
-
sheet.setCellFormula(C1, '=B1+1')
|
|
52
|
-
for (const [input, b, c] of [
|
|
53
|
-
[1, 2, 3],
|
|
54
|
-
[2, 4, 5],
|
|
55
|
-
[3, 6, 7],
|
|
56
|
-
[2, 4, 5],
|
|
57
|
-
[0, 0, 1]
|
|
58
|
-
] as const) {
|
|
59
|
-
sheet.setCellValue(A1, input)
|
|
60
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: b })
|
|
61
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: c })
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('重算收敛时值未变的格不产生派生变更(不触发 cell-change)', () => {
|
|
66
|
-
const sheet = new Sheet()
|
|
67
|
-
sheet.setCellFormula(B1, '=A1*0+1')
|
|
68
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 1 })
|
|
69
|
-
|
|
70
|
-
const handler = vi.fn()
|
|
71
|
-
sheet.on('cell-change', handler)
|
|
72
|
-
sheet.setCellValue(A1, 99)
|
|
73
|
-
// 只有 A1 自身变更;B1 收敛(恒为 1)不应有事件
|
|
74
|
-
expect(handler).toHaveBeenCalledTimes(1)
|
|
75
|
-
expect(handler).toHaveBeenCalledWith({ addr: A1 })
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
describe('循环引用', () => {
|
|
80
|
-
it('A1==B1+1, B1==A1+1 → 双格 #CYCLE!;打破循环自动恢复', () => {
|
|
81
|
-
const sheet = new Sheet()
|
|
82
|
-
sheet.setCellFormula(A1, '=B1+1')
|
|
83
|
-
sheet.setCellFormula(B1, '=A1+1')
|
|
84
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: '#CYCLE!', t: 'e' })
|
|
85
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: '#CYCLE!', t: 'e' })
|
|
86
|
-
|
|
87
|
-
// 打破循环:B1 改为常量 → A1 恢复
|
|
88
|
-
sheet.setCellValue(B1, 5)
|
|
89
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: 6, t: 'n' })
|
|
90
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 5 })
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
it('环外格引用环上格 → 错误传播(自身不入环)', () => {
|
|
94
|
-
const sheet = new Sheet()
|
|
95
|
-
sheet.setCellFormula(A1, '=B1+1')
|
|
96
|
-
sheet.setCellFormula(B1, '=A1+1')
|
|
97
|
-
sheet.setCellFormula(C1, '=A1*2')
|
|
98
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: '#CYCLE!', t: 'e' })
|
|
99
|
-
|
|
100
|
-
// 打破循环后环外格一并恢复
|
|
101
|
-
sheet.setCellValue(B1, 2)
|
|
102
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: 3 })
|
|
103
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 6 })
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('自引用 A1==A1+1 → #CYCLE!,改常量后恢复', () => {
|
|
107
|
-
const sheet = new Sheet()
|
|
108
|
-
sheet.setCellFormula(A1, '=A1+1')
|
|
109
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: '#CYCLE!', t: 'e' })
|
|
110
|
-
|
|
111
|
-
sheet.setCellValue(A1, 10)
|
|
112
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: 10 })
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('跨表循环:Sheet1!A1 ↔ Sheet2!B1 → 双格 #CYCLE!,打破后恢复', () => {
|
|
116
|
-
const workbook = new Workbook()
|
|
117
|
-
const sheet1 = workbook.activeSheet
|
|
118
|
-
const sheet2 = workbook.addSheet('Sheet2')
|
|
119
|
-
sheet1.setCellFormula(A1, '=Sheet2!B1+1')
|
|
120
|
-
sheet2.setCellFormula(B1, '=Sheet1!A1+1')
|
|
121
|
-
expect(sheet1.getCellData(A1)).toMatchObject({ v: '#CYCLE!', t: 'e' })
|
|
122
|
-
expect(sheet2.getCellData(B1)).toMatchObject({ v: '#CYCLE!', t: 'e' })
|
|
123
|
-
|
|
124
|
-
sheet2.setCellValue(B1, 3)
|
|
125
|
-
expect(sheet1.getCellData(A1)).toMatchObject({ v: 4 })
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('循环期间其它无关变更不影响环状态', () => {
|
|
129
|
-
const sheet = new Sheet()
|
|
130
|
-
sheet.setCellFormula(A1, '=B1+1')
|
|
131
|
-
sheet.setCellFormula(B1, '=A1+D1')
|
|
132
|
-
sheet.setCellFormula(C1, '=A1+1')
|
|
133
|
-
// A1 → B1 → A1 成环(D1 是环上引用);C1 环外
|
|
134
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: '#CYCLE!' })
|
|
135
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: '#CYCLE!' })
|
|
136
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: '#CYCLE!' })
|
|
137
|
-
|
|
138
|
-
// 改 D1(环的输入)→ 重算后仍是环
|
|
139
|
-
sheet.setCellValue(D1, 10)
|
|
140
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: '#CYCLE!' })
|
|
141
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: '#CYCLE!' })
|
|
142
|
-
})
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
describe('跨表引用', () => {
|
|
146
|
-
it('Sheet2 值变更 → Sheet1 引用格实时重算;undo 一并还原', () => {
|
|
147
|
-
const workbook = new Workbook()
|
|
148
|
-
const sheet1 = workbook.activeSheet
|
|
149
|
-
const sheet2 = workbook.addSheet('Sheet2')
|
|
150
|
-
sheet2.setCellValue(A1, 10)
|
|
151
|
-
sheet1.setCellFormula(B1, '=Sheet2!A1*2')
|
|
152
|
-
expect(sheet1.getCellData(B1)).toMatchObject({ v: 20 })
|
|
153
|
-
|
|
154
|
-
sheet2.setCellValue(A1, 7)
|
|
155
|
-
expect(sheet1.getCellData(B1)).toMatchObject({ v: 14 })
|
|
156
|
-
|
|
157
|
-
// 变更发生在 Sheet2,单次 undo 同时还原 Sheet2 源格与 Sheet1 派生格
|
|
158
|
-
expect(sheet2.undo()).toBe(true)
|
|
159
|
-
expect(sheet2.getCellData(A1)).toMatchObject({ v: 10 })
|
|
160
|
-
expect(sheet1.getCellData(B1)).toMatchObject({ v: 20 })
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
it('引用不存在的 sheet → #REF!', () => {
|
|
164
|
-
const sheet = new Sheet()
|
|
165
|
-
sheet.setCellFormula(A1, '=Nope!B2')
|
|
166
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: '#REF!', t: 'e' })
|
|
167
|
-
sheet.setCellFormula(B1, "=SUM('No Sheet'!A1:B2)")
|
|
168
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: '#REF!', t: 'e' })
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it("跨表区域:'S2'!A1:B2 聚合", () => {
|
|
172
|
-
const workbook = new Workbook()
|
|
173
|
-
const sheet1 = workbook.activeSheet
|
|
174
|
-
const s2 = workbook.addSheet('S2')
|
|
175
|
-
s2.setCellValue(A1, 1)
|
|
176
|
-
s2.setCellValue({ row: 1, col: 1 }, 2)
|
|
177
|
-
sheet1.setCellFormula(A1, "=SUM('S2'!A1:B2)")
|
|
178
|
-
expect(sheet1.getCellData(A1)).toMatchObject({ v: 3 })
|
|
179
|
-
|
|
180
|
-
// 区域内空格变更也触发重算
|
|
181
|
-
s2.setCellValue({ row: 0, col: 1 }, 10)
|
|
182
|
-
expect(sheet1.getCellData(A1)).toMatchObject({ v: 13 })
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
it('删除被引用的 sheet:缓存仍旧值,下次重算 → #REF!(已知限制行为)', () => {
|
|
186
|
-
const workbook = new Workbook()
|
|
187
|
-
const sheet1 = workbook.activeSheet
|
|
188
|
-
const sheet2 = workbook.addSheet('Sheet2')
|
|
189
|
-
sheet2.setCellValue(A1, 10)
|
|
190
|
-
sheet1.setCellFormula(B1, '=Sheet2!A1+D1')
|
|
191
|
-
sheet1.setCellValue(D1, 1)
|
|
192
|
-
expect(sheet1.getCellData(B1)).toMatchObject({ v: 11 })
|
|
193
|
-
|
|
194
|
-
workbook.removeSheet('Sheet2')
|
|
195
|
-
expect(workbook.formulaGraph.getSheet('Sheet2')).toBeUndefined()
|
|
196
|
-
// 删除本身不触发重算(已知限制):缓存仍旧值
|
|
197
|
-
expect(sheet1.getCellData(B1)).toMatchObject({ v: 11 })
|
|
198
|
-
// 任意触发源变更 → 重算时表已不存在 → #REF!
|
|
199
|
-
sheet1.setCellValue(D1, 2)
|
|
200
|
-
expect(sheet1.getCellData(B1)).toMatchObject({ v: '#REF!', t: 'e' })
|
|
201
|
-
})
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
describe('错误值体系', () => {
|
|
205
|
-
it('三类错误:除零 #DIV/0!、类型错误 #VALUE!、未知函数 #NAME?', () => {
|
|
206
|
-
const sheet = new Sheet()
|
|
207
|
-
sheet.setCellFormula(A1, '=1/0')
|
|
208
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: '#DIV/0!', t: 'e' })
|
|
209
|
-
sheet.setCellFormula(B1, '=1+"abc"')
|
|
210
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
211
|
-
sheet.setCellFormula(C1, '=NOSUCHFN(1)')
|
|
212
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: '#NAME?', t: 'e' })
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
it('0 作除数与 0 的负幂 → #DIV/0!;非法幂 → #VALUE!', () => {
|
|
216
|
-
const sheet = new Sheet()
|
|
217
|
-
sheet.setCellFormula(A1, '=0/0')
|
|
218
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: '#DIV/0!' })
|
|
219
|
-
sheet.setCellFormula(B1, '=0^-1')
|
|
220
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: '#DIV/0!' })
|
|
221
|
-
sheet.setCellFormula(C1, '=(-1)^0.5')
|
|
222
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: '#VALUE!' })
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
it('错误沿依赖链传播,源修复后恢复', () => {
|
|
226
|
-
const sheet = new Sheet()
|
|
227
|
-
sheet.setCellFormula(A1, '=1/0')
|
|
228
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
229
|
-
sheet.setCellFormula(C1, '=B1+1')
|
|
230
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: '#DIV/0!' })
|
|
231
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: '#DIV/0!' })
|
|
232
|
-
|
|
233
|
-
sheet.setCellValue(A1, 5)
|
|
234
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 10 })
|
|
235
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 11 })
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
it('区域直接作为公式结果 → #VALUE!', () => {
|
|
239
|
-
const sheet = new Sheet()
|
|
240
|
-
sheet.setCellValue(A1, 1)
|
|
241
|
-
sheet.setCellFormula(B1, '=A1:A3')
|
|
242
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
243
|
-
})
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
describe('undo/redo 集成', () => {
|
|
247
|
-
it('改值波及 10 格公式链 → 单次 undo 全部还原', () => {
|
|
248
|
-
const sheet = new Sheet()
|
|
249
|
-
sheet.setCellValue(A1, 1)
|
|
250
|
-
// B1..K1 共 10 格链:B1=A1+1, C1=B1+1, …
|
|
251
|
-
const chain: CellAddress[] = []
|
|
252
|
-
for (let col = 1; col <= 10; col++) {
|
|
253
|
-
const prev = { row: 0, col: col - 1 }
|
|
254
|
-
const addr = { row: 0, col }
|
|
255
|
-
sheet.setCellFormula(addr, `=${String.fromCharCode(64 + col)}1+1`)
|
|
256
|
-
chain.push(addr)
|
|
257
|
-
}
|
|
258
|
-
expect(sheet.getCellData(chain[9]!)).toMatchObject({ v: 11 })
|
|
259
|
-
sheet.history.clear()
|
|
260
|
-
|
|
261
|
-
sheet.setCellValue(A1, 100)
|
|
262
|
-
expect(sheet.getCellData(chain[9]!)).toMatchObject({ v: 110 })
|
|
263
|
-
// 源变更 + 10 格派生 = 一个 undo 单元
|
|
264
|
-
expect(sheet.history.undoSize).toBe(1)
|
|
265
|
-
|
|
266
|
-
expect(sheet.undo()).toBe(true)
|
|
267
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: 1 })
|
|
268
|
-
for (const [i, addr] of chain.entries()) {
|
|
269
|
-
expect(sheet.getCellData(addr)).toMatchObject({ v: i + 2 })
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
expect(sheet.redo()).toBe(true)
|
|
273
|
-
expect(sheet.getCellData(chain[9]!)).toMatchObject({ v: 110 })
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
it('undo 公式输入:公式、缓存与波及变更一步还原', () => {
|
|
277
|
-
const sheet = new Sheet()
|
|
278
|
-
sheet.setCellValue(A1, 5)
|
|
279
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
280
|
-
sheet.setCellFormula(C1, '=B1+1')
|
|
281
|
-
sheet.history.clear()
|
|
282
|
-
|
|
283
|
-
sheet.setCellFormula(D1, '=C1*10')
|
|
284
|
-
expect(sheet.getCellData(D1)).toMatchObject({ v: 110 })
|
|
285
|
-
|
|
286
|
-
sheet.undo()
|
|
287
|
-
expect(sheet.getCellData(D1)).toBeUndefined()
|
|
288
|
-
|
|
289
|
-
sheet.redo()
|
|
290
|
-
expect(sheet.getCellData(D1)).toMatchObject({ f: 'C1*10', v: 110 })
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
it('undo 公式删除 → 依赖图复原,再次改值照常重算', () => {
|
|
294
|
-
const sheet = new Sheet()
|
|
295
|
-
sheet.setCellValue(A1, 5)
|
|
296
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
297
|
-
sheet.setCellFormula(C1, '=B1+1')
|
|
298
|
-
const nodesBefore = sheet.formulaGraph.nodeCount
|
|
299
|
-
|
|
300
|
-
// 删除 B1 的公式(写常量覆盖)→ C1 按 B1 新值重算
|
|
301
|
-
sheet.setCellValue(B1, 0)
|
|
302
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 0 })
|
|
303
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 1 })
|
|
304
|
-
expect(sheet.formulaGraph.nodeCount).toBe(nodesBefore - 1)
|
|
305
|
-
|
|
306
|
-
// undo 删除 → 公式、缓存、派生值、图节点全部复原
|
|
307
|
-
sheet.undo()
|
|
308
|
-
expect(sheet.getCellData(B1)).toMatchObject({ f: 'A1*2', v: 10 })
|
|
309
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 11 })
|
|
310
|
-
expect(sheet.formulaGraph.nodeCount).toBe(nodesBefore)
|
|
311
|
-
|
|
312
|
-
// 图确实复原:再改 A1 仍触发 B1/C1 重算
|
|
313
|
-
sheet.setCellValue(A1, 7)
|
|
314
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 14 })
|
|
315
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 15 })
|
|
316
|
-
})
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
describe('公式与合并单元格(对抗场景)', () => {
|
|
320
|
-
it('引用合并格:锚点有值、被覆盖格按空(原始存储语义)', () => {
|
|
321
|
-
const sheet = new Sheet()
|
|
322
|
-
sheet.setCellValue({ row: 1, col: 1 }, 10) // B2
|
|
323
|
-
sheet.mergeCells(parseRange('B2:C3')!)
|
|
324
|
-
|
|
325
|
-
sheet.setCellFormula(D1, '=B2')
|
|
326
|
-
expect(sheet.getCellData(D1)).toMatchObject({ v: 10 })
|
|
327
|
-
// C3 是被覆盖格 → 空 → 0
|
|
328
|
-
sheet.setCellFormula({ row: 0, col: 4 }, '=C3+1')
|
|
329
|
-
expect(sheet.getCellData({ row: 0, col: 4 })).toMatchObject({ v: 1 })
|
|
330
|
-
// 区域含合并:只锚点计数一次
|
|
331
|
-
sheet.setCellFormula({ row: 0, col: 5 }, '=SUM(B2:C3)')
|
|
332
|
-
expect(sheet.getCellData({ row: 0, col: 5 })).toMatchObject({ v: 10 })
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
it('公式格作为合并锚点:合并后公式保留且继续重算', () => {
|
|
336
|
-
const sheet = new Sheet()
|
|
337
|
-
sheet.setCellValue(A1, 5)
|
|
338
|
-
sheet.setCellFormula({ row: 1, col: 1 }, '=A1+1') // B2
|
|
339
|
-
sheet.mergeCells(parseRange('B2:C3')!)
|
|
340
|
-
|
|
341
|
-
expect(sheet.getCellData({ row: 1, col: 1 })).toMatchObject({ f: 'A1+1', v: 6 })
|
|
342
|
-
sheet.setCellValue(A1, 7)
|
|
343
|
-
expect(sheet.getCellData({ row: 1, col: 1 })).toMatchObject({ v: 8 })
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
it('被覆盖格上的公式被合并清除:节点移除 + 依赖者重算', () => {
|
|
347
|
-
const sheet = new Sheet()
|
|
348
|
-
sheet.setCellValue({ row: 1, col: 1 }, 10) // B2 锚点
|
|
349
|
-
sheet.setCellFormula({ row: 2, col: 2 }, '=B2+1') // C3 将被覆盖
|
|
350
|
-
sheet.setCellFormula(D1, '=C3*2')
|
|
351
|
-
expect(sheet.getCellData(D1)).toMatchObject({ v: 22 })
|
|
352
|
-
const nodesBefore = sheet.formulaGraph.nodeCount
|
|
353
|
-
|
|
354
|
-
sheet.mergeCells(parseRange('B2:C3')!)
|
|
355
|
-
// C3 数据被清空 → 公式节点移除;D1 按 C3 空值重算
|
|
356
|
-
expect(sheet.getCellData({ row: 2, col: 2 })).toBeUndefined()
|
|
357
|
-
expect(sheet.formulaGraph.nodeCount).toBe(nodesBefore - 1)
|
|
358
|
-
expect(sheet.getCellData(D1)).toMatchObject({ v: 0 })
|
|
359
|
-
|
|
360
|
-
// undo 合并 → C3 公式恢复,D1 回到 22
|
|
361
|
-
sheet.undo()
|
|
362
|
-
expect(sheet.getCellData({ row: 2, col: 2 })).toMatchObject({ f: 'B2+1', v: 11 })
|
|
363
|
-
expect(sheet.getCellData(D1)).toMatchObject({ v: 22 })
|
|
364
|
-
})
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
describe('公式生命周期(对抗场景)', () => {
|
|
368
|
-
it('删除公式后依赖图清理:原依赖格改动不再触发重算', () => {
|
|
369
|
-
const sheet = new Sheet()
|
|
370
|
-
sheet.setCellValue(A1, 5)
|
|
371
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
372
|
-
sheet.setCellFormula(C1, '=B1+1')
|
|
373
|
-
expect(sheet.formulaGraph.nodeCount).toBe(2)
|
|
374
|
-
|
|
375
|
-
// 清空 B1(删除公式)→ C1 按空值重算
|
|
376
|
-
sheet.setCellValue(B1, null)
|
|
377
|
-
expect(sheet.getCellData(B1)).toBeUndefined()
|
|
378
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 1 })
|
|
379
|
-
expect(sheet.formulaGraph.nodeCount).toBe(1)
|
|
380
|
-
|
|
381
|
-
// A1 改动不再有任何依赖者
|
|
382
|
-
const handler = vi.fn()
|
|
383
|
-
sheet.on('cell-change', handler)
|
|
384
|
-
sheet.setCellValue(A1, 99)
|
|
385
|
-
expect(handler).toHaveBeenCalledTimes(1)
|
|
386
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 1 })
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
it('公式改公式:依赖边切换(旧依赖不再触发,新依赖生效)', () => {
|
|
390
|
-
const sheet = new Sheet()
|
|
391
|
-
sheet.setCellValue(A1, 1)
|
|
392
|
-
sheet.setCellValue(B1, 10)
|
|
393
|
-
sheet.setCellFormula(C1, '=A1+1')
|
|
394
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 2 })
|
|
395
|
-
|
|
396
|
-
sheet.setCellFormula(C1, '=B1+1')
|
|
397
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 11 })
|
|
398
|
-
|
|
399
|
-
// 旧依赖 A1 改动不再影响 C1
|
|
400
|
-
sheet.setCellValue(A1, 100)
|
|
401
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 11 })
|
|
402
|
-
// 新依赖 B1 生效
|
|
403
|
-
sheet.setCellValue(B1, 20)
|
|
404
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 21 })
|
|
405
|
-
})
|
|
406
|
-
|
|
407
|
-
it('同公式重复输入 = 无操作(不入历史)', () => {
|
|
408
|
-
const sheet = new Sheet()
|
|
409
|
-
sheet.setCellValue(A1, 1)
|
|
410
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
411
|
-
const depth = sheet.history.undoSize
|
|
412
|
-
|
|
413
|
-
sheet.setCellFormula(B1, '=A1*2')
|
|
414
|
-
expect(sheet.history.undoSize).toBe(depth)
|
|
415
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 2 })
|
|
416
|
-
})
|
|
417
|
-
|
|
418
|
-
it("'=' 单独输入 = 清除单元格", () => {
|
|
419
|
-
const sheet = new Sheet()
|
|
420
|
-
sheet.setCellValue(A1, 5)
|
|
421
|
-
sheet.setCellValue(A1, '=')
|
|
422
|
-
expect(sheet.getCellData(A1)).toBeUndefined()
|
|
423
|
-
// 空格输入 '=' 也是无操作
|
|
424
|
-
expect(sheet.canUndo).toBe(true) // 仍有上面两条历史
|
|
425
|
-
sheet.setCellValue(B1, '=')
|
|
426
|
-
expect(sheet.getCellData(B1)).toBeUndefined()
|
|
427
|
-
})
|
|
428
|
-
|
|
429
|
-
it('A1:A1 单格区域', () => {
|
|
430
|
-
const sheet = new Sheet()
|
|
431
|
-
sheet.setCellValue(A1, 42)
|
|
432
|
-
sheet.setCellFormula(B1, '=SUM(A1:A1)')
|
|
433
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 42 })
|
|
434
|
-
})
|
|
435
|
-
|
|
436
|
-
it('公式引用空区域 → SUM 为 0、COUNT 为 0、AVERAGE #DIV/0!', () => {
|
|
437
|
-
const sheet = new Sheet()
|
|
438
|
-
sheet.setCellFormula(A1, '=SUM(D1:E9)')
|
|
439
|
-
sheet.setCellFormula(B1, '=COUNT(D1:E9)')
|
|
440
|
-
sheet.setCellFormula(C1, '=AVERAGE(D1:E9)')
|
|
441
|
-
expect(sheet.getCellData(A1)).toMatchObject({ v: 0 })
|
|
442
|
-
expect(sheet.getCellData(B1)).toMatchObject({ v: 0 })
|
|
443
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: '#DIV/0!' })
|
|
444
|
-
})
|
|
445
|
-
|
|
446
|
-
it('批量写入(粘贴场景):重算派生并入同一 undo 单元', () => {
|
|
447
|
-
const sheet = new Sheet()
|
|
448
|
-
sheet.setCellFormula(C1, '=A1+B1')
|
|
449
|
-
sheet.history.clear()
|
|
450
|
-
|
|
451
|
-
sheet.setCells([
|
|
452
|
-
{ addr: A1, data: { v: 1, t: 'n' } },
|
|
453
|
-
{ addr: B1, data: { v: 2, t: 'n' } }
|
|
454
|
-
])
|
|
455
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 3 })
|
|
456
|
-
expect(sheet.history.undoSize).toBe(1)
|
|
457
|
-
|
|
458
|
-
sheet.undo()
|
|
459
|
-
expect(sheet.getCellData(A1)).toBeUndefined()
|
|
460
|
-
expect(sheet.getCellData(B1)).toBeUndefined()
|
|
461
|
-
expect(sheet.getCellData(C1)).toMatchObject({ v: 0 })
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
it('setCell 直接写带 f 的 CellData 同样走重算', () => {
|
|
465
|
-
const sheet = new Sheet()
|
|
466
|
-
sheet.setCellValue(A1, 6)
|
|
467
|
-
sheet.setCell(B1, { f: 'A1*2' })
|
|
468
|
-
expect(sheet.getCellData(B1)).toMatchObject({ f: 'A1*2', v: 12, t: 'n' })
|
|
469
|
-
})
|
|
470
|
-
})
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import type { CellAddress } from '../address'
|
|
4
|
-
import { Sheet } from '../sheet'
|
|
5
|
-
|
|
6
|
-
const A1 = { row: 0, col: 0 }
|
|
7
|
-
|
|
8
|
-
/** 写入公式并读回计算缓存值 */
|
|
9
|
-
function calcValue(sheet: Sheet, formula: string, addr: CellAddress = { row: 20, col: 20 }) {
|
|
10
|
-
sheet.setCellFormula(addr, formula)
|
|
11
|
-
return sheet.getCellData(addr)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe('函数集:SUM / AVERAGE / MAX / MIN', () => {
|
|
15
|
-
it('SUM:直接参数求和、数字文本强转、非法文本 #VALUE!', () => {
|
|
16
|
-
const sheet = new Sheet()
|
|
17
|
-
expect(calcValue(sheet, '=SUM(1,2,3)')).toMatchObject({ v: 6 })
|
|
18
|
-
expect(calcValue(sheet, '=SUM("5",3)')).toMatchObject({ v: 8 })
|
|
19
|
-
expect(calcValue(sheet, '=SUM("abc")')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('SUM:区域内文本/布尔忽略,空格按 0', () => {
|
|
23
|
-
const sheet = new Sheet()
|
|
24
|
-
sheet.setCellValue(A1, 2)
|
|
25
|
-
sheet.setCellValue({ row: 1, col: 0 }, 'text')
|
|
26
|
-
sheet.setCellValue({ row: 2, col: 0 }, true)
|
|
27
|
-
expect(calcValue(sheet, '=SUM(A1:A5)')).toMatchObject({ v: 2 })
|
|
28
|
-
// 全空区域 → 0
|
|
29
|
-
expect(calcValue(sheet, '=SUM(C1:C9)')).toMatchObject({ v: 0 })
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('AVERAGE:均值;无数字 → #DIV/0!', () => {
|
|
33
|
-
const sheet = new Sheet()
|
|
34
|
-
expect(calcValue(sheet, '=AVERAGE(2,4)')).toMatchObject({ v: 3 })
|
|
35
|
-
expect(calcValue(sheet, '=AVERAGE(C1:C9)')).toMatchObject({ v: '#DIV/0!', t: 'e' })
|
|
36
|
-
sheet.setCellValue(A1, 2)
|
|
37
|
-
sheet.setCellValue({ row: 1, col: 0 }, 'text')
|
|
38
|
-
// 区域内文本忽略,只按数字格求均值
|
|
39
|
-
expect(calcValue(sheet, '=AVERAGE(A1:A2)')).toMatchObject({ v: 2 })
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('MAX / MIN:极值;无数字 → 0', () => {
|
|
43
|
-
const sheet = new Sheet()
|
|
44
|
-
expect(calcValue(sheet, '=MAX(1,9,3)')).toMatchObject({ v: 9 })
|
|
45
|
-
expect(calcValue(sheet, '=MIN(1,-9,3)')).toMatchObject({ v: -9 })
|
|
46
|
-
expect(calcValue(sheet, '=MAX(C1:C9)')).toMatchObject({ v: 0 })
|
|
47
|
-
expect(calcValue(sheet, '=MIN(C1:C9)')).toMatchObject({ v: 0 })
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
describe('函数集:COUNT / COUNTA', () => {
|
|
52
|
-
it('COUNT 只数数字(区域内文本/空格不计;直接参数可强转即计)', () => {
|
|
53
|
-
const sheet = new Sheet()
|
|
54
|
-
sheet.setCellValue(A1, 1)
|
|
55
|
-
sheet.setCellValue({ row: 1, col: 0 }, 'text')
|
|
56
|
-
sheet.setCellValue({ row: 2, col: 0 }, true)
|
|
57
|
-
expect(calcValue(sheet, '=COUNT(A1:A5)')).toMatchObject({ v: 1 })
|
|
58
|
-
expect(calcValue(sheet, '=COUNT(1,"x","5",TRUE)')).toMatchObject({ v: 3 })
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('COUNTA 数非空(文本/布尔/空串结果都计,空格不计)', () => {
|
|
62
|
-
const sheet = new Sheet()
|
|
63
|
-
sheet.setCellValue(A1, 1)
|
|
64
|
-
sheet.setCellValue({ row: 1, col: 0 }, 'text')
|
|
65
|
-
sheet.setCellValue({ row: 2, col: 0 }, true)
|
|
66
|
-
expect(calcValue(sheet, '=COUNTA(A1:A5)')).toMatchObject({ v: 3 })
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
describe('函数集:IF / AND / OR / NOT', () => {
|
|
71
|
-
it('IF:分支选择;缺省 else → FALSE;条件非法 → #VALUE!', () => {
|
|
72
|
-
const sheet = new Sheet()
|
|
73
|
-
expect(calcValue(sheet, '=IF(1>0,"y","n")')).toMatchObject({ v: 'y' })
|
|
74
|
-
expect(calcValue(sheet, '=IF(0,1,2)')).toMatchObject({ v: 2 })
|
|
75
|
-
expect(calcValue(sheet, '=IF(1=1,7)')).toMatchObject({ v: 7 })
|
|
76
|
-
expect(calcValue(sheet, '=IF(0,7)')).toMatchObject({ v: false, t: 'b' })
|
|
77
|
-
expect(calcValue(sheet, '=IF("abc",1,2)')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('IF 惰性求值:未选分支的除零不产生错误', () => {
|
|
81
|
-
const sheet = new Sheet()
|
|
82
|
-
expect(calcValue(sheet, '=IF(FALSE,1/0,2)')).toMatchObject({ v: 2 })
|
|
83
|
-
expect(calcValue(sheet, '=IF(TRUE,1,1/0)')).toMatchObject({ v: 1 })
|
|
84
|
-
// 条件本身是错误则传播
|
|
85
|
-
expect(calcValue(sheet, '=IF(1/0,1,2)')).toMatchObject({ v: '#DIV/0!', t: 'e' })
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('AND / OR / NOT:布尔逻辑与强转', () => {
|
|
89
|
-
const sheet = new Sheet()
|
|
90
|
-
expect(calcValue(sheet, '=AND(1,TRUE)')).toMatchObject({ v: true })
|
|
91
|
-
expect(calcValue(sheet, '=AND(1,0)')).toMatchObject({ v: false })
|
|
92
|
-
expect(calcValue(sheet, '=OR(0,FALSE)')).toMatchObject({ v: false })
|
|
93
|
-
expect(calcValue(sheet, '=OR(0,1)')).toMatchObject({ v: true })
|
|
94
|
-
expect(calcValue(sheet, '=NOT(0)')).toMatchObject({ v: true })
|
|
95
|
-
expect(calcValue(sheet, '=NOT(TRUE)')).toMatchObject({ v: false })
|
|
96
|
-
// 无有效操作数 / 非法文本 → #VALUE!
|
|
97
|
-
expect(calcValue(sheet, '=AND(C1:C9)')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
98
|
-
expect(calcValue(sheet, '=OR("x")')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
99
|
-
expect(calcValue(sheet, '=NOT("x")')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
100
|
-
// 区域内只取布尔格
|
|
101
|
-
sheet.setCellValue(A1, true)
|
|
102
|
-
sheet.setCellValue({ row: 1, col: 0 }, 5)
|
|
103
|
-
expect(calcValue(sheet, '=AND(A1:A2)')).toMatchObject({ v: true })
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
describe('函数集:ROUND / ABS / CONCATENATE', () => {
|
|
108
|
-
it('ROUND:精度、负数位数、远离零取整', () => {
|
|
109
|
-
const sheet = new Sheet()
|
|
110
|
-
expect(calcValue(sheet, '=ROUND(3.14159,2)')).toMatchObject({ v: 3.14 })
|
|
111
|
-
expect(calcValue(sheet, '=ROUND(2.5,0)')).toMatchObject({ v: 3 })
|
|
112
|
-
expect(calcValue(sheet, '=ROUND(-2.5,0)')).toMatchObject({ v: -3 })
|
|
113
|
-
expect(calcValue(sheet, '=ROUND(1234.567,-2)')).toMatchObject({ v: 1200 })
|
|
114
|
-
// 浮点表示误差补偿(2.675 实际存储为 2.67499…)
|
|
115
|
-
expect(calcValue(sheet, '=ROUND(2.675,2)')).toMatchObject({ v: 2.68 })
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('ABS:绝对值;非法参数 → #VALUE!', () => {
|
|
119
|
-
const sheet = new Sheet()
|
|
120
|
-
expect(calcValue(sheet, '=ABS(-3)')).toMatchObject({ v: 3 })
|
|
121
|
-
expect(calcValue(sheet, '=ABS("x")')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('CONCATENATE:文本拼接(数字/布尔强转,区域展开,空格按空串)', () => {
|
|
125
|
-
const sheet = new Sheet()
|
|
126
|
-
expect(calcValue(sheet, '=CONCATENATE("a",1,TRUE)')).toMatchObject({ v: 'a1TRUE' })
|
|
127
|
-
sheet.setCellValue(A1, 'x')
|
|
128
|
-
sheet.setCellValue({ row: 2, col: 0 }, 'z')
|
|
129
|
-
// A2 为空格 → 空串
|
|
130
|
-
expect(calcValue(sheet, '=CONCATENATE(A1:A3)')).toMatchObject({ v: 'xz' })
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
describe('函数集:通用语义', () => {
|
|
135
|
-
it('嵌套函数与多层括号', () => {
|
|
136
|
-
const sheet = new Sheet()
|
|
137
|
-
sheet.setCellValue(A1, 1)
|
|
138
|
-
sheet.setCellValue({ row: 1, col: 0 }, 2)
|
|
139
|
-
sheet.setCellValue({ row: 2, col: 0 }, 3)
|
|
140
|
-
sheet.setCellValue({ row: 0, col: 1 }, 8)
|
|
141
|
-
sheet.setCellValue({ row: 0, col: 2 }, 4)
|
|
142
|
-
expect(calcValue(sheet, '=SUM(A1:A3, MAX(B1, MIN(C1, 10))) * 2')).toMatchObject({ v: 28 })
|
|
143
|
-
expect(calcValue(sheet, '=((1+2)*(3+4))/(7-2)')).toMatchObject({ v: 4.2 })
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('参数个数非法 → #VALUE!;未知函数 → #NAME?', () => {
|
|
147
|
-
const sheet = new Sheet()
|
|
148
|
-
expect(calcValue(sheet, '=IF(1)')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
149
|
-
expect(calcValue(sheet, '=ROUND(1)')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
150
|
-
expect(calcValue(sheet, '=ABS(1,2)')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
151
|
-
expect(calcValue(sheet, '=SUM()')).toMatchObject({ v: '#VALUE!', t: 'e' })
|
|
152
|
-
expect(calcValue(sheet, '=NOSUCHFN(1)')).toMatchObject({ v: '#NAME?', t: 'e' })
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
it('函数名大小写不敏感', () => {
|
|
156
|
-
const sheet = new Sheet()
|
|
157
|
-
expect(calcValue(sheet, '=sum(1,2)')).toMatchObject({ v: 3 })
|
|
158
|
-
expect(calcValue(sheet, '=If(1,2,3)')).toMatchObject({ v: 2 })
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it('区域内错误值传播', () => {
|
|
162
|
-
const sheet = new Sheet()
|
|
163
|
-
sheet.setCellFormula(A1, '=1/0')
|
|
164
|
-
sheet.setCellValue({ row: 1, col: 0 }, 5)
|
|
165
|
-
expect(calcValue(sheet, '=SUM(A1:A2)')).toMatchObject({ v: '#DIV/0!', t: 'e' })
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it('稀疏性:大区域内只有少量格参与迭代', () => {
|
|
169
|
-
const sheet = new Sheet()
|
|
170
|
-
sheet.setCellValue(A1, 1)
|
|
171
|
-
sheet.setCellValue({ row: 9999, col: 0 }, 2)
|
|
172
|
-
// A1:A10000 只有 2 个真实格;结果与迭代规模无关
|
|
173
|
-
expect(sheet.store.size).toBe(2)
|
|
174
|
-
expect(calcValue(sheet, '=SUM(A1:A10000)')).toMatchObject({ v: 3 })
|
|
175
|
-
expect(calcValue(sheet, '=COUNT(A1:A10000)')).toMatchObject({ v: 2 })
|
|
176
|
-
expect(calcValue(sheet, '=AVERAGE(A1:A10000)')).toMatchObject({ v: 1.5 })
|
|
177
|
-
})
|
|
178
|
-
})
|