undo_redo_test.cljs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. (ns frontend.worker.undo-redo-test
  2. (:require [clojure.test :as t :refer [deftest is testing use-fixtures]]
  3. [clojure.test.check.generators :as gen]
  4. [datascript.core :as d]
  5. [frontend.db :as db]
  6. [frontend.test.generators :as t.gen]
  7. [frontend.test.helper :as test-helper]
  8. [frontend.worker.undo-redo :as undo-redo]))
  9. (def ^:private page-uuid (random-uuid))
  10. (def ^:private init-data (test-helper/initial-test-page-and-blocks {:page-uuid page-uuid}))
  11. (defn- start-and-destroy-db
  12. [f]
  13. (test-helper/db-based-start-and-destroy-db
  14. f
  15. {:init-data (fn [conn] (d/transact! conn init-data))}))
  16. (use-fixtures :each start-and-destroy-db)
  17. (def ^:private gen-non-exist-block-uuid gen/uuid)
  18. (defn- gen-block-uuid
  19. [db & {:keys [non-exist-frequency] :or {non-exist-frequency 1}}]
  20. (gen/frequency [[9 (t.gen/gen-available-block-uuid db {:page-uuid page-uuid})]
  21. [non-exist-frequency gen-non-exist-block-uuid]]))
  22. (defn- gen-parent-left-pair
  23. [db]
  24. (gen/frequency [[9 (t.gen/gen-available-parent-left-pair db {:page-uuid page-uuid})]
  25. [1 (gen/vector gen-non-exist-block-uuid 2)]]))
  26. (defn- gen-move-block-op
  27. [db]
  28. (gen/let [block-uuid (gen-block-uuid db)
  29. [parent left] (gen-parent-left-pair db)]
  30. [:frontend.worker.undo-redo/move-block
  31. {:block-uuid block-uuid
  32. :block-origin-left left
  33. :block-origin-parent parent}]))
  34. (defn- gen-insert-block-op
  35. [db]
  36. (gen/let [block-uuid (gen-block-uuid db)]
  37. [:frontend.worker.undo-redo/insert-blocks
  38. {:block-uuids [block-uuid]}]))
  39. (defn- gen-remove-block-op
  40. [db]
  41. (gen/let [block-uuid (gen-block-uuid db {:non-exist-frequency 90})
  42. [parent left] (gen-parent-left-pair db)
  43. content gen/string-alphanumeric]
  44. [:frontend.worker.undo-redo/remove-block
  45. {:block-uuid block-uuid
  46. :block-entity-map
  47. {:block/uuid block-uuid
  48. :block/left left
  49. :block/parent parent
  50. :block/content content}}]))
  51. (defn- gen-update-block-op
  52. [db]
  53. (let [gen-content-attr (gen/let [content gen/string-alphanumeric]
  54. [:block-origin-content content])
  55. gen-collapsed-attr (gen/let [v gen/boolean]
  56. [:block-origin-collapsed v])
  57. gen-tags-attr (gen/let [tags (gen/vector (gen-block-uuid db))]
  58. [:block-origin-tags tags])]
  59. (gen/let [block-uuid (gen-block-uuid db)
  60. attrs (gen/vector (gen/one-of [gen-content-attr gen-collapsed-attr gen-tags-attr]) 3)]
  61. [:frontend.worker.undo-redo/update-block
  62. (into {:block-uuid block-uuid} attrs)])))
  63. (def ^:private gen-boundary (gen/return [:frontend.worker.undo-redo/boundary]))
  64. (defn- gen-op
  65. [db & {:keys [insert-block-op move-block-op remove-block-op update-block-op boundary-op]
  66. :or {insert-block-op 2
  67. move-block-op 2
  68. remove-block-op 4
  69. update-block-op 2
  70. boundary-op 2}}]
  71. (gen/frequency [[insert-block-op (gen-insert-block-op db)]
  72. [move-block-op (gen-move-block-op db)]
  73. [remove-block-op (gen-remove-block-op db)]
  74. [update-block-op (gen-update-block-op db)]
  75. [boundary-op gen-boundary]]))
  76. (defn- get-db-block-set
  77. [db]
  78. (set
  79. (apply concat
  80. (d/q '[:find ?uuid
  81. :where
  82. [?b :block/uuid ?uuid]
  83. [?b :block/parent ?parent]
  84. [?b :block/left ?left]
  85. [?parent :block/uuid ?parent-uuid]
  86. [?left :block/uuid ?left-uuid]]
  87. db))))
  88. (defn- check-block-count
  89. [{:keys [op tx]} current-db]
  90. (case (first op)
  91. :frontend.worker.undo-redo/move-block
  92. (assert (= (:block-origin-left (second op))
  93. (:block/uuid (:block/left (d/entity current-db [:block/uuid (:block-uuid (second op))]))))
  94. {:op op :tx-data (:tx-data tx) :x (keys tx)})
  95. :frontend.worker.undo-redo/update-block
  96. (assert (some? (d/entity current-db [:block/uuid (:block-uuid (second op))]))
  97. {:op op :tx-data (:tx-data tx)})
  98. :frontend.worker.undo-redo/insert-blocks
  99. (let [entities (map #(d/entity current-db [:block/uuid %]) (:block-uuids (second op)))]
  100. (assert (every? nil? entities)
  101. {:op op :tx-data (:tx-data tx) :x (keys tx)}))
  102. :frontend.worker.undo-redo/remove-block
  103. (assert (some? (d/entity current-db [:block/uuid (:block-uuid (second op))]))
  104. {:op op :tx-data (:tx-data tx) :x (keys tx)})
  105. ;; else
  106. nil))
  107. (defn- undo-all-then-redo-all
  108. [conn]
  109. (binding [undo-redo/*undo-redo-info-for-test* (atom nil)]
  110. (loop [i 0]
  111. (let [r (undo-redo/undo test-helper/test-db-name-db-version page-uuid conn)
  112. current-db @conn]
  113. (check-block-count @undo-redo/*undo-redo-info-for-test* current-db)
  114. (if (not= :frontend.worker.undo-redo/empty-undo-stack r)
  115. (recur (inc i))
  116. (prn :undo-count i))))
  117. (loop []
  118. (let [r (undo-redo/redo test-helper/test-db-name-db-version page-uuid conn)
  119. current-db @conn]
  120. (check-block-count @undo-redo/*undo-redo-info-for-test* current-db)
  121. (when (not= :frontend.worker.undo-redo/empty-redo-stack r)
  122. (recur))))))
  123. (deftest undo-redo-gen-test
  124. (let [conn (db/get-db false)
  125. all-remove-ops (gen/generate (gen/vector (gen-op @conn {:remove-block-op 1000}) 100))]
  126. (#'undo-redo/push-undo-ops test-helper/test-db-name-db-version page-uuid all-remove-ops)
  127. (prn :block-count-before-init (count (get-db-block-set @conn)))
  128. (loop [i 0]
  129. (when (not= :frontend.worker.undo-redo/empty-undo-stack
  130. (undo-redo/undo test-helper/test-db-name-db-version page-uuid conn))
  131. (recur (inc i))))
  132. (prn :block-count (count (get-db-block-set @conn)))
  133. (undo-redo/clear-undo-redo-stack)
  134. (testing "move blocks"
  135. (let [origin-graph-block-set (get-db-block-set @conn)
  136. ops (gen/generate (gen/vector (gen-op @conn {:move-block-op 1000 :boundary-op 500}) 300))]
  137. (prn :ops (count ops))
  138. (#'undo-redo/push-undo-ops test-helper/test-db-name-db-version page-uuid ops)
  139. (undo-all-then-redo-all conn)
  140. (undo-all-then-redo-all conn)
  141. (undo-all-then-redo-all conn)
  142. (is (= origin-graph-block-set (get-db-block-set @conn)))))
  143. (testing "random ops"
  144. (let [origin-graph-block-set (get-db-block-set @conn)
  145. ops (gen/generate (gen/vector (gen-op @conn) 1000))]
  146. (prn :ops (count ops))
  147. (#'undo-redo/push-undo-ops test-helper/test-db-name-db-version page-uuid ops)
  148. (undo-all-then-redo-all conn)
  149. (undo-all-then-redo-all conn)
  150. (undo-all-then-redo-all conn)
  151. (is (= origin-graph-block-set (get-db-block-set @conn)))))
  152. ))
  153. ;;; TODO: generate outliner-ops then undo/redo/validate
  154. ;; (deftest undo-redo-single-step-check-gen-test
  155. ;; (let [conn (db/get-db false)
  156. ;; all-remove-ops (gen/generate (gen/vector (gen-op @conn {:remove-block-op 1000}) 20))]
  157. ;; (#'undo-redo/push-undo-ops test-helper/test-db-name-db-version page-uuid all-remove-ops)
  158. ;; (loop []
  159. ;; (when (not= :frontend.worker.undo-redo/empty-undo-stack
  160. ;; (undo-redo/undo test-helper/test-db-name-db-version page-uuid conn))
  161. ;; (recur)))
  162. ;; (prn :init-blocks (d/entity @conn ))
  163. ;; ))