@odoo/o-spreadsheet 14.0.0 → 14.0.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.
Files changed (198) hide show
  1. package/.eslintrc.json +20 -0
  2. package/.husky/post-checkout +9 -0
  3. package/.husky/pre-commit +39 -0
  4. package/.prettierignore +1 -0
  5. package/astToFormula.patch +0 -0
  6. package/demo/data.js +255 -0
  7. package/demo/index.html +9 -0
  8. package/demo/libs/Chart.bundle.js +20755 -0
  9. package/demo/main.js +101 -0
  10. package/doc/readme.md +46 -0
  11. package/package.json +3 -17
  12. package/rollup.config.js +23 -0
  13. package/src/base_plugin.ts +102 -0
  14. package/src/components/autofill.ts +146 -0
  15. package/src/components/bottom_bar.ts +199 -0
  16. package/src/components/color_picker.ts +150 -0
  17. package/src/components/composer/autocomplete_dropdown.ts +135 -0
  18. package/src/components/composer/composer.ts +487 -0
  19. package/src/components/composer/content_editable_helper.ts +146 -0
  20. package/src/components/figures/chart.ts +70 -0
  21. package/src/components/figures/container.ts +268 -0
  22. package/src/components/figures/text_figure.ts +36 -0
  23. package/src/components/grid.ts +635 -0
  24. package/src/components/helpers/dom_helpers.ts +7 -0
  25. package/src/components/helpers/drag_and_drop.ts +17 -0
  26. package/src/components/icons.ts +36 -0
  27. package/src/components/index.ts +1 -0
  28. package/src/components/menu.ts +288 -0
  29. package/src/components/overlay.ts +457 -0
  30. package/src/components/scrollbar.ts +22 -0
  31. package/src/components/selection_input.ts +168 -0
  32. package/src/components/side_panel/cell_is_rule_editor.ts +169 -0
  33. package/src/components/side_panel/chart_panel.ts +104 -0
  34. package/src/components/side_panel/color_scale_rule_editor.ts +145 -0
  35. package/src/components/side_panel/conditional_formatting.ts +396 -0
  36. package/src/components/side_panel/side_panel.ts +127 -0
  37. package/src/components/side_panel/translations_terms.ts +45 -0
  38. package/src/components/spreadsheet.ts +198 -0
  39. package/src/components/top_bar.ts +535 -0
  40. package/src/constants.ts +26 -0
  41. package/src/data.ts +149 -0
  42. package/src/fonts.ts +41 -0
  43. package/src/formulas/compiler.ts +258 -0
  44. package/src/formulas/composer_tokenizer.ts +70 -0
  45. package/src/formulas/index.ts +15 -0
  46. package/src/formulas/normalize.ts +52 -0
  47. package/src/formulas/parser.ts +280 -0
  48. package/src/formulas/range_tokenizer.ts +97 -0
  49. package/src/formulas/tokenizer.ts +242 -0
  50. package/src/functions/arguments.ts +114 -0
  51. package/src/functions/dates.ts +302 -0
  52. package/src/functions/helpers.ts +592 -0
  53. package/src/functions/index.ts +52 -0
  54. package/src/functions/module_database.ts +282 -0
  55. package/src/functions/module_date.ts +743 -0
  56. package/src/functions/module_info.ts +69 -0
  57. package/src/functions/module_logical.ts +208 -0
  58. package/src/functions/module_lookup.ts +185 -0
  59. package/src/functions/module_math.ts +1252 -0
  60. package/src/functions/module_operators.ts +291 -0
  61. package/src/functions/module_statistical.ts +882 -0
  62. package/src/functions/module_text.ts +370 -0
  63. package/src/helpers/color.ts +30 -0
  64. package/src/helpers/coordinates.ts +71 -0
  65. package/src/helpers/grid_manipulation.ts +68 -0
  66. package/src/helpers/index.ts +7 -0
  67. package/src/helpers/misc.ts +76 -0
  68. package/src/helpers/numbers.ts +117 -0
  69. package/src/helpers/uuid.ts +19 -0
  70. package/src/helpers/zones.ts +263 -0
  71. package/src/history.ts +186 -0
  72. package/src/index.ts +84 -0
  73. package/src/model.ts +261 -0
  74. package/src/plugins/autofill.ts +412 -0
  75. package/src/plugins/chart.ts +404 -0
  76. package/src/plugins/clipboard.ts +461 -0
  77. package/src/plugins/conditional_format.ts +445 -0
  78. package/src/plugins/core.ts +1502 -0
  79. package/src/plugins/edition.ts +157 -0
  80. package/src/plugins/evaluation.ts +428 -0
  81. package/src/plugins/figures.ts +123 -0
  82. package/src/plugins/formatting.ts +630 -0
  83. package/src/plugins/highlight.ts +187 -0
  84. package/src/plugins/index.ts +32 -0
  85. package/src/plugins/merge.ts +524 -0
  86. package/src/plugins/renderer.ts +571 -0
  87. package/src/plugins/selection.ts +562 -0
  88. package/src/plugins/selection_inputs.ts +326 -0
  89. package/src/registries/autofill_modifiers.ts +76 -0
  90. package/src/registries/autofill_rules.ts +98 -0
  91. package/src/registries/figure_registry.ts +30 -0
  92. package/src/registries/index.ts +7 -0
  93. package/src/registries/menu_items_registry.ts +110 -0
  94. package/src/registries/menus/cell_menu_registry.ts +79 -0
  95. package/src/registries/menus/col_menu_registry.ts +63 -0
  96. package/src/registries/menus/index.ts +6 -0
  97. package/src/registries/menus/menu_items_actions.ts +425 -0
  98. package/src/registries/menus/row_menu_registry.ts +63 -0
  99. package/src/registries/menus/sheet_menu_registry.ts +75 -0
  100. package/src/registries/menus/topbar_menu_registry.ts +204 -0
  101. package/src/registries/side_panel_registry.ts +26 -0
  102. package/src/registries/topbar_component_registry.ts +12 -0
  103. package/src/registry.ts +51 -0
  104. package/src/translation.ts +44 -0
  105. package/src/types/autofill.ts +48 -0
  106. package/src/types/chart.ts +26 -0
  107. package/src/types/commands.ts +716 -0
  108. package/src/types/conditional_formatting.ts +124 -0
  109. package/src/types/env.ts +13 -0
  110. package/src/types/functions.ts +35 -0
  111. package/src/types/getters.ts +103 -0
  112. package/src/types/index.ts +20 -0
  113. package/src/types/misc.ts +143 -0
  114. package/src/types/rendering.ts +41 -0
  115. package/src/types/workbook_data.ts +62 -0
  116. package/tests/__mocks__/uuid.ts +7 -0
  117. package/tests/canvas.mock.ts +34 -0
  118. package/tests/components/__mocks__/content_editable_helper.ts +79 -0
  119. package/tests/components/__mocks__/scrollbar.ts +18 -0
  120. package/tests/components/__snapshots__/autocomplete_dropdown_test.ts.snap +29 -0
  121. package/tests/components/__snapshots__/bottom_bar_test.ts.snap +75 -0
  122. package/tests/components/__snapshots__/context_menu_test.ts.snap +88 -0
  123. package/tests/components/__snapshots__/grid_test.ts.snap +60 -0
  124. package/tests/components/__snapshots__/spreadsheet_test.ts.snap +455 -0
  125. package/tests/components/__snapshots__/top_bar_test.ts.snap +705 -0
  126. package/tests/components/autocomplete_dropdown_test.ts +315 -0
  127. package/tests/components/autofill_test.ts +167 -0
  128. package/tests/components/bottom_bar_test.ts +262 -0
  129. package/tests/components/chart_side_panel_test.ts +123 -0
  130. package/tests/components/composer_test.ts +400 -0
  131. package/tests/components/context_menu_test.ts +825 -0
  132. package/tests/components/grid_manipulation_test.ts +163 -0
  133. package/tests/components/grid_test.ts +581 -0
  134. package/tests/components/overlay_test.ts +541 -0
  135. package/tests/components/selection_input_test.ts +259 -0
  136. package/tests/components/side_panel_test.ts +148 -0
  137. package/tests/components/spreadsheet_test.ts +194 -0
  138. package/tests/components/top_bar_test.ts +427 -0
  139. package/tests/data_test.ts +175 -0
  140. package/tests/dom_helper.ts +42 -0
  141. package/tests/formulas/__snapshots__/compiler_test.ts.snap +302 -0
  142. package/tests/formulas/compiler_test.ts +266 -0
  143. package/tests/formulas/composer_tokenizer_test.ts +137 -0
  144. package/tests/formulas/expressions_test.ts +57 -0
  145. package/tests/formulas/formulas_test.ts +80 -0
  146. package/tests/formulas/normalize_test.ts +82 -0
  147. package/tests/formulas/parser_test.ts +224 -0
  148. package/tests/formulas/range_tokenizer_test.ts +187 -0
  149. package/tests/formulas/tokenizer_test.ts +164 -0
  150. package/tests/functions/arguments_test.ts +141 -0
  151. package/tests/functions/dates_test.ts +1081 -0
  152. package/tests/functions/functions_test.ts +53 -0
  153. package/tests/functions/helper_test.ts +98 -0
  154. package/tests/functions/module_database_test.ts +311 -0
  155. package/tests/functions/module_date_test.ts +941 -0
  156. package/tests/functions/module_info_test.ts +139 -0
  157. package/tests/functions/module_logical_test.ts +369 -0
  158. package/tests/functions/module_lookup_test.ts +541 -0
  159. package/tests/functions/module_math_test.ts +2482 -0
  160. package/tests/functions/module_operators_test.ts +955 -0
  161. package/tests/functions/module_statistical_test.ts +2014 -0
  162. package/tests/functions/module_text_test.ts +667 -0
  163. package/tests/helpers/coordinates_test.ts +25 -0
  164. package/tests/helpers/misc_test.ts +95 -0
  165. package/tests/helpers/numbers_test.ts +196 -0
  166. package/tests/helpers/zones_test.ts +97 -0
  167. package/tests/helpers.ts +384 -0
  168. package/tests/menu_items_registry_test.ts +540 -0
  169. package/tests/model/plugins/__snapshots__/chart_test.ts.snap +90 -0
  170. package/tests/model/plugins/chart_test.ts +562 -0
  171. package/tests/model_test.ts +82 -0
  172. package/tests/plugins/__snapshots__/conditional_formatting_test.ts.snap +131 -0
  173. package/tests/plugins/__snapshots__/renderer_test.ts.snap +371 -0
  174. package/tests/plugins/autofill_test.ts +457 -0
  175. package/tests/plugins/borders_test.ts +295 -0
  176. package/tests/plugins/clipboard_test.ts +1180 -0
  177. package/tests/plugins/conditional_formatting_test.ts +1253 -0
  178. package/tests/plugins/core_test.ts +614 -0
  179. package/tests/plugins/edition_test.ts +100 -0
  180. package/tests/plugins/evaluation_async_test.ts +332 -0
  181. package/tests/plugins/evaluation_dates_test.ts +79 -0
  182. package/tests/plugins/evaluation_test.ts +901 -0
  183. package/tests/plugins/figures_test.ts +327 -0
  184. package/tests/plugins/formatting_test.ts +391 -0
  185. package/tests/plugins/grid_manipulation_test.ts +1366 -0
  186. package/tests/plugins/highlight_test.ts +413 -0
  187. package/tests/plugins/history_test.ts +142 -0
  188. package/tests/plugins/import_export_test.ts +276 -0
  189. package/tests/plugins/merges_test.ts +490 -0
  190. package/tests/plugins/navigation_test.ts +181 -0
  191. package/tests/plugins/plugins/evaluation_test.ts +111 -0
  192. package/tests/plugins/renderer_test.ts +598 -0
  193. package/tests/plugins/resizing_test.ts +152 -0
  194. package/tests/plugins/selection_input_test.ts +409 -0
  195. package/tests/plugins/selection_test.ts +442 -0
  196. package/tests/plugins/sheets_test.ts +643 -0
  197. package/tests/plugins/style_test.ts +90 -0
  198. package/tsconfig.json +22 -0
@@ -0,0 +1,2482 @@
1
+ import { toNumber } from "../../src/functions/helpers";
2
+ import { evaluateCell, evaluateGrid } from "../helpers";
3
+
4
+ describe("math", () => {
5
+ //----------------------------------------------------------------------------
6
+ // ACOS
7
+ //----------------------------------------------------------------------------
8
+
9
+ test.each([
10
+ ["1", 0],
11
+ ["-1", Math.PI],
12
+ ["0", Math.PI / 2],
13
+ ["0.5", Math.PI / 3],
14
+ ])("ACOS(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
15
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: a })).toBeCloseTo(expected, 9);
16
+ });
17
+
18
+ test.each([["2"], ["-1.5"]])("ACOS(%s) - ERROR: take 1 parameter(s), return an error", (a) => {
19
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
20
+ });
21
+
22
+ test("ACOS: special value testing", () => {
23
+ expect(evaluateCell("A1", { A1: "=ACOS()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
24
+ expect(evaluateCell("A1", { A1: "=ACOS(TRUE)" })).toBeCloseTo(0, 9);
25
+ expect(evaluateCell("A1", { A1: "=ACOS(FALSE)" })).toBeCloseTo(Math.PI / 2, 9);
26
+
27
+ expect(evaluateCell("A1", { A1: '=ACOS("")' })).toBeCloseTo(Math.PI / 2, 9);
28
+ expect(evaluateCell("A1", { A1: '=ACOS(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
29
+ expect(evaluateCell("A1", { A1: '=ACOS("0")' })).toBeCloseTo(Math.PI / 2, 9);
30
+
31
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: "" })).toBeCloseTo(Math.PI / 2, 9);
32
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
33
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: "0" })).toBeCloseTo(Math.PI / 2, 9);
34
+
35
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
36
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
37
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: '"0"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
38
+
39
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: '=""' })).toBeCloseTo(Math.PI / 2, 9);
40
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
41
+ expect(evaluateCell("A1", { A1: "=ACOS(A2)", A2: '="0"' })).toBeCloseTo(Math.PI / 2, 9);
42
+ });
43
+
44
+ //----------------------------------------------------------------------------
45
+ // ACOSH
46
+ //----------------------------------------------------------------------------
47
+
48
+ test.each([
49
+ ["1.54308063481524", 1],
50
+ ["1", 0],
51
+ ["1.25", Math.log(2)],
52
+ ["2.125", Math.log(4)],
53
+ ])("ACOSH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
54
+ expect(evaluateCell("A1", { A1: "=ACOSH(A2)", A2: a })).toBeCloseTo(expected, 9);
55
+ });
56
+
57
+ test.each([["-2"], ["0.5"]])("ACOSH(%s) - ERROR: take 1 parameter(s), return an error", (a) => {
58
+ expect(evaluateCell("A1", { A1: "=ACOSH(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
59
+ });
60
+
61
+ //----------------------------------------------------------------------------
62
+ // ACOT
63
+ //----------------------------------------------------------------------------
64
+
65
+ test.each([
66
+ ["0", Math.PI / 2],
67
+ ["1", Math.PI / 4],
68
+ ["-1", -Math.PI / 4],
69
+ ["2", 0.463647609],
70
+ ["-4", -0.244978663],
71
+ ])("ACOT(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
72
+ expect(evaluateCell("A1", { A1: "=ACOT(A2)", A2: a })).toBeCloseTo(expected, 9);
73
+ });
74
+
75
+ //----------------------------------------------------------------------------
76
+ // ACOTH
77
+ //----------------------------------------------------------------------------
78
+
79
+ test.each([
80
+ ["1.25", Math.log(3)],
81
+ ["1.1", 1.522261219],
82
+ ["2", 0.549306144],
83
+ ["-3", -0.34657359],
84
+ ["-4", -0.255412812],
85
+ ])("ACOTH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
86
+ expect(evaluateCell("A1", { A1: "=ACOTH(A2)", A2: a })).toBeCloseTo(expected, 9);
87
+ });
88
+
89
+ test.each([["0"], ["1"], ["-1"], ["0.5"]])(
90
+ "ACOTH(%s) - ERROR: take 1 parameter(s), return an error",
91
+ (a) => {
92
+ expect(evaluateCell("A1", { A1: "=ACOTH(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
93
+ }
94
+ );
95
+
96
+ //----------------------------------------------------------------------------
97
+ // ASIN
98
+ //----------------------------------------------------------------------------
99
+
100
+ test.each([
101
+ ["1", Math.PI / 2],
102
+ ["-1", -Math.PI / 2],
103
+ ["0", 0],
104
+ ["0.5", Math.PI / 6],
105
+ ])("ASIN(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
106
+ expect(evaluateCell("A1", { A1: "=ASIN(A2)", A2: a })).toBeCloseTo(expected, 9);
107
+ });
108
+
109
+ test.each([["2"], ["-1.5"]])("ASIN(%s) - ERROR: take 1 parameter(s), return an error", (a) => {
110
+ expect(evaluateCell("A1", { A1: "=ASIN(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
111
+ });
112
+
113
+ //----------------------------------------------------------------------------
114
+ // ASINH
115
+ //----------------------------------------------------------------------------
116
+
117
+ test.each([
118
+ ["1.175201194", 1],
119
+ ["-3.626860408", -2],
120
+ ["0", 0],
121
+ ["0.75", Math.log(2)],
122
+ ["-1.875", -Math.log(4)],
123
+ ])("ASINH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
124
+ expect(evaluateCell("A1", { A1: "=ASINH(A2)", A2: a })).toBeCloseTo(expected, 9);
125
+ });
126
+
127
+ //----------------------------------------------------------------------------
128
+ // ATAN
129
+ //----------------------------------------------------------------------------
130
+
131
+ test.each([
132
+ ["0", 0],
133
+ ["1", Math.PI / 4],
134
+ ["1.557407725", 1],
135
+ ["-2", -1.107148718],
136
+ ["-4", -1.325817664],
137
+ ])("ATAN(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
138
+ expect(evaluateCell("A1", { A1: "=ATAN(A2)", A2: a })).toBeCloseTo(expected, 9);
139
+ });
140
+
141
+ //----------------------------------------------------------------------------
142
+ // ATAN2
143
+ //----------------------------------------------------------------------------
144
+
145
+ test.each([
146
+ ["42", "0", 0],
147
+ ["0", "42", Math.PI / 2],
148
+ ["2", "2", Math.PI / 4],
149
+ ["-2", "3", 2.15879893],
150
+ ["4", "-7", -1.051650213],
151
+ ])("ATAN2(%s, %s) - %s: take 1 parameter(s), return a number", (a, b, expected) => {
152
+ expect(evaluateCell("A1", { A1: "=ATAN2(A2, A3)", A2: a, A3: b })).toBeCloseTo(expected, 9);
153
+ });
154
+
155
+ test.each([["0", "0"]])("ATAN2(%s, %s) - ERROR: take 1 parameter(s), return an error", (a, b) => {
156
+ expect(evaluateCell("A1", { A1: "=ATAN2(A2, A3)", A2: a, A3: b })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
157
+ });
158
+
159
+ //----------------------------------------------------------------------------
160
+ // ATANH
161
+ //----------------------------------------------------------------------------
162
+
163
+ test.each([
164
+ ["0.8", Math.log(3)],
165
+ ["0.6", Math.log(2)],
166
+ ["0", 0],
167
+ [" 0.761594156", 1],
168
+ ["-0.9", -1.47221949],
169
+ ])("ATANH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
170
+ expect(evaluateCell("A1", { A1: "=ATANH(A2)", A2: a })).toBeCloseTo(expected, 9);
171
+ });
172
+
173
+ test.each([["1.1"], ["-1"], ["42"]])(
174
+ "ATANH(%s) - ERROR: take 1 parameter(s), return an error",
175
+ (a) => {
176
+ expect(evaluateCell("A1", { A1: "=ATANH(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
177
+ }
178
+ );
179
+
180
+ //----------------------------------------------------------------------------
181
+ // CEILING / CEILING.MATH / CEILING.PRECISE / ISO.CEILING
182
+ //----------------------------------------------------------------------------
183
+
184
+ test.each([
185
+ ["0", 0],
186
+ ["6", 6],
187
+ ["-6", -6],
188
+ ["6.7", 7],
189
+ ["7.89", 8],
190
+ ["-6.7", -6],
191
+ ["-7.89", -7],
192
+ ])("CEILING FUNCTIONS(%s) - %s: take 1 parameters, return a number", (a, expected) => {
193
+ expect(evaluateCell("A1", { A1: "=CEILING(A2)", A2: a })).toBe(expected);
194
+ expect(evaluateCell("A1", { A1: "=CEILING.MATH(A2)", A2: a })).toBe(expected);
195
+ expect(evaluateCell("A1", { A1: "=CEILING.PRECISE(A2)", A2: a })).toBe(expected);
196
+ expect(evaluateCell("A1", { A1: "=ISO.CEILING(A2)", A2: a })).toBe(expected);
197
+ });
198
+
199
+ test.each([
200
+ ["0", "0", 0],
201
+ ["0", "0.1", 0],
202
+ ["0", "-4", 0],
203
+ ["6", "0", 0],
204
+ ["6.7", "0", 0],
205
+ ["-6", "0", 0],
206
+ ["6", "0.1", 6],
207
+ ["-6", "0.1", -6],
208
+ ["6", "0.7", 6.3],
209
+ ["-6", "0.7", -5.6],
210
+ ["6.7", "0.2", 6.8],
211
+ ["-6.7", "0.2", -6.6],
212
+ ])("CEILING FUNCTIONS(%s, %s) - %s: take 2 parameters, return a number", (a, b, expected) => {
213
+ expect(evaluateCell("A1", { A1: "=CEILING(A2, A3)", A2: a, A3: b })).toBeCloseTo(expected, 9);
214
+ expect(evaluateCell("A1", { A1: "=CEILING.MATH(A2, A3)", A2: a, A3: b })).toBeCloseTo(
215
+ expected,
216
+ 9
217
+ );
218
+ expect(evaluateCell("A1", { A1: "=CEILING.PRECISE(A2, A3)", A2: a, A3: b })).toBeCloseTo(
219
+ expected,
220
+ 9
221
+ );
222
+ expect(evaluateCell("A1", { A1: "=ISO.CEILING(A2, A3)", A2: a, A3: b })).toBeCloseTo(
223
+ expected,
224
+ 9
225
+ );
226
+ });
227
+
228
+ test.each([
229
+ ["0", "0.2", 0],
230
+ ["0", "-0.2", 0],
231
+ ["7.89", "0.2", 8],
232
+ ["7.89", "-0.2", 8],
233
+ ["-7.89", "0.2", -7.8],
234
+ ["-7.89", "-0.2", -7.8],
235
+ ])(
236
+ "CEILING (MATH/PRECISE/ISO) FUNCTIONS(%s, %s) - %s: no effect with negative factor",
237
+ (a, b, expected) => {
238
+ expect(evaluateCell("A1", { A1: "=CEILING.MATH(A2, A3)", A2: a, A3: b })).toBeCloseTo(
239
+ expected,
240
+ 9
241
+ );
242
+ expect(evaluateCell("A1", { A1: "=CEILING.PRECISE(A2, A3)", A2: a, A3: b })).toBeCloseTo(
243
+ expected,
244
+ 9
245
+ );
246
+ expect(evaluateCell("A1", { A1: "=ISO.CEILING(A2, A3)", A2: a, A3: b })).toBeCloseTo(
247
+ expected,
248
+ 9
249
+ );
250
+ }
251
+ );
252
+
253
+ test.each([
254
+ ["6", "-0.2"],
255
+ ["7.89", "-0.2"],
256
+ ])("CEILING(%s, %s) - error: if value positive, factor can't be negative", (a, b) => {
257
+ expect(evaluateCell("A1", { A1: "=CEILING(A2, A3)", A2: a, A3: b })).toBe("#ERROR");
258
+ });
259
+
260
+ test.each([
261
+ // Concerning CEILING function
262
+ // if "a" is negative and "b" is negative, rounds a number down (and not up)
263
+ // the nearest integer multiple of b
264
+ ["-7.89", "0.2", -7.8],
265
+ ["-7.89", "-0.2", -8],
266
+ ])("CEILING(%s, %s) - %s: if factor negative, rounds number down", (a, b, expected) => {
267
+ expect(evaluateCell("A1", { A1: "=CEILING(A2, A3)", A2: a, A3: b })).toBeCloseTo(expected, 9);
268
+ });
269
+
270
+ test.each([
271
+ // Concerning CEILING.MATH function
272
+ // if "a" is negative and "c" different 0, rounds a number down (and not up)
273
+ // the nearest integer multiple of b
274
+ ["7.89", "0.2", "0", 8],
275
+ ["7.89", "0.2", "1", 8],
276
+ ["-7.89", "0.2", "0", -7.8],
277
+ ["-7.89", "0.2", "1", -8],
278
+ ["-7.89", "0.2", "2.2", -8],
279
+ ["-7.89", "0.2", "-2.2", -8],
280
+ ["-7.89", "-0.2", "0", -7.8],
281
+ ["-7.89", "-0.2", "1", -8],
282
+ ["-7.89", "-0.2", "2.2", -8],
283
+ ["-7.89", "-0.2", "-2.2", -8],
284
+ ])("CEILING.MATH(%s, %s, %s) - %s: take 3 parameters, return a number", (a, b, c, expected) => {
285
+ expect(
286
+ evaluateCell("A1", { A1: "=CEILING.MATH(A2, A3, A4)", A2: a, A3: b, A4: c })
287
+ ).toBeCloseTo(expected, 9);
288
+ });
289
+
290
+ function evaluateCeilingFunction(functionName: string): void {
291
+ expect(evaluateCell("A1", { A1: "=" + functionName + "()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
292
+ expect(evaluateCell("A1", { A1: "=" + functionName + "( , )" })).toBeCloseTo(0, 9);
293
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(-7.89, 0.2)" })).toBeCloseTo(-7.8, 9);
294
+ expect(evaluateCell("A1", { A1: "=" + functionName + "( , 0.2)" })).toBeCloseTo(0, 9);
295
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(-7.89, )" })).toBeCloseTo(0, 9); // @compatibility: on google sheets, return -7
296
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(-7.89, TRUE)" })).toBeCloseTo(-7, 9);
297
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(-7.89, FALSE)" })).toBeCloseTo(0, 9);
298
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(TRUE, 10)" })).toBeCloseTo(10, 9);
299
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(FALSE, 10)" })).toBeCloseTo(0, 9);
300
+
301
+ expect(evaluateCell("A1", { A1: "=" + functionName + '("" , "")' })).toBeCloseTo(0, 9);
302
+ expect(evaluateCell("A1", { A1: "=" + functionName + '(" " , " ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
303
+ expect(evaluateCell("A1", { A1: "=" + functionName + '("-7.89", "0.2")' })).toBeCloseTo(
304
+ -7.8,
305
+ 9
306
+ );
307
+
308
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "", A3: "" })).toBeCloseTo(
309
+ 0,
310
+ 9
311
+ );
312
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: " ", A3: " " })).toBe(
313
+ "#ERROR"
314
+ ); // @compatibility: on google sheets, return #VALUE!
315
+ expect(
316
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "-7.89", A3: "0.2" })
317
+ ).toBeCloseTo(-7.8, 9);
318
+ expect(
319
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "", A3: "0.2" })
320
+ ).toBeCloseTo(0, 9);
321
+ expect(
322
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "-7.89", A3: "" })
323
+ ).toBeCloseTo(0, 9);
324
+ expect(
325
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "-7.89", A3: "TRUE" })
326
+ ).toBeCloseTo(-7, 9);
327
+ expect(
328
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "-7.89", A3: "FALSE" })
329
+ ).toBeCloseTo(0, 9);
330
+ expect(
331
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "TRUE", A3: "10" })
332
+ ).toBeCloseTo(10, 9);
333
+ expect(
334
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "FALSE", A3: "10" })
335
+ ).toBeCloseTo(0, 9);
336
+
337
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '""', A3: '""' })).toBe(
338
+ "#ERROR"
339
+ ); // @compatibility: on google sheets, return #VALUE!
340
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '" "', A3: '" "' })).toBe(
341
+ "#ERROR"
342
+ ); // @compatibility: on google sheets, return #VALUE!
343
+ expect(
344
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '"-7.89"', A3: '"0.2"' })
345
+ ).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
346
+
347
+ expect(
348
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '=""', A3: '=""' })
349
+ ).toBeCloseTo(0, 9);
350
+ expect(
351
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '=" "', A3: '=" "' })
352
+ ).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
353
+ expect(
354
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '="-7.89"', A3: '="0.2"' })
355
+ ).toBeCloseTo(-7.8, 9);
356
+ }
357
+
358
+ test.each([["CEILING"], ["CEILING.MATH"], ["CEILING.PRECISE"], ["ISO.CEILING"]])(
359
+ "%s: special value testing",
360
+ (functionName) => {
361
+ evaluateCeilingFunction(functionName);
362
+ }
363
+ );
364
+
365
+ //----------------------------------------------------------------------------
366
+ // COS
367
+ //----------------------------------------------------------------------------
368
+
369
+ test.each([
370
+ ["0", 1],
371
+ ["=PI()", -1],
372
+ ["=PI()*2", 1],
373
+ ["=PI()/2", 0],
374
+ ["=PI()/3", 0.5],
375
+ ["=-PI()/2", 0],
376
+ ])("COS(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
377
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: a })).toBeCloseTo(expected, 9);
378
+ });
379
+
380
+ test("COS: special value testing", () => {
381
+ expect(evaluateCell("A1", { A1: "=COS()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
382
+ expect(evaluateCell("A1", { A1: "=COS(TRUE)" })).toBeCloseTo(0.54030230586814, 9);
383
+ expect(evaluateCell("A1", { A1: "=COS(FALSE)" })).toBeCloseTo(1, 9);
384
+
385
+ expect(evaluateCell("A1", { A1: '=COS("")' })).toBeCloseTo(1, 9);
386
+ expect(evaluateCell("A1", { A1: '=COS(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
387
+ expect(evaluateCell("A1", { A1: '=COS("0")' })).toBeCloseTo(1, 9);
388
+
389
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: "" })).toBeCloseTo(1, 9);
390
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
391
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: "0" })).toBeCloseTo(1, 9);
392
+
393
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
394
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
395
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: '"0"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
396
+
397
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: '=""' })).toBeCloseTo(1, 9);
398
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
399
+ expect(evaluateCell("A1", { A1: "=COS(A2)", A2: '="0"' })).toBeCloseTo(1, 9);
400
+ });
401
+
402
+ //----------------------------------------------------------------------------
403
+ // COSH
404
+ //----------------------------------------------------------------------------
405
+
406
+ test.each([
407
+ ["0", 1],
408
+ ["1", 1.543080635],
409
+ ["-1", 1.543080635],
410
+ ["=LN(2)", 1.25],
411
+ ["=LN(4)", 2.125],
412
+ ])("COSH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
413
+ expect(evaluateCell("A1", { A1: "=COSH(A2)", A2: a })).toBeCloseTo(expected, 9);
414
+ });
415
+
416
+ //----------------------------------------------------------------------------
417
+ // COT
418
+ //----------------------------------------------------------------------------
419
+
420
+ test.each([
421
+ ["=PI()/2", 0],
422
+ ["=PI()/4", 1],
423
+ ["=-PI()*5/4", -1],
424
+ ["1", 0.642092616],
425
+ ["2", -0.457657554],
426
+ ])("COT(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
427
+ expect(evaluateCell("A1", { A1: "=COT(A2)", A2: a })).toBeCloseTo(expected, 9);
428
+ });
429
+
430
+ test.each([["0"]])("COT(%s) - %s: take 1 parameter(s), return an error", (a) => {
431
+ expect(evaluateCell("A1", { A1: "=COT(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!!
432
+ });
433
+
434
+ //----------------------------------------------------------------------------
435
+ // COTH
436
+ //----------------------------------------------------------------------------
437
+
438
+ test.each([
439
+ ["=LN(3)", 1.25],
440
+ ["1", 1.313035285],
441
+ ["2", 1.037314721],
442
+ ["-3", -1.004969823],
443
+ ["-4", -1.00067115],
444
+ ])("COTH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
445
+ expect(evaluateCell("A1", { A1: "=COTH(A2)", A2: a })).toBeCloseTo(expected, 9);
446
+ });
447
+
448
+ test.each([["0"]])("COTH(%s) - %s: take 1 parameter(s), return an error", (a) => {
449
+ expect(evaluateCell("A1", { A1: "=COTH(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!!
450
+ });
451
+
452
+ //----------------------------------------------------------------------------
453
+ // COUNTBLANK
454
+ //----------------------------------------------------------------------------
455
+
456
+ test("COUNTBLANK: functional tests on simple arguments", () => {
457
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
458
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(,)" })).toBe(2);
459
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(0)" })).toBe(0);
460
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(42)" })).toBe(0);
461
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(TRUE)" })).toBe(0);
462
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(FALSE)" })).toBe(0);
463
+
464
+ expect(evaluateCell("A1", { A1: '=COUNTBLANK("")' })).toBe(1);
465
+ expect(evaluateCell("A1", { A1: '=COUNTBLANK(" ")' })).toBe(0);
466
+ expect(evaluateCell("A1", { A1: '=COUNTBLANK("hello there")' })).toBe(0);
467
+
468
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: "" })).toBe(1);
469
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: " " })).toBe(0);
470
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: "0" })).toBe(0);
471
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: "42" })).toBe(0);
472
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: "TRUE" })).toBe(0);
473
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: "FALSE" })).toBe(0);
474
+
475
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: '""' })).toBe(0);
476
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: '" "' })).toBe(0);
477
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: '"42"' })).toBe(0);
478
+
479
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: '=""' })).toBe(1);
480
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: '=" "' })).toBe(0);
481
+ expect(evaluateCell("A1", { A1: "=COUNTBLANK(A2)", A2: '="42"' })).toBe(0);
482
+ });
483
+
484
+ // prettier-ignore
485
+ test("COUNTBLANK: count blank on ranges ", () => {
486
+ const grid = {
487
+
488
+ B1: "=COUNTBLANK(D1:I2)",
489
+ C1: "=COUNTBLANK(D1:I1)",
490
+ C2: "=COUNTBLANK(D2:I2)",
491
+
492
+ D1: "0", E1: "1", F1: "2", G1: "", H1: '""', I1: "",
493
+ D2: "", E2: "", F2: '""', G2: "FALSE", H2: "TRUE", I2: "42",
494
+
495
+ A2: "=COUNTBLANK(A4:B9)",
496
+ A3: "=COUNTBLANK(A4:A9)", B3: "=COUNTBLANK(B4:B9)",
497
+
498
+ A4: "0", B4: "",
499
+ A5: "1", B5: "",
500
+ A6: "2", B6: '""',
501
+ A7: "", B7: "FALSE",
502
+ A8: '""', B8: "TRUE",
503
+ A9: "", B9: "42",
504
+ };
505
+
506
+ const gridResult = evaluateGrid(grid);
507
+ expect(gridResult.B1).toBe(4);
508
+ expect(gridResult.C1).toBe(2);
509
+ expect(gridResult.C2).toBe(2);
510
+ expect(gridResult.A2).toBe(4);
511
+ expect(gridResult.A3).toBe(2);
512
+ expect(gridResult.B3).toBe(2);
513
+ });
514
+
515
+ //----------------------------------------------------------------------------
516
+ // COUNTIF
517
+ //----------------------------------------------------------------------------
518
+
519
+ // prettier-ignore
520
+ test("COUNTIF: operator tests on type number and type string", () => {
521
+
522
+ const grid = {
523
+ A1 : "1" , B1 : '="1"',
524
+ A2 : "10", B2 : '="10"',
525
+ A3 : "2" , B3 : '="2"',
526
+ A4 : "20", B4 : '="20"',
527
+ A5 : "3" , B5 : '="3"',
528
+ A6 : "30", B6 : '="30"',
529
+ A7 : "4" , B7 : '="4"',
530
+ A8 : "40", B8 : '="40"',
531
+
532
+ A10: "=COUNTIF(A1:A8, 20)" , B10: "=COUNTIF(B1:B8, 20)",
533
+ A11: '=COUNTIF(A1:A8, "20")' , B11: '=COUNTIF(B1:B8, "20")',
534
+ A12: '=COUNTIF(A1:A8, "=20")' , B12: '=COUNTIF(B1:B8, "=20")',
535
+
536
+ A14: '=COUNTIF(A1:A8, "2*")' , B14: '=COUNTIF(B1:B8, "2*")',
537
+
538
+ A16: '=COUNTIF(A1:A8, "<20")' , B16: '=COUNTIF(B1:B8, "<20")',
539
+ A17: '=COUNTIF(A1:A8, ">20")' , B17: '=COUNTIF(B1:B8, ">20")',
540
+
541
+ A19: '=COUNTIF(A1:A8, "< 20 ")' , B19: '=COUNTIF(B1:B8, "< 20 ")',
542
+ A20: '=COUNTIF(A1:A8, "> 20 ")' , B20: '=COUNTIF(B1:B8, "> 20 ")',
543
+
544
+ A22: '=COUNTIF(A1:A8, "< 20% ")', B22: '=COUNTIF(B1:B8, "< 20% ")',
545
+ A23: '=COUNTIF(A1:A8, "> 20% ")', B23: '=COUNTIF(B1:B8, "> 20% ")',
546
+
547
+ A25: '=COUNTIF(A1:A8, "<a")' , B25: '=COUNTIF(B1:B8, "<a")',
548
+ A26: '=COUNTIF(A1:A8, ">a")' , B26: '=COUNTIF(B1:B8, ">a")',
549
+
550
+ A28: '=COUNTIF(A1:A8, "<20A")' , B28: '=COUNTIF(B1:B8, "<20A")',
551
+ A29: '=COUNTIF(A1:A8, ">20A")' , B29: '=COUNTIF(B1:B8, ">20A")',
552
+
553
+ A31: '=COUNTIF(A1:A8, "<2*")' , B31: '=COUNTIF(B1:B8, "<2*")',
554
+ A32: '=COUNTIF(A1:A8, ">2*")' , B32: '=COUNTIF(B1:B8, ">2*")',
555
+
556
+ A34: '=COUNTIF(A1:A8, "<2?")' , B34: '=COUNTIF(B1:B8, "<2?")',
557
+ A35: '=COUNTIF(A1:A8, ">2?")' , B35: '=COUNTIF(B1:B8, ">2?")',
558
+
559
+ A37: '=COUNTIF(A1:A8, "<>20")' , B37: '=COUNTIF(B1:B8, "<>20")',
560
+ };
561
+
562
+ const gridResult = evaluateGrid(grid);
563
+
564
+ expect(gridResult.A10).toBe(1); expect(gridResult.B10).toBe(1);
565
+ expect(gridResult.A11).toBe(1); expect(gridResult.B11).toBe(1);
566
+ expect(gridResult.A12).toBe(1); expect(gridResult.B12).toBe(1);
567
+
568
+ expect(gridResult.A14).toBe(0); expect(gridResult.B14).toBe(2);
569
+
570
+ expect(gridResult.A16).toBe(5); expect(gridResult.B16).toBe(0);
571
+ expect(gridResult.A17).toBe(2); expect(gridResult.B17).toBe(0);
572
+
573
+ expect(gridResult.A19).toBe(5); expect(gridResult.B19).toBe(0);
574
+ expect(gridResult.A20).toBe(2); expect(gridResult.B20).toBe(0);
575
+
576
+ expect(gridResult.A22).toBe(0); expect(gridResult.B22).toBe(0);
577
+ expect(gridResult.A23).toBe(8); expect(gridResult.B23).toBe(0);
578
+
579
+ expect(gridResult.A25).toBe(0); expect(gridResult.B25).toBe(8);
580
+ expect(gridResult.A26).toBe(0); expect(gridResult.B26).toBe(0);
581
+
582
+ expect(gridResult.A28).toBe(0); expect(gridResult.B28).toBe(4);
583
+ expect(gridResult.A29).toBe(0); expect(gridResult.B29).toBe(4);
584
+
585
+ expect(gridResult.A31).toBe(0); expect(gridResult.B31).toBe(3);
586
+ expect(gridResult.A32).toBe(0); expect(gridResult.B32).toBe(5);
587
+
588
+ expect(gridResult.A34).toBe(0); expect(gridResult.B34).toBe(4); // @compatibility: on google sheet, return 3
589
+ expect(gridResult.A35).toBe(0); expect(gridResult.B35).toBe(4); // @compatibility: on google sheet, return 5
590
+
591
+ expect(gridResult.A37).toBe(7); expect(gridResult.B37).toBe(8);
592
+ });
593
+
594
+ test("COUNTIF: operator tests on type boolean", () => {
595
+ expect(evaluateCell("A41", { A41: "=COUNTIF(A39, TRUE)", A39: "TRUE" })).toBe(1);
596
+ expect(evaluateCell("A42", { A42: '=COUNTIF(A39, "TRUE")', A39: "TRUE" })).toBe(1);
597
+ expect(evaluateCell("A43", { A43: '=COUNTIF(A39, "=TRUE")', A39: "TRUE" })).toBe(1);
598
+
599
+ expect(evaluateCell("B41", { B41: "=COUNTIF(B39, TRUE)", B39: '="TRUE"' })).toBe(0);
600
+ expect(evaluateCell("B42", { B42: '=COUNTIF(B39, "TRUE")', B39: '="TRUE"' })).toBe(0);
601
+ expect(evaluateCell("B43", { B43: '=COUNTIF(B39, "=TRUE")', B39: '="TRUE"' })).toBe(0);
602
+ });
603
+
604
+ test("COUNTIF: operator tests on criterion expression", () => {
605
+ const grid1 = {
606
+ A45: "abc",
607
+ A46: "abd",
608
+ A47: "bcd",
609
+ A48: "b~e",
610
+ A49: "x*y",
611
+ A50: "x?z",
612
+ A51: "zZz",
613
+ A52: "zzz",
614
+
615
+ C45: '=COUNTIF(A45:A52, "abd")',
616
+ C46: '=COUNTIF(A45:A52, "ab")',
617
+ C47: '=COUNTIF(A45:A52, "=abd")',
618
+ C48: '=COUNTIF(A45:A52, "<abd")',
619
+ C49: '=COUNTIF(A45:A52, ">abd")',
620
+ C50: '=COUNTIF(A45:A52, "<>abd")',
621
+ C51: '=COUNTIF(A45:A52, "><abd")',
622
+
623
+ D45: '=COUNTIF(A45:A52, "ab*")',
624
+ D46: '=COUNTIF(A45:A52, "a*")',
625
+ D47: '=COUNTIF(A45:A52, "a*c")',
626
+ D48: '=COUNTIF(A45:A52, "a?")',
627
+ D49: '=COUNTIF(A45:A52, "a?c")',
628
+ D50: '=COUNTIF(A45:A52, "zzz")',
629
+ D51: '=COUNTIF(A45:A52, "x*z")',
630
+ D52: '=COUNTIF(A45:A52, "x~*z")',
631
+ D53: '=COUNTIF(A45:A52, "x~*y")',
632
+ D54: '=COUNTIF(A45:A52, "x?y")',
633
+ D55: '=COUNTIF(A45:A52, "x~?y")',
634
+ D56: '=COUNTIF(A45:A52, "x~?z")',
635
+ D57: '=COUNTIF(A45:A52, "b~e")',
636
+ };
637
+
638
+ const grid1Result = evaluateGrid(grid1);
639
+
640
+ expect(grid1Result.C45).toBe(1);
641
+ expect(grid1Result.C46).toBe(0);
642
+ expect(grid1Result.C47).toBe(1);
643
+ expect(grid1Result.C48).toBe(1);
644
+ expect(grid1Result.C49).toBe(6);
645
+ expect(grid1Result.C50).toBe(7);
646
+ expect(grid1Result.C51).toBe(8);
647
+
648
+ expect(grid1Result.D45).toBe(2);
649
+ expect(grid1Result.D46).toBe(2);
650
+ expect(grid1Result.D47).toBe(1);
651
+ expect(grid1Result.D48).toBe(0);
652
+ expect(grid1Result.D49).toBe(1);
653
+ expect(grid1Result.D50).toBe(2);
654
+ expect(grid1Result.D51).toBe(1);
655
+ expect(grid1Result.D52).toBe(0);
656
+ expect(grid1Result.D53).toBe(1);
657
+ expect(grid1Result.D54).toBe(1);
658
+ expect(grid1Result.D55).toBe(0);
659
+ expect(grid1Result.D56).toBe(1);
660
+ expect(grid1Result.D57).toBe(1);
661
+
662
+ const grid2 = {
663
+ A59: "abc",
664
+ A60: "abd",
665
+ A61: "abe",
666
+ A62: "bcd",
667
+ A63: "bce",
668
+ A64: "b$f",
669
+ A65: "b",
670
+ A66: "cdf",
671
+ A67: "cdg",
672
+
673
+ C59: '=COUNTIF(A59:A67, "> bcf")',
674
+ C60: '=COUNTIF(A59:A67, ">bcf")',
675
+ C61: '=COUNTIF(A59:A67, ">b?f")',
676
+ C62: '=COUNTIF(A59:A67, ">c*")',
677
+
678
+ E59: '=COUNTIF(A59:A67, ">b?e")',
679
+ E60: '=COUNTIF(A59:A67, "<b?e")',
680
+
681
+ E62: '=COUNTIF(A59:A67, ">b*e")',
682
+ E63: '=COUNTIF(A59:A67, "<b*e")',
683
+
684
+ E65: '=COUNTIF(A59:A67, ">b*")',
685
+ E66: '=COUNTIF(A59:A67, "<b*")',
686
+
687
+ F65: '=COUNTIF(A59:A67, ">b")',
688
+ F66: '=COUNTIF(A59:A67, "<b")',
689
+ };
690
+
691
+ const grid2Result = evaluateGrid(grid2);
692
+
693
+ expect(grid2Result.C59).toBe(9);
694
+ expect(grid2Result.C60).toBe(2);
695
+ expect(grid2Result.C61).toBe(4);
696
+ expect(grid2Result.C62).toBe(2);
697
+
698
+ expect(grid2Result.E59).toBe(4);
699
+ expect(grid2Result.E60).toBe(5);
700
+
701
+ expect(grid2Result.E62).toBe(4);
702
+ expect(grid2Result.E63).toBe(5);
703
+
704
+ expect(grid2Result.E65).toBe(4);
705
+ expect(grid2Result.E66).toBe(5);
706
+
707
+ expect(grid2Result.F65).toBe(5);
708
+ expect(grid2Result.F66).toBe(3);
709
+
710
+ const grid3 = {
711
+ A69: "b$f",
712
+
713
+ C69: '=COUNTIF(A69, ">b?f")',
714
+ C70: '=COUNTIF(A69, ">b*f")',
715
+ C71: '=COUNTIF(A69, ">b#f")',
716
+ C72: '=COUNTIF(A69, ">b&f")',
717
+
718
+ D69: '=COUNTIF(A69, "<b?f")',
719
+ D70: '=COUNTIF(A69, "<b*f")',
720
+ D71: '=COUNTIF(A69, "<b#f")',
721
+ D72: '=COUNTIF(A69, "<b&f")',
722
+ };
723
+
724
+ const grid3Result = evaluateGrid(grid3);
725
+
726
+ expect(grid3Result.C69).toBe(0);
727
+ expect(grid3Result.C70).toBe(0);
728
+ expect(grid3Result.C71).toBe(1);
729
+ expect(grid3Result.C72).toBe(0);
730
+
731
+ expect(grid3Result.D69).toBe(1);
732
+ expect(grid3Result.D70).toBe(1);
733
+ expect(grid3Result.D71).toBe(0);
734
+ expect(grid3Result.D72).toBe(1);
735
+
736
+ const grid4 = {
737
+ A74: "ab",
738
+ A75: "abc",
739
+ A76: "abcd",
740
+ A77: "abcde",
741
+
742
+ C74: '=COUNTIF(A74:A77, "a")',
743
+ C75: '=COUNTIF(A74:A77, "ab")',
744
+
745
+ E74: '=COUNTIF(A74:A77, "*")',
746
+ E75: '=COUNTIF(A74:A77, "a*")',
747
+ E76: '=COUNTIF(A74:A77, "ab*")',
748
+ E77: '=COUNTIF(A74:A77, "abc*")',
749
+
750
+ G74: '=COUNTIF(A74:A77, "?")',
751
+ G75: '=COUNTIF(A74:A77, "a?")',
752
+ G76: '=COUNTIF(A74:A77, "ab?")',
753
+ G77: '=COUNTIF(A74:A77, "abc?")',
754
+ };
755
+
756
+ const grid4Result = evaluateGrid(grid4);
757
+
758
+ expect(grid4Result.C74).toBe(0);
759
+ expect(grid4Result.C75).toBe(1);
760
+
761
+ expect(grid4Result.E74).toBe(4);
762
+ expect(grid4Result.E75).toBe(4);
763
+ expect(grid4Result.E76).toBe(4);
764
+ expect(grid4Result.E77).toBe(3);
765
+
766
+ expect(grid4Result.G74).toBe(0);
767
+ expect(grid4Result.G75).toBe(1);
768
+ expect(grid4Result.G76).toBe(1);
769
+ expect(grid4Result.G77).toBe(1);
770
+
771
+ const grid5 = {
772
+ J73: "aslnlsngj",
773
+
774
+ J74: '=COUNTIF(J73, "a*d")',
775
+ J75: '=COUNTIF(J73, "a*j")',
776
+ J76: '=COUNTIF(J73, "a*g")',
777
+ J77: '=COUNTIF(J73, "a*gj")',
778
+ J78: '=COUNTIF(J73, "a*nl*")',
779
+ J79: '=COUNTIF(J73, "a*nl*?n*j")',
780
+
781
+ J80: '=COUNTIF(J73, "<>*")',
782
+ J81: '=COUNTIF(J73, "<>a*")',
783
+ J82: '=COUNTIF(J73, "<>a*b")',
784
+ };
785
+
786
+ const grid5Result = evaluateGrid(grid5);
787
+
788
+ expect(grid5Result.J74).toBe(0);
789
+ expect(grid5Result.J75).toBe(1);
790
+ expect(grid5Result.J76).toBe(0);
791
+ expect(grid5Result.J77).toBe(1);
792
+ expect(grid5Result.J78).toBe(1);
793
+ expect(grid5Result.J79).toBe(1);
794
+
795
+ expect(grid5Result.J80).toBe(0);
796
+ expect(grid5Result.J81).toBe(0);
797
+ expect(grid5Result.J82).toBe(1);
798
+ });
799
+
800
+ //----------------------------------------------------------------------------
801
+ // COUNTIFS
802
+ //----------------------------------------------------------------------------
803
+
804
+ test("COUNTIFS: functional tests on range", () => {
805
+ // prettier-ignore
806
+ const grid = {
807
+ A1: "Jim" , B1: "MN", C1: "100",
808
+ A2: "Sarah", B2: "CA", C2: "125",
809
+ A3: "Jane" , B3: "GA", C3: "200",
810
+ A4: "Steve", B4: "CA", C4: "50" ,
811
+ A5: "Jim" , B5: "WY", C5: "75" ,
812
+ A6: "Joan" , B6: "WA", C6: "150",
813
+ A7: "Jane" , B7: "GA", C7: "200",
814
+ A8: "Jim" , B8: "WY", C8: "50" ,
815
+
816
+ D1: '=COUNTIFS(A1:A8, "JIM", B1:B8, "??" , C1:C8, ">=100")',
817
+ D2: '=COUNTIFS(A1:A8, "JIM", B1:B8, "??" , C1:C8, ">=50")',
818
+ D3: '=COUNTIFS(A1:A8, "JIM", B1:B8, "WY" , C1:C8, ">=50")',
819
+ D4: '=COUNTIFS(A1:A8, "J*" , B1:B8, "W?" , C1:C8, ">=50")',
820
+ D5: '=COUNTIFS(A1:A8, "J*" , B1:B8, "?A" , C1:C8, ">=50")',
821
+ D6: '=COUNTIFS(A1:A8, "J*" , B1:B8, "?A" , C1:C8, "<>200")',
822
+ D7: '=COUNTIFS(A1:A8, "J*" , B1:B8, "*" , C1:C8, "50")',
823
+ D8: '=COUNTIFS(A1:A8, "*a*", B1:B8, "*a*", C1:C8, ">0")',
824
+ };
825
+
826
+ const gridResult = evaluateGrid(grid);
827
+ expect(gridResult.D1).toBe(1);
828
+ expect(gridResult.D2).toBe(3);
829
+ expect(gridResult.D3).toBe(2);
830
+ expect(gridResult.D4).toBe(3);
831
+ expect(gridResult.D5).toBe(3);
832
+ expect(gridResult.D6).toBe(1);
833
+ expect(gridResult.D7).toBe(1);
834
+ expect(gridResult.D8).toBe(4);
835
+ });
836
+
837
+ //----------------------------------------------------------------------------
838
+ // COUNTUNIQUE
839
+ //----------------------------------------------------------------------------
840
+
841
+ test("COUNTUNIQUE: functional tests on simple arguments", () => {
842
+ expect(evaluateCell("A1", { A1: "=countunique()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
843
+ expect(evaluateCell("A1", { A1: "=countunique(,)" })).toBe(0);
844
+ expect(evaluateCell("A1", { A1: "=countunique(0)" })).toBe(1);
845
+ expect(evaluateCell("A1", { A1: "=countunique(1, 2, 3, 1, 2)" })).toBe(3);
846
+ expect(evaluateCell("A1", { A1: "=countunique(1, , 2, , 3)" })).toBe(3);
847
+ expect(evaluateCell("A1", { A1: "=countunique(1.5, 1.4)" })).toBe(2);
848
+ expect(evaluateCell("A1", { A1: '=countunique("Jean Saisrien", "Jean Prendnote")' })).toBe(2);
849
+ expect(evaluateCell("A1", { A1: '=countunique("")' })).toBe(0);
850
+ expect(evaluateCell("A1", { A1: '=countunique(" ")' })).toBe(1);
851
+ expect(evaluateCell("A1", { A1: '=countunique("2", "-2")' })).toBe(2);
852
+ expect(evaluateCell("A1", { A1: '=countunique("2", "")' })).toBe(1);
853
+ expect(evaluateCell("A1", { A1: '=countunique("2", " ")' })).toBe(2);
854
+ expect(evaluateCell("A1", { A1: "=countunique(TRUE, FALSE)" })).toBe(2);
855
+ expect(evaluateCell("A1", { A1: '=countunique(1, "1", TRUE)' })).toBe(3);
856
+ });
857
+
858
+ test("COUNTUNIQUE: functional tests on cell arguments", () => {
859
+ expect(evaluateCell("A1", { A1: "=countunique(A2)", A2: "" })).toBe(0);
860
+ expect(evaluateCell("A1", { A1: "=countunique(A2)", A2: " " })).toBe(1);
861
+ expect(evaluateCell("A1", { A1: "=countunique(A2)", A2: "," })).toBe(1);
862
+ expect(evaluateCell("A1", { A1: "=countunique(A2)", A2: "0" })).toBe(1);
863
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "1", A3: "2" })).toBe(2);
864
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3, A4)", A2: "1", A3: "", A4: "1" })).toBe(
865
+ 1
866
+ );
867
+ expect(
868
+ evaluateCell("A1", {
869
+ A1: "=countunique(A2, A3, A4)",
870
+ A2: "1.4",
871
+ A3: "-1",
872
+ A4: "Jean Peuxplus",
873
+ })
874
+ ).toBe(3);
875
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "", A3: "" })).toBe(0);
876
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: " ", A3: "" })).toBe(1);
877
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "", A3: '=" "' })).toBe(1);
878
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: " ", A3: '=" "' })).toBe(1);
879
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: " ", A3: '=" "' })).toBe(2);
880
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: " ", A3: '=" "' })).toBe(2);
881
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "42", A3: "42" })).toBe(1);
882
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "42", A3: '"42"' })).toBe(2);
883
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "42", A3: "=42" })).toBe(1);
884
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "42", A3: '="42"' })).toBe(2);
885
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: '"42"', A3: '"42"' })).toBe(1);
886
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: '"42"', A3: "=42" })).toBe(2);
887
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: '"42"', A3: '="42"' })).toBe(2);
888
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "=42", A3: "=42" })).toBe(1);
889
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: "=42", A3: '="42"' })).toBe(2);
890
+ expect(evaluateCell("A1", { A1: "=countunique(A2, A3)", A2: '="42"', A3: '="42"' })).toBe(1);
891
+ });
892
+
893
+ test("COUNTUNIQUE: functional tests on simple and cell arguments", () => {
894
+ expect(evaluateCell("A1", { A1: "=countunique(A2,)", A2: "" })).toBe(0);
895
+ expect(evaluateCell("A1", { A1: "=countunique(A2,)", A2: " " })).toBe(1);
896
+ expect(evaluateCell("A1", { A1: "=countunique(A2,)", A2: '=""' })).toBe(0);
897
+ expect(evaluateCell("A1", { A1: "=countunique(A2,)", A2: '=" "' })).toBe(1);
898
+ expect(evaluateCell("A1", { A1: '=countunique(A2, "")', A2: "" })).toBe(0);
899
+ expect(evaluateCell("A1", { A1: '=countunique(A2, "")', A2: " " })).toBe(1);
900
+ expect(evaluateCell("A1", { A1: '=countunique(A2, "")', A2: '=""' })).toBe(0);
901
+ expect(evaluateCell("A1", { A1: '=countunique(A2, "")', A2: '=" "' })).toBe(1);
902
+ expect(evaluateCell("A1", { A1: '=countunique(A2, " ")', A2: "" })).toBe(1);
903
+ expect(evaluateCell("A1", { A1: '=countunique(A2, " ")', A2: " " })).toBe(1);
904
+ expect(evaluateCell("A1", { A1: '=countunique(A2, " ")', A2: '=""' })).toBe(1);
905
+ expect(evaluateCell("A1", { A1: '=countunique(A2, " ")', A2: '=" "' })).toBe(1);
906
+ expect(evaluateCell("A1", { A1: '=countunique(42, "42")' })).toBe(2);
907
+ expect(evaluateCell("A1", { A1: "=countunique(A2, 42)", A2: "42" })).toBe(1);
908
+ expect(evaluateCell("A1", { A1: "=countunique(A2, 42)", A2: '"42"' })).toBe(2);
909
+ expect(evaluateCell("A1", { A1: "=countunique(A2, 42)", A2: "=42" })).toBe(1);
910
+ expect(evaluateCell("A1", { A1: "=countunique(A2, 42)", A2: '="42"' })).toBe(2);
911
+ expect(evaluateCell("A1", { A1: '=countunique(A2, "42")', A2: "42" })).toBe(2);
912
+ expect(evaluateCell("A1", { A1: '=countunique(A2, "42")', A2: '"42"' })).toBe(2);
913
+ expect(evaluateCell("A1", { A1: '=countunique(A2, "42")', A2: "=42" })).toBe(2);
914
+ expect(evaluateCell("A1", { A1: '=countunique(A2, "42")', A2: '="42"' })).toBe(1);
915
+ expect(evaluateCell("A1", { A1: "=countunique(A2, TRUE)", A2: "1" })).toBe(2);
916
+ expect(evaluateCell("A1", { A1: "=countunique(A2, TRUE)", A2: '"1"' })).toBe(2);
917
+ expect(evaluateCell("A1", { A1: "=countunique(A2, TRUE)", A2: "=1" })).toBe(2);
918
+ expect(evaluateCell("A1", { A1: "=countunique(A2, TRUE)", A2: '="1"' })).toBe(2);
919
+ });
920
+
921
+ test("COUNTUNIQUE: functional tests on range arguments", () => {
922
+ const grid = {
923
+ A1: "=COUNTUNIQUE(B2:D3,B4:D4,E2:E3,E4)",
924
+
925
+ A2: "=COUNTUNIQUE(B2:E2)",
926
+ A3: "=COUNTUNIQUE(B3:E3)",
927
+ A4: "=COUNTUNIQUE(B4:E4)",
928
+
929
+ B1: "=COUNTUNIQUE(B2:B4)",
930
+ C1: "=COUNTUNIQUE(C2:C4)",
931
+ D1: "=COUNTUNIQUE(D2:D4)",
932
+ E1: "=COUNTUNIQUE(E2:E4)",
933
+
934
+ B2: "=3",
935
+ C2: "3",
936
+ D2: '"3"',
937
+ E2: '="3"',
938
+
939
+ B3: '=" "',
940
+ C3: "0",
941
+ D3: "Jean Registre",
942
+ E3: '"Jean Titouplin"',
943
+
944
+ B4: " ",
945
+ C4: '""',
946
+ D4: '=""',
947
+ E4: '" "',
948
+ };
949
+
950
+ const gridResult = evaluateGrid(grid);
951
+ expect(gridResult.A1).toBe(9);
952
+ expect(gridResult.A2).toBe(3);
953
+ expect(gridResult.A3).toBe(4);
954
+ expect(gridResult.A4).toBe(3);
955
+ expect(gridResult.B1).toBe(2);
956
+ expect(gridResult.C1).toBe(3);
957
+ expect(gridResult.D1).toBe(2);
958
+ expect(gridResult.E1).toBe(3);
959
+ });
960
+
961
+ //----------------------------------------------------------------------------
962
+ // COUNTUNIQUEIFS
963
+ //----------------------------------------------------------------------------
964
+
965
+ test("COUNTUNIQUEIFS: functional tests on range", () => {
966
+ // prettier-ignore
967
+ const grid = {
968
+ A1: "car" , B1: "4" , C1: "14", D1: "Yes",
969
+ A2: "toy" , B2: "28", C2: "30", D2: "Yes",
970
+ A3: "shirt" , B3: "31", C3: "47", D3: "Yes",
971
+ A4: "car" , B4: "12", C4: "0" , D4: "Yes",
972
+ A5: "toy" , B5: "31", C5: "47", D5: "Yes",
973
+ A6: "milk" , B6: "13", C6: "5" , D6: "No" ,
974
+ A7: "butter", B7: "18", C7: "43", D7: "No" ,
975
+ A8: "shirt" , B8: "24", C8: "7" , D8: "Yes",
976
+ A9: "car" , B9: "44", C9: "28", D9: "No" ,
977
+ A10: '=""' , B10: "22", C10: "23", D10: "No" ,
978
+ B11: "9" , C11: "13", D11: "No" ,
979
+
980
+ A12: '=COUNTUNIQUEIFS(A1:A11, B1:B11, ">20")',
981
+ A13: '=COUNTUNIQUEIFS(A1:A11, B1:B11, ">20", C1:C11, "<30")',
982
+ A14: '=COUNTUNIQUEIFS(A1:A11, D1:D11, "No")',
983
+ };
984
+
985
+ const gridResult = evaluateGrid(grid);
986
+ expect(gridResult.A12).toBe(3);
987
+ expect(gridResult.A13).toBe(2);
988
+ expect(gridResult.A14).toBe(3);
989
+ });
990
+
991
+ //----------------------------------------------------------------------------
992
+ // CSC
993
+ //----------------------------------------------------------------------------
994
+
995
+ test.each([
996
+ ["=3*PI()/2", -1],
997
+ ["=PI()/2", 1],
998
+ ["=-PI()/2", -1],
999
+ ["1", 1.188395106],
1000
+ ["-2", -1.09975017],
1001
+ ])("CSC(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
1002
+ expect(evaluateCell("A1", { A1: "=CSC(A2)", A2: a })).toBeCloseTo(expected, 9);
1003
+ });
1004
+
1005
+ test.each([["0"]])("CSC(%s) - ERROR: take 1 parameter(s), return an error", (a) => {
1006
+ expect(evaluateCell("A1", { A1: "=CSC(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1007
+ });
1008
+
1009
+ //----------------------------------------------------------------------------
1010
+ // CSCH
1011
+ //----------------------------------------------------------------------------
1012
+
1013
+ test.each([
1014
+ ["1", 0.850918128],
1015
+ ["-2", -0.275720565],
1016
+ ["=LN(3)", 0.75],
1017
+ ["=-LN(9)", -0.225],
1018
+ ])("CSCH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
1019
+ expect(evaluateCell("A1", { A1: "=CSCH(A2)", A2: a })).toBeCloseTo(expected, 9);
1020
+ });
1021
+
1022
+ test.each([["0"]])("CSCH(%s) - ERROR: take 1 parameter(s), return an error", (a) => {
1023
+ expect(evaluateCell("A1", { A1: "=CSCH(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1024
+ });
1025
+
1026
+ //----------------------------------------------------------------------------
1027
+ // DECIMAL
1028
+ //----------------------------------------------------------------------------
1029
+
1030
+ test.each([
1031
+ ["0", "2", 0],
1032
+ ["0", "4", 0],
1033
+ ["0", "36", 0],
1034
+ ["1010", "2", 10],
1035
+ ["1010", "4", 68],
1036
+ ["1010", "36", 46692],
1037
+ ["2020", "36", 93384],
1038
+ ["3030", "4", 204],
1039
+ ["-1010", "16", -4112], // @compatibility: return error on parameter 1 on google sheets
1040
+ ["ABAB", "16", 43947],
1041
+ ["ABAB", "36", 481187],
1042
+ ["A1A1", "16", 41377],
1043
+ ["a1a1", "16", 41377],
1044
+ ["a1a1", "36", 468217],
1045
+ ["-ABAB", "16", -43947], // @compatibility: return error on parameter 1 on google sheets
1046
+ ["-ABAB", "36", -481187], // @compatibility: return error on parameter 1 on google sheets
1047
+ ["zzzz", "36", 1679615],
1048
+ ])("DECIMAL(%s, %s) - %s: take 2 parameter(s), return a number", (a, b, expected) => {
1049
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: a, A3: b })).toBe(expected);
1050
+ });
1051
+
1052
+ test.each([
1053
+ ["1010", "-1"],
1054
+ ["1010", "0"],
1055
+ ["1010", "0.1"],
1056
+ ["1010", "1"],
1057
+ ["1010", "37"],
1058
+ ["-", "2"],
1059
+ ["A-", "36"],
1060
+ ["@ABAB", "36"],
1061
+ ["AB AB", "36"],
1062
+ ])("DECIMAL(%s, %s) - error: take 2 parameter(s), return error on parameter 2", (a, b) => {
1063
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: a, A3: b })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1064
+ });
1065
+
1066
+ test.each([
1067
+ ["0.1", "2"],
1068
+ ["2020", "2"],
1069
+ ["-ABAB", "2"],
1070
+ ["-ABAB", "10"],
1071
+ ["ZZZZ", "16"],
1072
+ ["-", "36"],
1073
+ ["A-", "36"],
1074
+ ["@ABAB", "36"],
1075
+ ["ABAB@", "36"],
1076
+ ["ABAB.2", "36"],
1077
+ ["ABAB.21@", "36"],
1078
+ ["AB AB", "36"],
1079
+ ])("DECIMAL(%s, %s) - error: take 2 parameter(s), return error on parameter 1", (a, b) => {
1080
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: a, A3: b })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1081
+ });
1082
+
1083
+ test("DECIMAL: special value testing", () => {
1084
+ expect(evaluateCell("A1", { A1: "=DECIMAL()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1085
+ expect(evaluateCell("A1", { A1: "=DECIMAL( , )" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1086
+ expect(evaluateCell("A1", { A1: "=DECIMAL(42, 12)" })).toBe(50);
1087
+ expect(evaluateCell("A1", { A1: "=DECIMAL( , 12)" })).toBe(0);
1088
+ expect(evaluateCell("A1", { A1: "=DECIMAL(42, )" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1089
+ expect(evaluateCell("A1", { A1: "=DECIMAL(42, TRUE)" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1090
+ expect(evaluateCell("A1", { A1: "=DECIMAL(42, FALSE)" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1091
+ expect(evaluateCell("A1", { A1: "=DECIMAL(TRUE, 10)" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1092
+ expect(evaluateCell("A1", { A1: "=DECIMAL(FALSE, 10)" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1093
+
1094
+ expect(evaluateCell("A1", { A1: '=DECIMAL("" , "")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1095
+ expect(evaluateCell("A1", { A1: '=DECIMAL("" , 12)' })).toBe(0);
1096
+ expect(evaluateCell("A1", { A1: '=DECIMAL(" " , 12)' })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1097
+ expect(evaluateCell("A1", { A1: '=DECIMAL("42", 12)' })).toBe(50);
1098
+ expect(evaluateCell("A1", { A1: '=DECIMAL("42", "12")' })).toBe(50);
1099
+ expect(evaluateCell("A1", { A1: '=DECIMAL("42", "")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1100
+
1101
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "", A3: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1102
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "42", A3: "12" })).toBe(50);
1103
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "", A3: "12" })).toBe(0);
1104
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "42", A3: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1105
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "42", A3: "TRUE" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1106
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "42", A3: "FALSE" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1107
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "42", A3: "FALSE" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1108
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "TRUE", A3: "10" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1109
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: "FALSE", A3: "10" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1110
+
1111
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: '"42"', A3: '"12"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1112
+
1113
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: '=""', A3: '="2"' })).toBe(0);
1114
+ expect(evaluateCell("A1", { A1: "=DECIMAL(A2, A3)", A2: '="42"', A3: '="36"' })).toBe(146);
1115
+ });
1116
+
1117
+ //----------------------------------------------------------------------------
1118
+ // DEGREES
1119
+ //----------------------------------------------------------------------------
1120
+
1121
+ test.each([
1122
+ ["-5", -286.4788975654116], // @compatibility: on google sheets return -286.47889756541(2) and not (16)
1123
+ ["-3.14", -179.90874767107852], // @compatibility: on google sheets return -179.90874767107(9) and not (852)
1124
+ ["0", 0],
1125
+ ["3.14", 179.90874767107852], // @compatibility: on google sheets return 179.90874767107(9) and not (852)
1126
+ ["5", 286.4788975654116], // @compatibility: on google sheets return 286.47889756541(2) and not (16)
1127
+ ["=-PI()", -180],
1128
+ ["=PI()", 180],
1129
+ ["=PI() * 3", 540],
1130
+ ["3.141592653589793", 180],
1131
+ ["3.14159265358979", 179.99999999999983], // @compatibility: on google sheets return 180
1132
+ ["3.1415926535897", 179.99999999999466], // @compatibility: on google sheets return 179.99999999999(5) and not (466)
1133
+ ])("DEGREES(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
1134
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: a })).toBe(expected);
1135
+ });
1136
+
1137
+ test("DEGREES: functional tests on simple arguments", () => {
1138
+ expect(evaluateCell("A1", { A1: "=DEGREES()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1139
+ expect(evaluateCell("A1", { A1: "=DEGREES(0)" })).toBe(0);
1140
+ expect(evaluateCell("A1", { A1: "=DEGREES(PI())" })).toBe(180);
1141
+ expect(evaluateCell("A1", { A1: "=DEGREES(TRUE)" })).toBeCloseTo(57.29577951, 8);
1142
+ expect(evaluateCell("A1", { A1: "=DEGREES(FALSE)" })).toBe(0);
1143
+
1144
+ expect(evaluateCell("A1", { A1: '=DEGREES("")' })).toBe(0);
1145
+ expect(evaluateCell("A1", { A1: '=DEGREES(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1146
+ expect(evaluateCell("A1", { A1: '=DEGREES("hello there")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1147
+
1148
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: "" })).toBe(0);
1149
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1150
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: "0" })).toBe(0);
1151
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: "=PI()" })).toBe(180);
1152
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: "TRUE" })).toBeCloseTo(57.29577951, 8);
1153
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: "FALSE" })).toBe(0);
1154
+
1155
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1156
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1157
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: '"42"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1158
+
1159
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: '=""' })).toBe(0);
1160
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1161
+ expect(evaluateCell("A1", { A1: "=DEGREES(A2)", A2: '="0"' })).toBe(0);
1162
+ });
1163
+
1164
+ //----------------------------------------------------------------------------
1165
+ // EXP
1166
+ //----------------------------------------------------------------------------
1167
+
1168
+ test.each([
1169
+ ["1", Math.exp(1)],
1170
+ ["=LN(1)", 1],
1171
+ ["0", 1],
1172
+ ["-1", 0.3678794412],
1173
+ ["2", 7.389056099],
1174
+ ])("EXP(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
1175
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: a })).toBeCloseTo(expected, 9);
1176
+ });
1177
+
1178
+ test("EXP: special value testing", () => {
1179
+ expect(evaluateCell("A1", { A1: "=EXP()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1180
+ expect(evaluateCell("A1", { A1: "=EXP(TRUE)" })).toBeCloseTo(Math.exp(1), 9);
1181
+ expect(evaluateCell("A1", { A1: "=EXP(FALSE)" })).toBeCloseTo(1, 9);
1182
+
1183
+ expect(evaluateCell("A1", { A1: '=EXP("")' })).toBeCloseTo(1, 9);
1184
+ expect(evaluateCell("A1", { A1: '=EXP(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1185
+ expect(evaluateCell("A1", { A1: '=EXP("1")' })).toBeCloseTo(Math.exp(1), 9);
1186
+
1187
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: "" })).toBeCloseTo(1, 9);
1188
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1189
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: "1" })).toBeCloseTo(Math.exp(1), 9);
1190
+
1191
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1192
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1193
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: '"1"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1194
+
1195
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: '=""' })).toBeCloseTo(1, 9);
1196
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1197
+ expect(evaluateCell("A1", { A1: "=EXP(A2)", A2: '="1"' })).toBeCloseTo(Math.exp(1), 9);
1198
+ });
1199
+
1200
+ //----------------------------------------------------------------------------
1201
+ // FLOOR / FLOOR.MATH / FLOOR.PRECISE
1202
+ //----------------------------------------------------------------------------
1203
+
1204
+ test.each([
1205
+ ["0", 0],
1206
+ ["6", 6],
1207
+ ["6.7", 6],
1208
+ ["7.89", 7],
1209
+ ["-6", -6],
1210
+ ["-6.7", -7],
1211
+ ["-7.89", -8],
1212
+ ])("FLOOR FUNCTIONS(%s) - %s: take 1 parameters, return a number", (a, expected) => {
1213
+ expect(evaluateCell("A1", { A1: "=FLOOR(A2)", A2: a })).toBe(expected);
1214
+ expect(evaluateCell("A1", { A1: "=FLOOR.MATH(A2)", A2: a })).toBe(expected);
1215
+ expect(evaluateCell("A1", { A1: "=FLOOR.PRECISE(A2)", A2: a })).toBe(expected);
1216
+ });
1217
+
1218
+ test.each([
1219
+ ["0", "0", 0],
1220
+ ["0", "0.1", 0],
1221
+ ["0", "-4", 0],
1222
+ ["6", "0", 0], // @compatibility on google sheets: concerning basic floor function, return error div by 0
1223
+ ["6.7", "0", 0], // @compatibility on google sheets: concerning basic floor function, return error div by 0
1224
+ ["-6", "0", 0], // @compatibility on google sheets: concerning basic floor function, return error div by 0
1225
+ ["6", "0.1", 6],
1226
+ ["-6", "0.1", -6],
1227
+ ["6", "0.7", 5.6],
1228
+ ["-6", "0.7", -6.3],
1229
+ ["6.7", "0.2", 6.6],
1230
+ ["-6.7", "0.2", -6.8],
1231
+ ])("FLOOR FUNCTIONS(%s, %s) - %s: take 2 parameters, return a number", (a, b, expected) => {
1232
+ expect(evaluateCell("A1", { A1: "=FLOOR(A2, A3)", A2: a, A3: b })).toBeCloseTo(expected);
1233
+ expect(evaluateCell("A1", { A1: "=FLOOR.MATH(A2, A3)", A2: a, A3: b })).toBeCloseTo(expected);
1234
+ expect(evaluateCell("A1", { A1: "=FLOOR.PRECISE(A2, A3)", A2: a, A3: b })).toBeCloseTo(
1235
+ expected
1236
+ );
1237
+ });
1238
+
1239
+ test.each([
1240
+ ["0", "0.2", 0],
1241
+ ["0", "-0.2", 0],
1242
+ ["7.89", "0.2", 7.8],
1243
+ ["7.89", "-0.2", 7.8],
1244
+ ["-7.89", "0.2", -8],
1245
+ ["-7.89", "-0.2", -8],
1246
+ ])(
1247
+ "FLOOR (MATH/PRECISE) FUNCTIONS(%s, %s) - %s: no effect with negative factor",
1248
+ (a, b, expected) => {
1249
+ expect(evaluateCell("A1", { A1: "=FLOOR.MATH(A2, A3)", A2: a, A3: b })).toBeCloseTo(expected);
1250
+ expect(evaluateCell("A1", { A1: "=FLOOR.PRECISE(A2, A3)", A2: a, A3: b })).toBeCloseTo(
1251
+ expected
1252
+ );
1253
+ }
1254
+ );
1255
+
1256
+ test.each([
1257
+ ["6", "-0.2"],
1258
+ ["7.89", "-0.2"],
1259
+ ])("FLOOR(%s, %s) - error: if value positive, factor can't be negative", (a, b) => {
1260
+ expect(evaluateCell("A1", { A1: "=FLOOR(A2, A3)", A2: a, A3: b })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1261
+ });
1262
+
1263
+ test.each([
1264
+ // Concerning FLOOR function
1265
+ // if "a" is negative and "b" is negative, rounds a number up (and not down)
1266
+ // the nearest integer multiple of b
1267
+ ["-7.89", "0.2", -8],
1268
+ ["-7.89", "-0.2", -7.8],
1269
+ ])("FLOOR(%s, %s) - %s: if factor negative, rouds number down", (a, b, expected) => {
1270
+ expect(evaluateCell("A1", { A1: "=FLOOR(A2, A3)", A2: a, A3: b })).toBeCloseTo(expected, 9);
1271
+ });
1272
+
1273
+ test.each([
1274
+ // Concerning FLOOR.MATH function
1275
+ // if "a" is negative and "c" different 0, rounds a number up (and not down)
1276
+ // the nearest integer multiple of b
1277
+ ["7.89", "0.2", "0", 7.8],
1278
+ ["7.89", "0.2", "1", 7.8],
1279
+ ["-7.89", "0.2", "0", -8],
1280
+ ["-7.89", "0.2", "1", -7.8],
1281
+ ["-7.89", "0.2", "2.2", -7.8],
1282
+ ["-7.89", "0.2", "-2.2", -7.8],
1283
+ ["-7.89", "-0.2", "0", -8],
1284
+ ["-7.89", "-0.2", "1", -7.8],
1285
+ ["-7.89", "-0.2", "2.2", -7.8],
1286
+ ["-7.89", "-0.2", "-2.2", -7.8],
1287
+ ])("FLOOR.MATH(%s, %s, %s) - %s: take 3 parameters, return a number", (a, b, c, expected) => {
1288
+ expect(evaluateCell("A1", { A1: "=FLOOR.MATH(A2, A3, A4)", A2: a, A3: b, A4: c })).toBeCloseTo(
1289
+ expected,
1290
+ 9
1291
+ );
1292
+ });
1293
+
1294
+ function evaluateFloorFunction(functionName: string): void {
1295
+ expect(evaluateCell("A1", { A1: "=" + functionName + "()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1296
+ expect(evaluateCell("A1", { A1: "=" + functionName + "( , )" })).toBeCloseTo(0, 9);
1297
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(-7.89, 0.2)" })).toBeCloseTo(-8, 9);
1298
+ expect(evaluateCell("A1", { A1: "=" + functionName + "( , 0.2)" })).toBeCloseTo(0, 9);
1299
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(-7.89, )" })).toBeCloseTo(0, 9); // @compatibility: on google sheets, return -8
1300
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(-7.89, TRUE)" })).toBeCloseTo(-8, 9);
1301
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(-7.89, FALSE)" })).toBeCloseTo(0, 9); // @compatibility on google sheets: concerning basic floor function, return error div by 0
1302
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(TRUE, 10)" })).toBeCloseTo(0, 9);
1303
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(FALSE, 10)" })).toBeCloseTo(0, 9);
1304
+
1305
+ expect(evaluateCell("A1", { A1: "=" + functionName + '("" , "")' })).toBeCloseTo(0, 9);
1306
+ expect(evaluateCell("A1", { A1: "=" + functionName + '(" " , " ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1307
+ expect(evaluateCell("A1", { A1: "=" + functionName + '("-7.89", "0.2")' })).toBeCloseTo(-8, 9);
1308
+
1309
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "", A3: "" })).toBeCloseTo(
1310
+ 0,
1311
+ 9
1312
+ );
1313
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: " ", A3: " " })).toBe(
1314
+ "#ERROR"
1315
+ ); // @compatibility: on google sheets, return #VALUE!
1316
+ expect(
1317
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "-7.89", A3: "0.2" })
1318
+ ).toBeCloseTo(-8, 9);
1319
+ expect(
1320
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "", A3: "0.2" })
1321
+ ).toBeCloseTo(0, 9);
1322
+ expect(
1323
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "-7.89", A3: "" })
1324
+ ).toBeCloseTo(0, 9); // @compatibility on google sheets: concerning basic floor function, return error div by 0
1325
+ expect(
1326
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "-7.89", A3: "TRUE" })
1327
+ ).toBeCloseTo(-8, 9);
1328
+ expect(
1329
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "-7.89", A3: "FALSE" })
1330
+ ).toBeCloseTo(0, 9); // @compatibility on google sheets: concerning basic floor function, return error div by 0
1331
+ expect(
1332
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "TRUE", A3: "10" })
1333
+ ).toBeCloseTo(0, 9);
1334
+ expect(
1335
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: "FALSE", A3: "10" })
1336
+ ).toBeCloseTo(0, 9);
1337
+
1338
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '""', A3: '""' })).toBe(
1339
+ "#ERROR"
1340
+ ); // @compatibility: on google sheets, return #VALUE!
1341
+ expect(evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '" "', A3: '" "' })).toBe(
1342
+ "#ERROR"
1343
+ ); // @compatibility: on google sheets, return #VALUE!
1344
+ expect(
1345
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '"-7.89"', A3: '"0.2"' })
1346
+ ).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1347
+
1348
+ expect(
1349
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '=""', A3: '=""' })
1350
+ ).toBeCloseTo(0, 9);
1351
+ expect(
1352
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '=" "', A3: '=" "' })
1353
+ ).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1354
+ expect(
1355
+ evaluateCell("A1", { A1: "=" + functionName + "(A2, A3)", A2: '="-7.89"', A3: '="0.2"' })
1356
+ ).toBeCloseTo(-8, 9);
1357
+ }
1358
+
1359
+ test.each([["FLOOR"], ["FLOOR.MATH"], ["FLOOR.PRECISE"]])(
1360
+ "%s: special value testing",
1361
+ (functionName) => {
1362
+ evaluateFloorFunction(functionName);
1363
+ }
1364
+ );
1365
+
1366
+ //----------------------------------------------------------------------------
1367
+ // ISEVEN
1368
+ //----------------------------------------------------------------------------
1369
+
1370
+ test.each([
1371
+ ["-3", false],
1372
+ ["-2.3", true],
1373
+ ["-2", true],
1374
+ ["0", true],
1375
+ ["2", true],
1376
+ ["2.3", true],
1377
+ ["3", false],
1378
+ ])("ISEVEN(%s) - %s: take 1 parameter(s), return a boolean", (a, expected) => {
1379
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: a })).toBe(expected);
1380
+ });
1381
+
1382
+ test("ISEVEN: functional tests on simple arguments", () => {
1383
+ expect(evaluateCell("A1", { A1: "=ISEVEN()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1384
+ expect(evaluateCell("A1", { A1: "=ISEVEN(0)" })).toBe(true);
1385
+ expect(evaluateCell("A1", { A1: "=ISEVEN(1)" })).toBe(false);
1386
+ expect(evaluateCell("A1", { A1: "=ISEVEN(TRUE)" })).toBe(false);
1387
+ expect(evaluateCell("A1", { A1: "=ISEVEN(FALSE)" })).toBe(true);
1388
+
1389
+ expect(evaluateCell("A1", { A1: '=ISEVEN("")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1390
+ expect(evaluateCell("A1", { A1: '=ISEVEN(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1391
+ expect(evaluateCell("A1", { A1: '=ISEVEN("hello there")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1392
+
1393
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: "" })).toBe(true);
1394
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1395
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: "0" })).toBe(true);
1396
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: "1" })).toBe(false);
1397
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: "TRUE" })).toBe(false);
1398
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: "FALSE" })).toBe(true);
1399
+
1400
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1401
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1402
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: '"42"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1403
+
1404
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: '=""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1405
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1406
+ expect(evaluateCell("A1", { A1: "=ISEVEN(A2)", A2: '="0"' })).toBe(true);
1407
+ });
1408
+
1409
+ //----------------------------------------------------------------------------
1410
+ // ISODD
1411
+ //----------------------------------------------------------------------------
1412
+
1413
+ test.each([
1414
+ ["-3", true],
1415
+ ["-2.2", false],
1416
+ ["-2", false],
1417
+ ["0", false],
1418
+ ["2", false],
1419
+ ["2.3", false],
1420
+ ["3", true],
1421
+ ])("ISODD(%s) - %s: take 1 parameter(s), return a boolean", (a, expected) => {
1422
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: a })).toBe(expected);
1423
+ });
1424
+
1425
+ test("ISODD: functional tests on simple arguments", () => {
1426
+ expect(evaluateCell("A1", { A1: "=ISODD()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1427
+ expect(evaluateCell("A1", { A1: "=ISODD(0)" })).toBe(false);
1428
+ expect(evaluateCell("A1", { A1: "=ISODD(1)" })).toBe(true);
1429
+ expect(evaluateCell("A1", { A1: "=ISODD(TRUE)" })).toBe(true);
1430
+ expect(evaluateCell("A1", { A1: "=ISODD(FALSE)" })).toBe(false);
1431
+
1432
+ expect(evaluateCell("A1", { A1: '=ISODD("")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1433
+ expect(evaluateCell("A1", { A1: '=ISODD(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1434
+ expect(evaluateCell("A1", { A1: '=ISODD("hello there")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1435
+
1436
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: "" })).toBe(false);
1437
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1438
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: "0" })).toBe(false);
1439
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: "1" })).toBe(true);
1440
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: "TRUE" })).toBe(true);
1441
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: "FALSE" })).toBe(false);
1442
+
1443
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1444
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1445
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: '"42"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1446
+
1447
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: '=""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1448
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1449
+ expect(evaluateCell("A1", { A1: "=ISODD(A2)", A2: '="0"' })).toBe(false);
1450
+ });
1451
+
1452
+ //----------------------------------------------------------------------------
1453
+ // LN
1454
+ //----------------------------------------------------------------------------
1455
+
1456
+ test.each([
1457
+ ["1", 0],
1458
+ ["=EXP(1)", 1],
1459
+ ["2", 0.6931471806],
1460
+ ["9", Math.log(3) + Math.log(3)],
1461
+ ])("LN(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
1462
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: a })).toBeCloseTo(expected, 9);
1463
+ });
1464
+
1465
+ test.each([["0"], ["-42"]])("LN(%s) - %s: take 1 parameter(s), return an error", (a) => {
1466
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1467
+ });
1468
+
1469
+ test("LN: special value testing", () => {
1470
+ expect(evaluateCell("A1", { A1: "=LN()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1471
+ expect(evaluateCell("A1", { A1: "=LN(TRUE)" })).toBeCloseTo(0, 9);
1472
+ expect(evaluateCell("A1", { A1: "=LN(FALSE)" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1473
+
1474
+ expect(evaluateCell("A1", { A1: '=LN("")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1475
+ expect(evaluateCell("A1", { A1: '=LN(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1476
+ expect(evaluateCell("A1", { A1: '=LN("1")' })).toBeCloseTo(0, 9);
1477
+
1478
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1479
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1480
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: "1" })).toBeCloseTo(0, 9);
1481
+
1482
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1483
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1484
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: '"1"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1485
+
1486
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: '=""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1487
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1488
+ expect(evaluateCell("A1", { A1: "=LN(A2)", A2: '="1"' })).toBeCloseTo(0, 9);
1489
+ });
1490
+
1491
+ //----------------------------------------------------------------------------
1492
+ // MOD
1493
+ //----------------------------------------------------------------------------
1494
+
1495
+ test.each([
1496
+ ["-42", "-10", -2],
1497
+ ["-42", "-2.2", -0.2],
1498
+ ["-42", "-2", 0],
1499
+ ["-42", "2", 0],
1500
+ ["-42", "2.2", 2],
1501
+ ["-42", "10", 8],
1502
+ ["0", "-10", 0],
1503
+ ["0", "-2.2", 0],
1504
+ ["0", "-2", 0],
1505
+ ["0", "2", 0],
1506
+ ["0", "2.2", 0],
1507
+ ["0", "10", 0],
1508
+ ["2.2", "-10", -7.8],
1509
+ ["2.2", "-2.2", 0],
1510
+ ["2.2", "-2", -1.8],
1511
+ ["2.2", "2", 0.2],
1512
+ ["2.2", "2.2", 0],
1513
+ ["2.2", "10", 2.2],
1514
+ ])("MOD(%s, %s) - %s: take 2 parameter(s), return a number", (a, b, expected) => {
1515
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: a, A3: b })).toBeCloseTo(expected, 9);
1516
+ });
1517
+
1518
+ test.each([
1519
+ ["-42", "0"],
1520
+ ["0", "0"],
1521
+ ["2.2", "0"],
1522
+ ])("MOD(%s, %s) - error: take 2 parameter(s), return error on parameter 2", (a, b) => {
1523
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: a, A3: b })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1524
+ });
1525
+
1526
+ test("MOD: special value testing", () => {
1527
+ expect(evaluateCell("A1", { A1: "=MOD()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1528
+ expect(evaluateCell("A1", { A1: "=MOD( , )" })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1529
+ expect(evaluateCell("A1", { A1: "=MOD(42, 12)" })).toBe(6);
1530
+ expect(evaluateCell("A1", { A1: "=MOD( , 12)" })).toBe(0);
1531
+ expect(evaluateCell("A1", { A1: "=MOD(42, )" })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1532
+ expect(evaluateCell("A1", { A1: "=MOD(42.42, TRUE)" })).toBeCloseTo(0.42, 9);
1533
+ expect(evaluateCell("A1", { A1: "=MOD(42, FALSE)" })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1534
+ expect(evaluateCell("A1", { A1: "=MOD(TRUE, 10)" })).toBe(1);
1535
+ expect(evaluateCell("A1", { A1: "=MOD(FALSE, 10)" })).toBe(0);
1536
+
1537
+ expect(evaluateCell("A1", { A1: '=MOD("" , "")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1538
+ expect(evaluateCell("A1", { A1: '=MOD("" , 12)' })).toBe(0);
1539
+ expect(evaluateCell("A1", { A1: '=MOD(" " , 12)' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1540
+ expect(evaluateCell("A1", { A1: '=MOD("42", 12)' })).toBe(6);
1541
+ expect(evaluateCell("A1", { A1: '=MOD("42", "12")' })).toBe(6);
1542
+ expect(evaluateCell("A1", { A1: '=MOD("42", "")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1543
+
1544
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: "", A3: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1545
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: "42", A3: "12" })).toBe(6);
1546
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: "", A3: "12" })).toBe(0);
1547
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: "42", A3: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1548
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: "42.42", A3: "TRUE" })).toBeCloseTo(
1549
+ 0.42,
1550
+ 9
1551
+ );
1552
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: "42.42", A3: "FALSE" })).toBe("#ERROR"); // @compatibility: on google sheets, return #DIV/0!
1553
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: "TRUE", A3: "10" })).toBe(1);
1554
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: "FALSE", A3: "10" })).toBe(0);
1555
+
1556
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: '"42"', A3: '"12"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1557
+
1558
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: '=""', A3: '="2"' })).toBe(0);
1559
+ expect(evaluateCell("A1", { A1: "=MOD(A2, A3)", A2: '="42"', A3: '="36"' })).toBe(6);
1560
+ });
1561
+
1562
+ //----------------------------------------------------------------------------
1563
+ // ODD
1564
+ //----------------------------------------------------------------------------
1565
+
1566
+ test.each([
1567
+ ["-3.9", -5],
1568
+ ["-3.1", -5],
1569
+ ["-3", -3],
1570
+ ["-2.9", -3],
1571
+ ["-2.1", -3],
1572
+ ["-2", -3],
1573
+ ["0", 1],
1574
+ ["2", 3],
1575
+ ["2.1", 3],
1576
+ ["2.9", 3],
1577
+ ["3", 3],
1578
+ ["3.1", 5],
1579
+ ["3.9", 5],
1580
+ ])("ODD(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
1581
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: a })).toBe(expected);
1582
+ });
1583
+
1584
+ test("ODD: functional tests on simple arguments", () => {
1585
+ expect(evaluateCell("A1", { A1: "=ODD()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1586
+ expect(evaluateCell("A1", { A1: "=ODD(0)" })).toBe(1);
1587
+ expect(evaluateCell("A1", { A1: "=ODD(2)" })).toBe(3);
1588
+ expect(evaluateCell("A1", { A1: "=ODD(TRUE)" })).toBe(1);
1589
+ expect(evaluateCell("A1", { A1: "=ODD(FALSE)" })).toBe(1);
1590
+
1591
+ expect(evaluateCell("A1", { A1: '=ODD("")' })).toBe(1);
1592
+ expect(evaluateCell("A1", { A1: '=ODD(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1593
+ expect(evaluateCell("A1", { A1: '=ODD("hello there")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1594
+
1595
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: "" })).toBe(1);
1596
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1597
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: "0" })).toBe(1);
1598
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: "2" })).toBe(3);
1599
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: "TRUE" })).toBe(1);
1600
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: "FALSE" })).toBe(1);
1601
+
1602
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1603
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1604
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: '"42"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1605
+
1606
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: '=""' })).toBe(1);
1607
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1608
+ expect(evaluateCell("A1", { A1: "=ODD(A2)", A2: '="42"' })).toBe(43);
1609
+ });
1610
+
1611
+ //----------------------------------------------------------------------------
1612
+ // PI
1613
+ //----------------------------------------------------------------------------
1614
+
1615
+ test("PI", () => {
1616
+ expect(evaluateCell("A1", { A1: "=PI()" })).toBeCloseTo(Math.PI, 9); // @compatibility: on google sheets return 3.14159265358979
1617
+ });
1618
+
1619
+ //----------------------------------------------------------------------------
1620
+ // POWER
1621
+ //----------------------------------------------------------------------------
1622
+
1623
+ test.each([
1624
+ ["0", "0", 1],
1625
+ ["0", "0.5", 0],
1626
+ ["4", "0", 1],
1627
+ ["0", "4", 0],
1628
+ ["4", "2", 16],
1629
+ ["-4", "2", 16],
1630
+ ["4", "3", 64],
1631
+ ["-4", "3", -64],
1632
+ ["4", "0.5", 2],
1633
+ ["4", "-0.5", 0.5],
1634
+ ["4", "-2", 0.0625],
1635
+ ])("POWER(%s, %s) - %s: take 2 parameter(s), return a number", (a, b, expected) => {
1636
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: a, A3: b })).toBe(expected);
1637
+ });
1638
+
1639
+ test.each([
1640
+ ["-4", "0.5"],
1641
+ ["-4", "1.5"],
1642
+ ["-4", "0.2"],
1643
+ ])("POWER(%s, %s) - error: take 2 parameter(s), return an error on parameter 2", (a, b) => {
1644
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: a, A3: b })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1645
+ });
1646
+
1647
+ test("POWER: special value testing", () => {
1648
+ expect(evaluateCell("A1", { A1: "=POWER()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1649
+ expect(evaluateCell("A1", { A1: "=POWER( , )" })).toBe(1);
1650
+ expect(evaluateCell("A1", { A1: "=POWER(42, 2)" })).toBe(1764);
1651
+ expect(evaluateCell("A1", { A1: "=POWER( , 12)" })).toBe(0);
1652
+ expect(evaluateCell("A1", { A1: "=POWER(42, )" })).toBe(1);
1653
+ expect(evaluateCell("A1", { A1: "=POWER(42.42, TRUE)" })).toBe(42.42);
1654
+ expect(evaluateCell("A1", { A1: "=POWER(42.42, FALSE)" })).toBe(1);
1655
+ expect(evaluateCell("A1", { A1: "=POWER(TRUE, 10)" })).toBe(1);
1656
+ expect(evaluateCell("A1", { A1: "=POWER(FALSE, 10)" })).toBe(0);
1657
+
1658
+ expect(evaluateCell("A1", { A1: '=POWER("" , "")' })).toBe(1);
1659
+ expect(evaluateCell("A1", { A1: '=POWER("" , 12)' })).toBe(0);
1660
+ expect(evaluateCell("A1", { A1: '=POWER(" " , 12)' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1661
+ expect(evaluateCell("A1", { A1: '=POWER("42", 2)' })).toBe(1764);
1662
+ expect(evaluateCell("A1", { A1: '=POWER("42", "2")' })).toBe(1764);
1663
+ expect(evaluateCell("A1", { A1: '=POWER("42", "")' })).toBe(1);
1664
+
1665
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: "", A3: "" })).toBe(1);
1666
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: "42", A3: "2" })).toBe(1764);
1667
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: "", A3: "12" })).toBe(0);
1668
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: "42", A3: "" })).toBe(1);
1669
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: "42.42", A3: "TRUE" })).toBe(42.42);
1670
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: "42.42", A3: "FALSE" })).toBe(1);
1671
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: "TRUE", A3: "10" })).toBe(1);
1672
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: "FALSE", A3: "10" })).toBe(0);
1673
+
1674
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: '"42"', A3: '"12"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1675
+
1676
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: '=""', A3: '="2"' })).toBe(0);
1677
+ expect(evaluateCell("A1", { A1: "=POWER(A2, A3)", A2: '="42"', A3: '="2"' })).toBe(1764);
1678
+ });
1679
+
1680
+ //----------------------------------------------------------------------------
1681
+ // PRODUCT
1682
+ //----------------------------------------------------------------------------
1683
+
1684
+ test("PRODUCT: functional tests on simple arguments", () => {
1685
+ expect(evaluateCell("A1", { A1: "=PRODUCT()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1686
+ expect(evaluateCell("A1", { A1: "=PRODUCT(,)" })).toBe(0);
1687
+ expect(evaluateCell("A1", { A1: "=PRODUCT(0)" })).toBe(0);
1688
+ expect(evaluateCell("A1", { A1: "=PRODUCT(1, 2, 3, 1, 2)" })).toBe(12);
1689
+ expect(evaluateCell("A1", { A1: "=PRODUCT(1, , 2, , 3)" })).toBe(6);
1690
+ expect(evaluateCell("A1", { A1: "=PRODUCT(1.5, 1.4)" })).toBeCloseTo(2.1);
1691
+ expect(evaluateCell("A1", { A1: '=PRODUCT("Jean Chante", "Jean Courage")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1692
+ expect(evaluateCell("A1", { A1: '=PRODUCT("")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1693
+ expect(evaluateCell("A1", { A1: '=PRODUCT(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1694
+ expect(evaluateCell("A1", { A1: '=PRODUCT("2", "-2")' })).toBe(-4);
1695
+ expect(evaluateCell("A1", { A1: '=PRODUCT("2", "")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1696
+ expect(evaluateCell("A1", { A1: '=PRODUCT("2", " ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1697
+ expect(evaluateCell("A1", { A1: "=PRODUCT(TRUE, FALSE)" })).toBe(0);
1698
+ expect(evaluateCell("A1", { A1: '=PRODUCT(1, "1", TRUE)' })).toBe(1);
1699
+ });
1700
+
1701
+ test("PRODUCT: functional tests on cell arguments", () => {
1702
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2)", A2: "" })).toBe(0);
1703
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2)", A2: " " })).toBe(0);
1704
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2)", A2: "," })).toBe(0);
1705
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2)", A2: "0" })).toBe(0);
1706
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "1", A3: "2" })).toBe(2);
1707
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3, A4)", A2: "1", A3: "", A4: "1" })).toBe(1);
1708
+ expect(
1709
+ evaluateCell("A1", { A1: "=PRODUCT(A2, A3, A4)", A2: "1.5", A3: "-10", A4: "Jean Brasse" })
1710
+ ).toBe(-15);
1711
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "", A3: "" })).toBe(0);
1712
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: " ", A3: "" })).toBe(0);
1713
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "", A3: '=" "' })).toBe(0);
1714
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: " ", A3: '=" "' })).toBe(0);
1715
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: " ", A3: '=" "' })).toBe(0);
1716
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: " ", A3: '=" "' })).toBe(0);
1717
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "42", A3: "42" })).toBe(1764);
1718
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "42", A3: '"42"' })).toBe(42);
1719
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "42", A3: "=42" })).toBe(1764);
1720
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "42", A3: '="42"' })).toBe(42);
1721
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: '"42"', A3: '"42"' })).toBe(0);
1722
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: '"42"', A3: "=42" })).toBe(42);
1723
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: '"42"', A3: '="42"' })).toBe(0);
1724
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "=42", A3: "=42" })).toBe(1764);
1725
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: "=42", A3: '="42"' })).toBe(42);
1726
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, A3)", A2: '="42"', A3: '="42"' })).toBe(0);
1727
+ });
1728
+
1729
+ test("PRODUCT: functional tests on simple and cell arguments", () => {
1730
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2,)", A2: "" })).toBe(0);
1731
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2,)", A2: " " })).toBe(0);
1732
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2,)", A2: '=""' })).toBe(0);
1733
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2,)", A2: '=" "' })).toBe(0);
1734
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, "")', A2: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1735
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, "")', A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1736
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, "")', A2: '=""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1737
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, "")', A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1738
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, " ")', A2: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1739
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, " ")', A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1740
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, " ")', A2: '=""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1741
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, " ")', A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1742
+ expect(evaluateCell("A1", { A1: '=PRODUCT(42, "42")' })).toBe(1764);
1743
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, 42)", A2: "42" })).toBe(1764);
1744
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, 42)", A2: '"42"' })).toBe(42);
1745
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, 42)", A2: "=42" })).toBe(1764);
1746
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, 42)", A2: '="42"' })).toBe(42);
1747
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, "42")', A2: "42" })).toBe(1764);
1748
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, "42")', A2: '"42"' })).toBe(42);
1749
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, "42")', A2: "=42" })).toBe(1764);
1750
+ expect(evaluateCell("A1", { A1: '=PRODUCT(A2, "42")', A2: '="42"' })).toBe(42);
1751
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, TRUE)", A2: "1" })).toBe(1);
1752
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, TRUE)", A2: '"1"' })).toBe(1);
1753
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, TRUE)", A2: "=1" })).toBe(1);
1754
+ expect(evaluateCell("A1", { A1: "=PRODUCT(A2, TRUE)", A2: '="1"' })).toBe(1);
1755
+ });
1756
+
1757
+ test("PRODUCT: functional tests on range arguments", () => {
1758
+ const grid = {
1759
+ A1: "=PRODUCT(B2:D3,B4:D4,E2:E3,E4)",
1760
+
1761
+ A2: "=PRODUCT(B2:E2)",
1762
+ A3: "=PRODUCT(B3:E3)",
1763
+ A4: "=PRODUCT(B4:E4)",
1764
+
1765
+ B1: "=PRODUCT(B2:B4)",
1766
+ C1: "=PRODUCT(C2:C4)",
1767
+ D1: "=PRODUCT(D2:D4)",
1768
+ E1: "=PRODUCT(E2:E4)",
1769
+
1770
+ B2: "=3",
1771
+ C2: "3",
1772
+ D2: '"3"',
1773
+ E2: '="3"',
1774
+
1775
+ B3: '=" "',
1776
+ C3: "0",
1777
+ D3: "Jean Jardindu",
1778
+ E3: '"Jean Fortroche"',
1779
+
1780
+ B4: " ",
1781
+ C4: '""',
1782
+ D4: '=""',
1783
+ E4: '" "',
1784
+ };
1785
+
1786
+ const gridResult = evaluateGrid(grid);
1787
+ expect(gridResult.A1).toBe(0);
1788
+ expect(gridResult.A2).toBe(9);
1789
+ expect(gridResult.A3).toBe(0);
1790
+ expect(gridResult.A4).toBe(0);
1791
+ expect(gridResult.B1).toBe(3);
1792
+ expect(gridResult.C1).toBe(0);
1793
+ expect(gridResult.D1).toBe(0);
1794
+ expect(gridResult.E1).toBe(0);
1795
+ });
1796
+
1797
+ //----------------------------------------------------------------------------
1798
+ // RAND
1799
+ //----------------------------------------------------------------------------
1800
+
1801
+ test("RAND(): return a number", () => {
1802
+ expect(evaluateCell("A1", { A1: "=RAND()" })).toBeGreaterThanOrEqual(0);
1803
+ expect(evaluateCell("A1", { A1: "=RAND()" })).toBeLessThan(1);
1804
+ });
1805
+
1806
+ //----------------------------------------------------------------------------
1807
+ // RANDBETWEEN
1808
+ //----------------------------------------------------------------------------
1809
+
1810
+ test.each([
1811
+ ["0", "0", 0],
1812
+ ["42", "42", 42],
1813
+ ["-42", "-42", -42],
1814
+ ["1.1", "2", 2],
1815
+ ])("RANDBETWEEN(%s, %s) - %s: take 2 parameter(s), return a number", (a, b, expected) => {
1816
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: a, A3: b })).toBe(expected);
1817
+ });
1818
+
1819
+ test.each([
1820
+ ["-42", "42"],
1821
+ ["24", "42"],
1822
+ ["-42", "-24"],
1823
+ ])("RANDBETWEEN(%s, %s): take 2 parameter(s), return a number", (a, b) => {
1824
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: a, A3: b })).toBeGreaterThanOrEqual(
1825
+ toNumber(a)
1826
+ );
1827
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: a, A3: b })).toBeLessThanOrEqual(
1828
+ toNumber(b)
1829
+ );
1830
+ });
1831
+
1832
+ test.each([
1833
+ ["1.1", "1.2"], // @compatibility: on google sheets, return 2
1834
+ ["-24", "-42"],
1835
+ ["42", "24"],
1836
+ ])("RANDBETWEEN(%s, %s) - error: take 2 parameter(s), return an error", (a, b) => {
1837
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: a, A3: b })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1838
+ });
1839
+
1840
+ test("RANDBETWEEN: special value testing", () => {
1841
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1842
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN( , )" })).toBe(0);
1843
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(2, 2)" })).toBe(2);
1844
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN( , 0)" })).toBe(0);
1845
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(42, )" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1846
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(42.42, TRUE)" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1847
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(42.42, FALSE)" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1848
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(TRUE, 1)" })).toBe(1);
1849
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(FALSE, 0)" })).toBe(0);
1850
+
1851
+ expect(evaluateCell("A1", { A1: '=RANDBETWEEN("" , "")' })).toBe(0);
1852
+ expect(evaluateCell("A1", { A1: '=RANDBETWEEN("" , 0)' })).toBe(0);
1853
+ expect(evaluateCell("A1", { A1: '=RANDBETWEEN(" " , 12)' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1854
+ expect(evaluateCell("A1", { A1: '=RANDBETWEEN("42", 42)' })).toBe(42);
1855
+ expect(evaluateCell("A1", { A1: '=RANDBETWEEN("42", "42")' })).toBe(42);
1856
+ expect(evaluateCell("A1", { A1: '=RANDBETWEEN("42", "")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1857
+
1858
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: "", A3: "" })).toBe(0);
1859
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: "2", A3: "2" })).toBe(2);
1860
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: "", A3: "0" })).toBe(0);
1861
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: "42", A3: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!
1862
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: "42.42", A3: "TRUE" })).toBe(
1863
+ "#ERROR"
1864
+ ); // @compatibility: on google sheets, return #NUM!
1865
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: "42.42", A3: "FALSE" })).toBe(
1866
+ "#ERROR"
1867
+ ); // @compatibility: on google sheets, return #NUM!
1868
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: "TRUE", A3: "1" })).toBe(1);
1869
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: "FALSE", A3: "0" })).toBe(0);
1870
+
1871
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: '"42"', A3: '"42"' })).toBe(
1872
+ "#ERROR"
1873
+ ); // @compatibility: on google sheets, return #VALUE!
1874
+
1875
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: '=""', A3: '="0"' })).toBe(0);
1876
+ expect(evaluateCell("A1", { A1: "=RANDBETWEEN(A2, A3)", A2: '="42"', A3: '="42"' })).toBe(42);
1877
+ });
1878
+
1879
+ //----------------------------------------------------------------------------
1880
+ // ROUND
1881
+ //----------------------------------------------------------------------------
1882
+
1883
+ test.each([
1884
+ ["-1.6", -2],
1885
+ ["-1.5", -2],
1886
+ ["-1.4", -1],
1887
+ ["0", 0],
1888
+ ["1.4", 1],
1889
+ ["1.5", 2],
1890
+ ["1.6", 2],
1891
+ ])("ROUND(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
1892
+ expect(evaluateCell("A1", { A1: "=ROUND(A2)", A2: a })).toBe(expected);
1893
+ });
1894
+
1895
+ test.each([
1896
+ ["0.3", "2", 0.3],
1897
+ ["0.34", "2", 0.34],
1898
+ ["0.345", "2", 0.35],
1899
+ ["-0.3", "2", -0.3],
1900
+ ["-0.34", "2", -0.34],
1901
+ ["-0.345", "2", -0.35],
1902
+ ["0.345", "1.9", 0.3],
1903
+ ["-0.345", "1.9", -0.3],
1904
+ ["4", "-1", 0],
1905
+ ["5", "-1", 10],
1906
+ ["50", "-2", 100],
1907
+ ["-5", "-1", -10],
1908
+ ["-50", "-2", -100],
1909
+ ["5", "-1.9", 10],
1910
+ ["-5", "-1.9", -10],
1911
+ ])("ROUND(%s, %s) - %s: take 2 parameter(s), return a number", (a, b, expected) => {
1912
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: a, A3: b })).toBe(expected);
1913
+ });
1914
+
1915
+ test("ROUND: special value testing", () => {
1916
+ expect(evaluateCell("A1", { A1: "=ROUND()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1917
+ expect(evaluateCell("A1", { A1: "=ROUND( , )" })).toBe(0);
1918
+ expect(evaluateCell("A1", { A1: "=ROUND(42, -1)" })).toBe(40);
1919
+ expect(evaluateCell("A1", { A1: "=ROUND( , 42)" })).toBe(0);
1920
+ expect(evaluateCell("A1", { A1: "=ROUND(42, )" })).toBe(42);
1921
+ expect(evaluateCell("A1", { A1: "=ROUND(42.42, TRUE)" })).toBe(42.4);
1922
+ expect(evaluateCell("A1", { A1: "=ROUND(42.42, FALSE)" })).toBe(42);
1923
+ expect(evaluateCell("A1", { A1: "=ROUND(TRUE, 10)" })).toBe(1);
1924
+ expect(evaluateCell("A1", { A1: "=ROUND(FALSE, 10)" })).toBe(0);
1925
+
1926
+ expect(evaluateCell("A1", { A1: '=ROUND("" , "")' })).toBe(0);
1927
+ expect(evaluateCell("A1", { A1: '=ROUND("" , 12)' })).toBe(0);
1928
+ expect(evaluateCell("A1", { A1: '=ROUND(" " , 0)' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1929
+ expect(evaluateCell("A1", { A1: '=ROUND("42", -1)' })).toBe(40);
1930
+ expect(evaluateCell("A1", { A1: '=ROUND("42", "-1")' })).toBe(40);
1931
+ expect(evaluateCell("A1", { A1: '=ROUND("42", "")' })).toBe(42);
1932
+
1933
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: "", A3: "" })).toBe(0);
1934
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: "42", A3: "-1" })).toBe(40);
1935
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: "", A3: "42" })).toBe(0);
1936
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: "42", A3: "" })).toBe(42);
1937
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: "42.42", A3: "TRUE" })).toBe(42.4);
1938
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: "42.42", A3: "FALSE" })).toBe(42);
1939
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: "TRUE", A3: "10" })).toBe(1);
1940
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: "FALSE", A3: "10" })).toBe(0);
1941
+
1942
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: '"42"', A3: '"42"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1943
+
1944
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: '=""', A3: '="2"' })).toBe(0);
1945
+ expect(evaluateCell("A1", { A1: "=ROUND(A2, A3)", A2: '="42"', A3: '="-1"' })).toBe(40);
1946
+ });
1947
+
1948
+ //----------------------------------------------------------------------------
1949
+ // ROUNDDOWN
1950
+ //----------------------------------------------------------------------------
1951
+
1952
+ test.each([
1953
+ ["-1.9", -1],
1954
+ ["-1.5", -1],
1955
+ ["-1.4", -1],
1956
+ ["0", 0],
1957
+ ["1.4", 1],
1958
+ ["1.5", 1],
1959
+ ["1.9", 1],
1960
+ ])("ROUNDDOWN(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
1961
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2)", A2: a })).toBe(expected);
1962
+ });
1963
+
1964
+ test.each([
1965
+ ["0.3", "2", 0.3],
1966
+ ["0.34", "2", 0.34],
1967
+ ["0.349", "2", 0.34],
1968
+ ["-0.3", "2", -0.3],
1969
+ ["-0.34", "2", -0.34],
1970
+ ["-0.349", "2", -0.34],
1971
+ ["0.349", "1.9", 0.3],
1972
+ ["-0.349", "1.9", -0.3],
1973
+ ["9", "-1", 0],
1974
+ ["19", "-1", 10],
1975
+ ["599", "-2", 500],
1976
+ ["-19", "-1", -10],
1977
+ ["-599", "-2", -500],
1978
+ ["19", "-1.9", 10],
1979
+ ["-19", "-1.9", -10],
1980
+ ])("ROUNDDOWN(%s, %s) - %s: take 2 parameter(s), return a number", (a, b, expected) => {
1981
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: a, A3: b })).toBe(expected);
1982
+ });
1983
+
1984
+ test("ROUNDDOWN: special value testing", () => {
1985
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
1986
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN( , )" })).toBe(0);
1987
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(49, -1)" })).toBe(40);
1988
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN( , 49)" })).toBe(0);
1989
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(49, )" })).toBe(49);
1990
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(49.49, TRUE)" })).toBe(49.4);
1991
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(49.49, FALSE)" })).toBe(49);
1992
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(TRUE, 10)" })).toBe(1);
1993
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(FALSE, 10)" })).toBe(0);
1994
+
1995
+ expect(evaluateCell("A1", { A1: '=ROUNDDOWN("" , "")' })).toBe(0);
1996
+ expect(evaluateCell("A1", { A1: '=ROUNDDOWN("" , 12)' })).toBe(0);
1997
+ expect(evaluateCell("A1", { A1: '=ROUNDDOWN(" " , 0)' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
1998
+ expect(evaluateCell("A1", { A1: '=ROUNDDOWN("49", -1)' })).toBe(40);
1999
+ expect(evaluateCell("A1", { A1: '=ROUNDDOWN("49", "-1")' })).toBe(40);
2000
+ expect(evaluateCell("A1", { A1: '=ROUNDDOWN("49", "")' })).toBe(49);
2001
+
2002
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: "", A3: "" })).toBe(0);
2003
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: "49", A3: "-1" })).toBe(40);
2004
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: "", A3: "49" })).toBe(0);
2005
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: "49", A3: "" })).toBe(49);
2006
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: "49.49", A3: "TRUE" })).toBe(49.4);
2007
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: "49.49", A3: "FALSE" })).toBe(49);
2008
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: "TRUE", A3: "10" })).toBe(1);
2009
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: "FALSE", A3: "10" })).toBe(0);
2010
+
2011
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: '"49"', A3: '"49"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2012
+
2013
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: '=""', A3: '="2"' })).toBe(0);
2014
+ expect(evaluateCell("A1", { A1: "=ROUNDDOWN(A2, A3)", A2: '="49"', A3: '="-1"' })).toBe(40);
2015
+ });
2016
+
2017
+ //----------------------------------------------------------------------------
2018
+ // ROUNDUP
2019
+ //----------------------------------------------------------------------------
2020
+
2021
+ test.each([
2022
+ ["-1.6", -2],
2023
+ ["-1.5", -2],
2024
+ ["-1.1", -2],
2025
+ ["0", 0],
2026
+ ["1.1", 2],
2027
+ ["1.5", 2],
2028
+ ["1.6", 2],
2029
+ ])("ROUNDUP(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2030
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2)", A2: a })).toBe(expected);
2031
+ });
2032
+
2033
+ test.each([
2034
+ ["0.3", "2", 0.3],
2035
+ ["0.34", "2", 0.34],
2036
+ ["0.341", "2", 0.35],
2037
+ ["-0.3", "2", -0.3],
2038
+ ["-0.34", "2", -0.34],
2039
+ ["-0.341", "2", -0.35],
2040
+ ["0.311", "1.9", 0.4],
2041
+ ["-0.311", "1.9", -0.4],
2042
+ ["1", "-1", 10],
2043
+ ["11", "-1", 20],
2044
+ ["1", "-2", 100],
2045
+ ["-11", "-1", -20],
2046
+ ["1", "-2", 100],
2047
+ ["11", "-1.9", 20],
2048
+ ["-11", "-1.9", -20],
2049
+ ])("ROUNDUP(%s, %s) - %s: take 2 parameter(s), return a number", (a, b, expected) => {
2050
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: a, A3: b })).toBe(expected);
2051
+ });
2052
+
2053
+ test("ROUNDUP: special value testing", () => {
2054
+ expect(evaluateCell("A1", { A1: "=ROUNDUP()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
2055
+ expect(evaluateCell("A1", { A1: "=ROUNDUP( , )" })).toBe(0);
2056
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(42, -1)" })).toBe(50);
2057
+ expect(evaluateCell("A1", { A1: "=ROUNDUP( , 42)" })).toBe(0);
2058
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(42, )" })).toBe(42);
2059
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(42.42, TRUE)" })).toBe(42.5);
2060
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(42.42, FALSE)" })).toBe(43);
2061
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(TRUE, 10)" })).toBe(1);
2062
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(FALSE, 10)" })).toBe(0);
2063
+
2064
+ expect(evaluateCell("A1", { A1: '=ROUNDUP("" , "")' })).toBe(0);
2065
+ expect(evaluateCell("A1", { A1: '=ROUNDUP("" , 12)' })).toBe(0);
2066
+ expect(evaluateCell("A1", { A1: '=ROUNDUP(" " , 0)' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2067
+ expect(evaluateCell("A1", { A1: '=ROUNDUP("42", -1)' })).toBe(50);
2068
+ expect(evaluateCell("A1", { A1: '=ROUNDUP("42", "-1")' })).toBe(50);
2069
+ expect(evaluateCell("A1", { A1: '=ROUNDUP("42", "")' })).toBe(42);
2070
+
2071
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: "", A3: "" })).toBe(0);
2072
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: "42", A3: "-1" })).toBe(50);
2073
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: "", A3: "42" })).toBe(0);
2074
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: "42", A3: "" })).toBe(42);
2075
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: "42.42", A3: "TRUE" })).toBe(42.5);
2076
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: "42.42", A3: "FALSE" })).toBe(43);
2077
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: "TRUE", A3: "10" })).toBe(1);
2078
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: "FALSE", A3: "10" })).toBe(0);
2079
+
2080
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: '"42"', A3: '"42"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2081
+
2082
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: '=""', A3: '="2"' })).toBe(0);
2083
+ expect(evaluateCell("A1", { A1: "=ROUNDUP(A2, A3)", A2: '="42"', A3: '="-1"' })).toBe(50);
2084
+ });
2085
+
2086
+ //----------------------------------------------------------------------------
2087
+ // SEC
2088
+ //----------------------------------------------------------------------------
2089
+
2090
+ test.each([
2091
+ ["=PI()/3", 2],
2092
+ ["1", 1.850815718],
2093
+ ["-2", -2.402997962],
2094
+ ["3", -1.010108666],
2095
+ ["-4", -1.529885656],
2096
+ ["0", 1],
2097
+ ])("SEC(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2098
+ expect(evaluateCell("A1", { A1: "=SEC(A2)", A2: a })).toBeCloseTo(expected, 9);
2099
+ });
2100
+
2101
+ //----------------------------------------------------------------------------
2102
+ // SECH
2103
+ //----------------------------------------------------------------------------
2104
+
2105
+ test.each([
2106
+ ["1", 0.648054274],
2107
+ ["-1", 0.648054274],
2108
+ ["0", 1],
2109
+ ["=LN(2)", 0.8],
2110
+ ["=-LN(3)", 0.6],
2111
+ ])("SECH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2112
+ expect(evaluateCell("A1", { A1: "=SECH(A2)", A2: a })).toBeCloseTo(expected, 9);
2113
+ });
2114
+
2115
+ //----------------------------------------------------------------------------
2116
+ // SIN
2117
+ //----------------------------------------------------------------------------
2118
+
2119
+ test.each([
2120
+ ["0", 0],
2121
+ ["=PI()", 0],
2122
+ ["=PI()*2", 0],
2123
+ ["=PI()/2", 1],
2124
+ ["=PI()/6", 0.5],
2125
+ ["=-PI()/2", -1],
2126
+ ])("SIN(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2127
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: a })).toBeCloseTo(expected, 9);
2128
+ });
2129
+
2130
+ test("SIN: special value testing", () => {
2131
+ expect(evaluateCell("A1", { A1: "=SIN()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
2132
+ expect(evaluateCell("A1", { A1: "=SIN(TRUE)" })).toBeCloseTo(0.841470984807897, 9);
2133
+ expect(evaluateCell("A1", { A1: "=SIN(FALSE)" })).toBeCloseTo(0, 9);
2134
+
2135
+ expect(evaluateCell("A1", { A1: '=SIN("")' })).toBeCloseTo(0, 9);
2136
+ expect(evaluateCell("A1", { A1: '=SIN(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2137
+ expect(evaluateCell("A1", { A1: '=SIN("0")' })).toBeCloseTo(0, 9);
2138
+
2139
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: "" })).toBeCloseTo(0, 9);
2140
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2141
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: "0" })).toBeCloseTo(0, 9);
2142
+
2143
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2144
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2145
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: '"0"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2146
+
2147
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: '=""' })).toBeCloseTo(0, 9);
2148
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2149
+ expect(evaluateCell("A1", { A1: "=SIN(A2)", A2: '="0"' })).toBeCloseTo(0, 9);
2150
+ });
2151
+
2152
+ //----------------------------------------------------------------------------
2153
+ // SINH
2154
+ //----------------------------------------------------------------------------
2155
+
2156
+ test.each([
2157
+ ["1", 1.175201194],
2158
+ ["-2", -3.626860408],
2159
+ ["0", 0],
2160
+ ["=LN(2)", 0.75],
2161
+ ["=-LN(4)", -1.875],
2162
+ ])("SINH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2163
+ expect(evaluateCell("A1", { A1: "=SINH(A2)", A2: a })).toBeCloseTo(expected, 9);
2164
+ });
2165
+
2166
+ //----------------------------------------------------------------------------
2167
+ // SQRT
2168
+ //----------------------------------------------------------------------------
2169
+
2170
+ test.each([
2171
+ ["0", 0],
2172
+ ["4", 2],
2173
+ ["9", 3],
2174
+ ])("SQRT(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2175
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: a })).toBe(expected);
2176
+ });
2177
+
2178
+ test.each([["-4"], ["-9"]])("SQRT(%s) - error: take 1 parameter(s), return an error ", (a) => {
2179
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: a })).toBe("#ERROR"); // @compatibility: on google sheets, return #NUM!!
2180
+ });
2181
+
2182
+ test("SQRT: special value testing", () => {
2183
+ expect(evaluateCell("A1", { A1: "=SQRT()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
2184
+ expect(evaluateCell("A1", { A1: "=SQRT(TRUE)" })).toBe(1);
2185
+ expect(evaluateCell("A1", { A1: "=SQRT(FALSE)" })).toBe(0);
2186
+
2187
+ expect(evaluateCell("A1", { A1: '=SQRT("")' })).toBe(0);
2188
+ expect(evaluateCell("A1", { A1: '=SQRT(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2189
+ expect(evaluateCell("A1", { A1: '=SQRT("49")' })).toBe(7);
2190
+
2191
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: "" })).toBe(0);
2192
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2193
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: "49" })).toBe(7);
2194
+
2195
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: '""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2196
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: '" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2197
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: '"0"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2198
+
2199
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: '=""' })).toBe(0);
2200
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2201
+ expect(evaluateCell("A1", { A1: "=SQRT(A2)", A2: '="49"' })).toBe(7);
2202
+ });
2203
+
2204
+ //----------------------------------------------------------------------------
2205
+ // SUM
2206
+ //----------------------------------------------------------------------------
2207
+
2208
+ test("SUM: functional tests on simple arguments", () => {
2209
+ expect(evaluateCell("A1", { A1: "=SUM()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
2210
+ expect(evaluateCell("A1", { A1: "=SUM(,)" })).toBe(0);
2211
+ expect(evaluateCell("A1", { A1: "=SUM(0)" })).toBe(0);
2212
+ expect(evaluateCell("A1", { A1: "=SUM(1, 2, 3, 1, 2)" })).toBe(9);
2213
+ expect(evaluateCell("A1", { A1: "=SUM(1, , 2, , 3)" })).toBe(6);
2214
+ expect(evaluateCell("A1", { A1: "=SUM(1.5, 1.4)" })).toBe(2.9);
2215
+ expect(evaluateCell("A1", { A1: '=SUM("Jean Saigne", "Jean Tanrien")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2216
+ expect(evaluateCell("A1", { A1: '=SUM("")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2217
+ expect(evaluateCell("A1", { A1: '=SUM(" ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2218
+ expect(evaluateCell("A1", { A1: '=SUM("2", "-2")' })).toBe(0);
2219
+ expect(evaluateCell("A1", { A1: '=SUM("2", "")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2220
+ expect(evaluateCell("A1", { A1: '=SUM("2", " ")' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2221
+ expect(evaluateCell("A1", { A1: "=SUM(TRUE, FALSE)" })).toBe(1);
2222
+ expect(evaluateCell("A1", { A1: '=SUM(1, "1", TRUE)' })).toBe(3);
2223
+ expect(evaluateCell("A1", { A1: '=SUM(1, "10/10/10")' })).toBe(40462);
2224
+ });
2225
+
2226
+ test("SUM: functional tests on cell arguments", () => {
2227
+ expect(evaluateCell("A1", { A1: "=SUM(A2)", A2: "" })).toBe(0);
2228
+ expect(evaluateCell("A1", { A1: "=SUM(A2)", A2: " " })).toBe(0);
2229
+ expect(evaluateCell("A1", { A1: "=SUM(A2)", A2: "," })).toBe(0);
2230
+ expect(evaluateCell("A1", { A1: "=SUM(A2)", A2: "0" })).toBe(0);
2231
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "1", A3: "2" })).toBe(3);
2232
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3, A4)", A2: "1", A3: "", A4: "1" })).toBe(2);
2233
+ expect(
2234
+ evaluateCell("A1", { A1: "=SUM(A2, A3, A4)", A2: "1.5", A3: "-10", A4: "Jean Vier" })
2235
+ ).toBe(-8.5);
2236
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "", A3: "" })).toBe(0);
2237
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: " ", A3: "" })).toBe(0);
2238
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "", A3: '=" "' })).toBe(0);
2239
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: " ", A3: '=" "' })).toBe(0);
2240
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: " ", A3: '=" "' })).toBe(0);
2241
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: " ", A3: '=" "' })).toBe(0);
2242
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "42", A3: "42" })).toBe(84);
2243
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "42", A3: '"42"' })).toBe(42);
2244
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "42", A3: "=42" })).toBe(84);
2245
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "42", A3: '="42"' })).toBe(42);
2246
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: '"42"', A3: '"42"' })).toBe(0);
2247
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: '"42"', A3: "=42" })).toBe(42);
2248
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: '"42"', A3: '="42"' })).toBe(0);
2249
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "=42", A3: "=42" })).toBe(84);
2250
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: "=42", A3: '="42"' })).toBe(42);
2251
+ expect(evaluateCell("A1", { A1: "=SUM(A2, A3)", A2: '="42"', A3: '="42"' })).toBe(0);
2252
+ });
2253
+
2254
+ test("SUM: functional tests on simple and cell arguments", () => {
2255
+ expect(evaluateCell("A1", { A1: "=SUM(A2,)", A2: "" })).toBe(0);
2256
+ expect(evaluateCell("A1", { A1: "=SUM(A2,)", A2: " " })).toBe(0);
2257
+ expect(evaluateCell("A1", { A1: "=SUM(A2,)", A2: '=""' })).toBe(0);
2258
+ expect(evaluateCell("A1", { A1: "=SUM(A2,)", A2: '=" "' })).toBe(0);
2259
+ expect(evaluateCell("A1", { A1: '=SUM(A2, "")', A2: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2260
+ expect(evaluateCell("A1", { A1: '=SUM(A2, "")', A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2261
+ expect(evaluateCell("A1", { A1: '=SUM(A2, "")', A2: '=""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2262
+ expect(evaluateCell("A1", { A1: '=SUM(A2, "")', A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2263
+ expect(evaluateCell("A1", { A1: '=SUM(A2, " ")', A2: "" })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2264
+ expect(evaluateCell("A1", { A1: '=SUM(A2, " ")', A2: " " })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2265
+ expect(evaluateCell("A1", { A1: '=SUM(A2, " ")', A2: '=""' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2266
+ expect(evaluateCell("A1", { A1: '=SUM(A2, " ")', A2: '=" "' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2267
+ expect(evaluateCell("A1", { A1: '=SUM(42, "42")' })).toBe(84);
2268
+ expect(evaluateCell("A1", { A1: "=SUM(A2, 42)", A2: "10/10/10" })).toBe(40503);
2269
+ expect(evaluateCell("A1", { A1: "=SUM(A2, 42)", A2: "42" })).toBe(84);
2270
+ expect(evaluateCell("A1", { A1: "=SUM(A2, 42)", A2: '"42"' })).toBe(42);
2271
+ expect(evaluateCell("A1", { A1: "=SUM(A2, 42)", A2: "=42" })).toBe(84);
2272
+ expect(evaluateCell("A1", { A1: "=SUM(A2, 42)", A2: '="42"' })).toBe(42);
2273
+ expect(evaluateCell("A1", { A1: '=SUM(A2, "42")', A2: "42" })).toBe(84);
2274
+ expect(evaluateCell("A1", { A1: '=SUM(A2, "42")', A2: '"42"' })).toBe(42);
2275
+ expect(evaluateCell("A1", { A1: '=SUM(A2, "42")', A2: "=42" })).toBe(84);
2276
+ expect(evaluateCell("A1", { A1: '=SUM(A2, "42")', A2: '="42"' })).toBe(42);
2277
+ expect(evaluateCell("A1", { A1: "=SUM(A2, TRUE)", A2: "1" })).toBe(2);
2278
+ expect(evaluateCell("A1", { A1: "=SUM(A2, TRUE)", A2: '"1"' })).toBe(1);
2279
+ expect(evaluateCell("A1", { A1: "=SUM(A2, TRUE)", A2: "=1" })).toBe(2);
2280
+ expect(evaluateCell("A1", { A1: "=SUM(A2, TRUE)", A2: '="1"' })).toBe(1);
2281
+ });
2282
+
2283
+ test("SUM: functional tests on range arguments", () => {
2284
+ const grid = {
2285
+ A1: "=SUM(B2:D3,B4:D4,E2:E3,E4)",
2286
+
2287
+ A2: "=SUM(B2:E2)",
2288
+ A3: "=SUM(B3:E3)",
2289
+ A4: "=SUM(B4:E4)",
2290
+ A5: "=SUM(B4:E2000)",
2291
+
2292
+ B1: "=SUM(B2:B4)",
2293
+ C1: "=SUM(C2:C4)",
2294
+ D1: "=SUM(D2:D4)",
2295
+ E1: "=SUM(E2:E4)",
2296
+
2297
+ B2: "=3",
2298
+ C2: "3",
2299
+ D2: '"3"',
2300
+ E2: '="3"',
2301
+
2302
+ B3: '=" "',
2303
+ C3: "0",
2304
+ D3: "Jean Bave",
2305
+ E3: '"Jean Darme"',
2306
+
2307
+ B4: " ",
2308
+ C4: '""',
2309
+ D4: '=""',
2310
+ E4: '" "',
2311
+ };
2312
+
2313
+ const gridResult = evaluateGrid(grid);
2314
+ expect(gridResult.A1).toBe(6);
2315
+ expect(gridResult.A2).toBe(6);
2316
+ expect(gridResult.A3).toBe(0);
2317
+ expect(gridResult.A4).toBe(0);
2318
+ expect(gridResult.A5).toBe(0);
2319
+ expect(gridResult.B1).toBe(3);
2320
+ expect(gridResult.C1).toBe(3);
2321
+ expect(gridResult.D1).toBe(0);
2322
+ expect(gridResult.E1).toBe(0);
2323
+ });
2324
+
2325
+ //----------------------------------------------------------------------------
2326
+ // SUMIF
2327
+ //----------------------------------------------------------------------------
2328
+
2329
+ test("COUNTUNIQUEIFS: functional tests on range", () => {
2330
+ // prettier-ignore
2331
+ const grid = {
2332
+ A1: "Coffee" , B1: "4" ,
2333
+ A2: "Newspaper" , B2: "1" ,
2334
+ A3: "Taxi" , B3: "10" ,
2335
+ A4: "Golf" , B4: "26" ,
2336
+ A5: "Taxi" , B5: "8" ,
2337
+ A6: "Coffee" , B6: "3.5",
2338
+ A7: "Gas" , B7: "46" ,
2339
+ A8: "Restaurant", B8: "31" ,
2340
+
2341
+
2342
+ A12: '=SUMIF(A1:A8, "Taxi", B1:B8)',
2343
+ A13: '=SUMIF(B1:B8, ">=10", B1:B8)',
2344
+ A14: '=SUMIF(B1:B8, ">=10")',
2345
+ A15: '=SUMIF(A1:A8, "G*", B1:B8)',
2346
+ };
2347
+
2348
+ const gridResult = evaluateGrid(grid);
2349
+ expect(gridResult.A12).toBe(18);
2350
+ expect(gridResult.A13).toBe(113);
2351
+ expect(gridResult.A14).toBe(113);
2352
+ expect(gridResult.A15).toBe(72);
2353
+ });
2354
+
2355
+ //----------------------------------------------------------------------------
2356
+ // SUMIFS
2357
+ //----------------------------------------------------------------------------
2358
+
2359
+ test("COUNTUNIQUEIFS: functional tests on range", () => {
2360
+ // prettier-ignore
2361
+ const grid = {
2362
+ B1: "4" , C1: "14", D1: "Yes",
2363
+ B2: "28", C2: "30", D2: "Yes",
2364
+ B3: "31", C3: "47", D3: "Yes",
2365
+ B4: "12", C4: "0" , D4: "Yes",
2366
+ B5: "31", C5: "47", D5: "Yes",
2367
+ B6: "13", C6: "5" , D6: "No" ,
2368
+ B7: "18", C7: "43", D7: "No" ,
2369
+ B8: "24", C8: "7" , D8: "Yes",
2370
+ B9: "44", C9: "28", D9: "No" ,
2371
+ B10: "22", C10: "23", D10: "No" ,
2372
+ B11: "9" , C11: "13", D11: "No" ,
2373
+
2374
+ A12: '=SUMIFS(B1:B11, B1:B11, ">20")',
2375
+ A13: '=SUMIFS(B1:B11, B1:B11, ">20", C1:C11, "<30")',
2376
+ A14: '=SUMIFS(B1:B11, D1:D11, "No")',
2377
+ };
2378
+
2379
+ const gridResult = evaluateGrid(grid);
2380
+ expect(gridResult.A12).toBe(180);
2381
+ expect(gridResult.A13).toBe(90);
2382
+ expect(gridResult.A14).toBe(106);
2383
+ });
2384
+
2385
+ //----------------------------------------------------------------------------
2386
+ // TAN
2387
+ //----------------------------------------------------------------------------
2388
+
2389
+ test.each([
2390
+ ["0", 0],
2391
+ ["=PI()/4", 1],
2392
+ ["1", 1.557407725],
2393
+ ["2", -2.185039863],
2394
+ ["-3", 0.142546543],
2395
+ ["-4", -1.157821282],
2396
+ ])("TAN(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2397
+ expect(evaluateCell("A1", { A1: "=TAN(A2)", A2: a })).toBeCloseTo(expected, 9);
2398
+ });
2399
+
2400
+ //----------------------------------------------------------------------------
2401
+ // TANH
2402
+ //----------------------------------------------------------------------------
2403
+
2404
+ test.each([
2405
+ ["=LN(3)", 0.8],
2406
+ ["=LN(2)", 0.6],
2407
+ ["0", 0],
2408
+ ["1", 0.761594156],
2409
+ ["-2", -0.96402758],
2410
+ ])("TANH(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2411
+ expect(evaluateCell("A1", { A1: "=TANH(A2)", A2: a })).toBeCloseTo(expected, 9);
2412
+ });
2413
+
2414
+ //----------------------------------------------------------------------------
2415
+ // TRUNC
2416
+ //----------------------------------------------------------------------------
2417
+
2418
+ test.each([
2419
+ ["-1.6", -1],
2420
+ ["-1.5", -1],
2421
+ ["-1.4", -1],
2422
+ ["0", 0],
2423
+ ["1.4", 1],
2424
+ ["1.5", 1],
2425
+ ["1.6", 1],
2426
+ ])("TRUNC(%s) - %s: take 1 parameter(s), return a number", (a, expected) => {
2427
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: a })).toBe(expected);
2428
+ });
2429
+
2430
+ test.each([
2431
+ ["0.3", "2", 0.3],
2432
+ ["0.34", "2", 0.34],
2433
+ ["0.345", "2", 0.34],
2434
+ ["-0.3", "2", -0.3],
2435
+ ["-0.34", "2", -0.34],
2436
+ ["-0.345", "2", -0.34],
2437
+ ["0.345", "1.9", 0.3],
2438
+ ["-0.345", "1.9", -0.3],
2439
+ ["12345", "-1", 12340],
2440
+ ["123456", "-1", 123450],
2441
+ ["12345", "-2", 12300],
2442
+ ["-123456", "-1", -123450],
2443
+ ["-12345", "-2", -12300],
2444
+ ["12345", "-1.9", 12340],
2445
+ ["-12345", "-1.9", -12340],
2446
+ ])("TRUNC(%s, %s) - %s: take 2 parameter(s), return a number", (a, b, expected) => {
2447
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: a, A3: b })).toBe(expected);
2448
+ });
2449
+
2450
+ test("TRUNC: special value testing", () => {
2451
+ expect(evaluateCell("A1", { A1: "=TRUNC()" })).toBe("#BAD_EXPR"); // @compatibility: on google sheets, return #N/A
2452
+ expect(evaluateCell("A1", { A1: "=TRUNC( , )" })).toBe(0);
2453
+ expect(evaluateCell("A1", { A1: "=TRUNC(42, -1)" })).toBe(40);
2454
+ expect(evaluateCell("A1", { A1: "=TRUNC( , 42)" })).toBe(0);
2455
+ expect(evaluateCell("A1", { A1: "=TRUNC(42, )" })).toBe(42);
2456
+ expect(evaluateCell("A1", { A1: "=TRUNC(42.42, TRUE)" })).toBe(42.4);
2457
+ expect(evaluateCell("A1", { A1: "=TRUNC(42.42, FALSE)" })).toBe(42);
2458
+ expect(evaluateCell("A1", { A1: "=TRUNC(TRUE, 10)" })).toBe(1);
2459
+ expect(evaluateCell("A1", { A1: "=TRUNC(FALSE, 10)" })).toBe(0);
2460
+
2461
+ expect(evaluateCell("A1", { A1: '=TRUNC("" , "")' })).toBe(0);
2462
+ expect(evaluateCell("A1", { A1: '=TRUNC("" , 12)' })).toBe(0);
2463
+ expect(evaluateCell("A1", { A1: '=TRUNC(" " , 0)' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2464
+ expect(evaluateCell("A1", { A1: '=TRUNC("42", -1)' })).toBe(40);
2465
+ expect(evaluateCell("A1", { A1: '=TRUNC("42", "-1")' })).toBe(40);
2466
+ expect(evaluateCell("A1", { A1: '=TRUNC("42", "")' })).toBe(42);
2467
+
2468
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: "", A3: "" })).toBe(0);
2469
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: "42", A3: "-1" })).toBe(40);
2470
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: "", A3: "42" })).toBe(0);
2471
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: "42", A3: "" })).toBe(42);
2472
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: "42.42", A3: "TRUE" })).toBe(42.4);
2473
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: "42.42", A3: "FALSE" })).toBe(42);
2474
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: "TRUE", A3: "10" })).toBe(1);
2475
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: "FALSE", A3: "10" })).toBe(0);
2476
+
2477
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: '"42"', A3: '"42"' })).toBe("#ERROR"); // @compatibility: on google sheets, return #VALUE!
2478
+
2479
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: '=""', A3: '="2"' })).toBe(0);
2480
+ expect(evaluateCell("A1", { A1: "=TRUNC(A2, A3)", A2: '="42"', A3: '="-1"' })).toBe(40);
2481
+ });
2482
+ });