evaluation_test.cljs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. (ns microtables-frontend.evaluation-test
  2. (:require
  3. [cljs.test :refer-macros [deftest testing is]]
  4. [microtables-frontend.evaluation :as evaluation]
  5. [microtables-frontend.utils.data :as data-utils]))
  6. (defn prepare-and-evaluate
  7. "Wire up all references and evaluate all formula cells in a raw table-data map."
  8. [raw-data]
  9. (-> raw-data
  10. (data-utils/walk-modify-data
  11. (fn [_c _r datum]
  12. (if (data-utils/formula? (:value datum))
  13. (assoc datum :dirty true)
  14. datum)))
  15. data-utils/create-all-references
  16. evaluation/create-all-back-references
  17. evaluation/evaluate-all))
  18. ;; --- Literal values ---
  19. (deftest literal-value-is-preserved
  20. (let [data (prepare-and-evaluate {"A" {1 {:value "42"}}})]
  21. (is (= "42" (get-in data ["A" 1 :value])))))
  22. ;; --- Basic arithmetic ---
  23. (deftest pure-arithmetic-formula
  24. (let [data (prepare-and-evaluate {"A" {1 {:value "=1+2"}}})]
  25. (is (= 3 (get-in data ["A" 1 :display])))))
  26. (deftest arithmetic-multiplication
  27. (let [data (prepare-and-evaluate {"A" {1 {:value "=3*4"}}})]
  28. (is (= 12 (get-in data ["A" 1 :display])))))
  29. ;; --- Cell references ---
  30. (deftest single-cell-reference
  31. (let [data (prepare-and-evaluate {"A" {1 {:value "5"}}
  32. "B" {1 {:value "=A1+3"}}})]
  33. (is (= 8 (get-in data ["B" 1 :display])))))
  34. (deftest chained-references
  35. (let [data (prepare-and-evaluate {"A" {1 {:value "2"}}
  36. "B" {1 {:value "=A1*3"}}
  37. "C" {1 {:value "=B1+1"}}})]
  38. (is (= 7 (get-in data ["C" 1 :display])))))
  39. (deftest multi-cell-reference-in-formula
  40. (let [data (prepare-and-evaluate {"A" {1 {:value "10"}}
  41. "B" {1 {:value "20"}}
  42. "C" {1 {:value "=A1+B1"}}})]
  43. (is (= 30 (get-in data ["C" 1 :display])))))
  44. ;; --- Range functions ---
  45. (deftest sum-contiguous-range
  46. (let [data (prepare-and-evaluate {"A" {1 {:value "1"}
  47. 2 {:value "2"}
  48. 3 {:value "3"}}
  49. "B" {1 {:value "=sum(A1:A3)"}}})]
  50. (is (= 6 (get-in data ["B" 1 :display])))))
  51. (deftest average-range
  52. (let [data (prepare-and-evaluate {"A" {1 {:value "2"}
  53. 2 {:value "4"}
  54. 3 {:value "6"}}
  55. "B" {1 {:value "=average(A1:A3)"}}})]
  56. (is (= 4 (get-in data ["B" 1 :display])))))
  57. (deftest sum-with-empty-cell-in-range
  58. ; A2 is absent — treated as 0 and excluded from the range by preprocess-expression,
  59. ; so sum(A1,A3) = 5 + 10 = 15
  60. (let [data (prepare-and-evaluate {"A" {1 {:value "5"}
  61. 3 {:value "10"}}
  62. "B" {1 {:value "=sum(A1:A3)"}}})]
  63. (is (= 15 (get-in data ["B" 1 :display])))))
  64. ;; --- Cycle detection ---
  65. (deftest mutual-cycle-marked-as-error
  66. (let [data (prepare-and-evaluate {"A" {1 {:value "=B1"}}
  67. "B" {1 {:value "=A1"}}})]
  68. (is (= :cycle-error (get-in data ["A" 1 :display])))
  69. (is (= :cycle-error (get-in data ["B" 1 :display])))))
  70. (deftest self-reference-marked-as-cycle
  71. (let [data (prepare-and-evaluate {"A" {1 {:value "=A1"}}})]
  72. (is (= :cycle-error (get-in data ["A" 1 :display])))))
  73. (deftest three-cell-cycle
  74. (let [data (prepare-and-evaluate {"A" {1 {:value "=C1"}}
  75. "B" {1 {:value "=A1"}}
  76. "C" {1 {:value "=B1"}}})]
  77. (is (= :cycle-error (get-in data ["A" 1 :display])))))
  78. ;; --- Error propagation ---
  79. (deftest calc-error-on-invalid-formula
  80. (let [data (prepare-and-evaluate {"A" {1 {:value "=notafunction()"}}})]
  81. (is (= :calc-error (get-in data ["A" 1 :display])))))
  82. ;; --- Non-formula cell has no :display ---
  83. (deftest non-formula-has-no-display
  84. (let [data (prepare-and-evaluate {"A" {1 {:value "hello"}}})]
  85. (is (nil? (get-in data ["A" 1 :display])))))