Преглед изворни кода

Add tests for function macro

Split out macros ns that it can be tested. Merged in handler ns as it is
only used for the function macro
Gabriel Horner пре 2 година
родитељ
комит
e2214b9963

+ 1 - 0
.clj-kondo/config.edn

@@ -39,6 +39,7 @@
              electron.utils utils
              "/electron/utils" js-utils
              frontend.commands commands
+             frontend.components.block.macros block-macros
              frontend.components.query query
              frontend.components.query.result query-result
              frontend.config config

+ 2 - 13
src/main/frontend/components/block.cljs

@@ -12,6 +12,7 @@
             [datascript.core :as d]
             [dommy.core :as dom]
             [frontend.commands :as commands]
+            [frontend.components.block.macros :as block-macros]
             [frontend.components.datetime :as datetime-comp]
             [frontend.components.lazy-editor :as lazy-editor]
             [frontend.components.macro :as macro]
@@ -37,13 +38,11 @@
             [frontend.fs :as fs]
             [frontend.handler.assets :as assets-handler]
             [frontend.handler.block :as block-handler]
-            [frontend.handler.common :as common-handler]
             [frontend.handler.dnd :as dnd]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.file-sync :as file-sync]
             [frontend.handler.notification :as notification]
             [frontend.handler.plugin :as plugin-handler]
-            [frontend.handler.query :as query-handler]
             [frontend.handler.repeated :as repeated]
             [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
@@ -1243,17 +1242,7 @@
 (defn- macro-function-cp
   [config arguments]
   (or
-   (when (:query-result config)
-     (when-let [query-result (rum/react (:query-result config))]
-       (let [fn-string (-> (util/format "(fn [result] %s)" (first arguments))
-                           (common-handler/safe-read-string "failed to parse function")
-                           (query-handler/normalize-query-function query-result)
-                           (str))
-             f (sci/eval-string fn-string)]
-         (when (fn? f)
-           (try (f query-result)
-                (catch :default e
-                  (js/console.error e)))))))
+   (some-> (:query-result config) rum/react (block-macros/function-macro arguments))
    [:span.warning
     (util/format "{{function %s}}" (first arguments))]))
 

+ 67 - 0
src/main/frontend/components/block/macros.cljs

@@ -0,0 +1,67 @@
+(ns frontend.components.block.macros
+  "Logseq macros that render and evaluate in blocks"
+  (:require [clojure.walk :as walk]
+            [frontend.extensions.sci :as sci]
+            [frontend.handler.common :as common-handler]
+            [goog.string :as gstring]
+            [goog.string.format]))
+
+(defn- normalize-query-function
+  [ast result]
+  (let [ast (walk/prewalk
+             (fn [f]
+               (if (and (list? f)
+                        (keyword? (second f))
+                        (contains? #{'sum 'average 'count 'min 'max} (first f)))
+                 (if (contains? #{'min 'max} (first f))
+                   (list
+                    'apply
+                    (first f)
+                    (list 'map (second f) 'result))
+                   (list
+                    (first f)
+                    (list 'map (second f) 'result)))
+                 f))
+             ast)]
+    (walk/postwalk
+     (fn [f]
+       (cond
+         (keyword? f)
+         ;; These keyword aliases should be the same as those used in the query-table for sorting
+         (case f
+           :block
+           :block/content
+
+           :page
+           :block/name
+
+           :created-at
+           :block/created-at
+
+           :updated-at
+           :block/updated-at
+
+           (let [vals (map #(get-in % [:block/properties f]) result)
+                 int? (some integer? vals)]
+             `(~'fn [~'b]
+                    (~'let [~'result-str (~'get-in ~'b [:block/properties ~f])
+                            ~'result-num (~'parseFloat ~'result-str)
+                            ~'result (if (~'isNaN ~'result-num) ~'result-str ~'result-num)]
+                           (~'or ~'result (~'when ~int? 0))))))
+
+         :else
+         f))
+     ast)))
+
+(defn function-macro
+  "Provides functionality for {{function}}"
+  [query-result arguments]
+  (let [fn-string (-> (gstring/format "(fn [result] %s)" (first arguments))
+                      (common-handler/safe-read-string "failed to parse function")
+                      (normalize-query-function query-result)
+                      (str))
+        f (sci/eval-string fn-string)]
+    (when (fn? f)
+      (try (f query-result)
+           (catch :default e
+             (js/console.error e))))))

+ 0 - 49
src/main/frontend/handler/query.cljs

@@ -1,49 +0,0 @@
-(ns frontend.handler.query
-  "Provides util handler fns for query"
-  (:require [clojure.walk :as walk]))
-
-(defn normalize-query-function
-  [ast result]
-  (let [ast (walk/prewalk
-             (fn [f]
-               (if (and (list? f)
-                        (keyword? (second f))
-                        (contains? #{'sum 'average 'count 'min 'max} (first f)))
-                 (if (contains? #{'min 'max} (first f))
-                   (list
-                    'apply
-                    (first f)
-                    (list 'map (second f) 'result))
-                   (list
-                    (first f)
-                    (list 'map (second f) 'result)))
-                 f))
-             ast)]
-    (walk/postwalk
-     (fn [f]
-       (cond
-         (keyword? f)
-         (case f
-           :block
-           :block/content
-
-           :page
-           :block/name
-
-           :created-at
-           :block/created-at
-
-           :updated-at
-           :block/updated-at
-
-           (let [vals (map #(get-in % [:block/properties f]) result)
-                 int? (some integer? vals)]
-             `(~'fn [~'b]
-               (~'let [~'result-str (~'get-in ~'b [:block/properties ~f])
-                       ~'result-num (~'parseFloat ~'result-str)
-                       ~'result (if (~'isNaN ~'result-num) ~'result-str ~'result-num)]
-                (~'or ~'result (~'when ~int? 0))))))
-
-         :else
-         f))
-     ast)))

+ 35 - 0
src/test/frontend/components/block/macros_test.cljs

@@ -0,0 +1,35 @@
+(ns frontend.components.block.macros-test
+  (:require [frontend.components.block.macros :as block-macros]
+            [clojure.test :refer [deftest are testing is]]))
+
+(deftest macro-function
+  (testing "Default table functions with property argument"
+    (are [user-input result]
+         (= result
+            (block-macros/function-macro
+             (mapv #(hash-map :block/properties %) [{:total 10} {:total 20} {:total 30}])
+             [user-input]))
+      "(sum :total)" 60
+      "(average :total)" 20
+      "(max :total)" 30
+      "(min :total)" 10
+      "(count :total)" 3))
+
+  (testing "Table function with clojure function argument"
+    (is (= 130
+           (block-macros/function-macro
+            (mapv #(hash-map :block/properties %)
+                  [{:total 10 :qty 3} {:total 20 :qty 5}])
+            ["(sum (map (fn [x] (* (:total x) (:qty x))) result))"]))))
+
+  (testing "Edge cases"
+    (is (= 40
+           (block-macros/function-macro
+            (mapv #(hash-map :block/properties %) [{:total 10} {} {:total 30}])
+            ["(sum :total)"]))
+        "Function still works when some results are missing property")
+    (is (= 0
+           (block-macros/function-macro
+            (mapv #(hash-map :block/properties %) [{:total 10} {} {:total 30}])
+            ["(sum :totally)"]))
+        "Function gives back 0 when given wrong property")))