| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- (ns microtables-frontend.evaluation-test
- (:require
- [cljs.test :refer-macros [deftest testing is]]
- [microtables-frontend.evaluation :as evaluation]
- [microtables-frontend.utils.data :as data-utils]))
- (defn prepare-and-evaluate
- "Wire up all references and evaluate all formula cells in a raw table-data map."
- [raw-data]
- (-> raw-data
- (data-utils/walk-modify-data
- (fn [_c _r datum]
- (if (data-utils/formula? (:value datum))
- (assoc datum :dirty true)
- datum)))
- data-utils/create-all-references
- evaluation/create-all-back-references
- evaluation/evaluate-all))
- ;; --- Literal values ---
- (deftest literal-value-is-preserved
- (let [data (prepare-and-evaluate {"A" {1 {:value "42"}}})]
- (is (= "42" (get-in data ["A" 1 :value])))))
- ;; --- Basic arithmetic ---
- (deftest pure-arithmetic-formula
- (let [data (prepare-and-evaluate {"A" {1 {:value "=1+2"}}})]
- (is (= 3 (get-in data ["A" 1 :display])))))
- (deftest arithmetic-multiplication
- (let [data (prepare-and-evaluate {"A" {1 {:value "=3*4"}}})]
- (is (= 12 (get-in data ["A" 1 :display])))))
- ;; --- Cell references ---
- (deftest single-cell-reference
- (let [data (prepare-and-evaluate {"A" {1 {:value "5"}}
- "B" {1 {:value "=A1+3"}}})]
- (is (= 8 (get-in data ["B" 1 :display])))))
- (deftest chained-references
- (let [data (prepare-and-evaluate {"A" {1 {:value "2"}}
- "B" {1 {:value "=A1*3"}}
- "C" {1 {:value "=B1+1"}}})]
- (is (= 7 (get-in data ["C" 1 :display])))))
- (deftest multi-cell-reference-in-formula
- (let [data (prepare-and-evaluate {"A" {1 {:value "10"}}
- "B" {1 {:value "20"}}
- "C" {1 {:value "=A1+B1"}}})]
- (is (= 30 (get-in data ["C" 1 :display])))))
- ;; --- Range functions ---
- (deftest sum-contiguous-range
- (let [data (prepare-and-evaluate {"A" {1 {:value "1"}
- 2 {:value "2"}
- 3 {:value "3"}}
- "B" {1 {:value "=sum(A1:A3)"}}})]
- (is (= 6 (get-in data ["B" 1 :display])))))
- (deftest average-range
- (let [data (prepare-and-evaluate {"A" {1 {:value "2"}
- 2 {:value "4"}
- 3 {:value "6"}}
- "B" {1 {:value "=average(A1:A3)"}}})]
- (is (= 4 (get-in data ["B" 1 :display])))))
- (deftest sum-with-empty-cell-in-range
- ; A2 is absent — treated as 0 and excluded from the range by preprocess-expression,
- ; so sum(A1,A3) = 5 + 10 = 15
- (let [data (prepare-and-evaluate {"A" {1 {:value "5"}
- 3 {:value "10"}}
- "B" {1 {:value "=sum(A1:A3)"}}})]
- (is (= 15 (get-in data ["B" 1 :display])))))
- ;; --- Cycle detection ---
- (deftest mutual-cycle-marked-as-error
- (let [data (prepare-and-evaluate {"A" {1 {:value "=B1"}}
- "B" {1 {:value "=A1"}}})]
- (is (= :cycle-error (get-in data ["A" 1 :display])))
- (is (= :cycle-error (get-in data ["B" 1 :display])))))
- (deftest self-reference-marked-as-cycle
- (let [data (prepare-and-evaluate {"A" {1 {:value "=A1"}}})]
- (is (= :cycle-error (get-in data ["A" 1 :display])))))
- (deftest three-cell-cycle
- (let [data (prepare-and-evaluate {"A" {1 {:value "=C1"}}
- "B" {1 {:value "=A1"}}
- "C" {1 {:value "=B1"}}})]
- (is (= :cycle-error (get-in data ["A" 1 :display])))))
- ;; --- Error propagation ---
- (deftest calc-error-on-invalid-formula
- (let [data (prepare-and-evaluate {"A" {1 {:value "=notafunction()"}}})]
- (is (= :calc-error (get-in data ["A" 1 :display])))))
- ;; --- Non-formula cell has no :display ---
- (deftest non-formula-has-no-display
- (let [data (prepare-and-evaluate {"A" {1 {:value "hello"}}})]
- (is (nil? (get-in data ["A" 1 :display])))))
|