🎉 Add frontend tests for files and events that manage shapes

This commit is contained in:
Andrés Moya 2021-01-19 15:15:17 +01:00 committed by Andrey Antukh
parent 0cfb66ae16
commit 686814f537
5 changed files with 201 additions and 54 deletions

View file

@ -146,6 +146,10 @@
shapes (remove #(= (:type %) :frame) (vals objects))] shapes (remove #(= (:type %) :frame) (vals objects))]
(some contains-shape-fn shapes))) (some contains-shape-fn shapes)))
(defn get-top-frame
[objects]
(get objects uuid/zero))
(defn get-parent (defn get-parent
"Retrieve the id of the parent for the shape-id (if exists)" "Retrieve the id of the parent for the shape-id (if exists)"
[shape-id objects] [shape-id objects]

View file

@ -12,6 +12,7 @@
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.globals :as globals]
[cuerdas.core :as str] [cuerdas.core :as str]
[goog.dom :as dom])) [goog.dom :as dom]))
@ -130,9 +131,9 @@
(defn create-element (defn create-element
([tag] ([tag]
(.createElement js/document tag)) (.createElement globals/document tag))
([ns tag] ([ns tag]
(.createElementNS js/document ns tag))) (.createElementNS globals/document ns tag)))
(defn set-html! (defn set-html!
[el html] [el html]
@ -201,11 +202,11 @@
(defn fullscreen? (defn fullscreen?
[] []
(cond (cond
(obj/in? js/document "webkitFullscreenElement") (obj/in? globals/document "webkitFullscreenElement")
(boolean (.-webkitFullscreenElement js/document)) (boolean (.-webkitFullscreenElement globals/document))
(obj/in? js/document "fullscreenElement") (obj/in? globals/document "fullscreenElement")
(boolean (.-fullscreenElement js/document)) (boolean (.-fullscreenElement globals/document))
:else :else
(ex/raise :type :not-supported (ex/raise :type :not-supported
@ -242,17 +243,17 @@
(-> event get-target (.releasePointerCapture (.-pointerId event)))) (-> event get-target (.releasePointerCapture (.-pointerId event))))
(defn get-root [] (defn get-root []
(query js/document "#app")) (query globals/document "#app"))
(defn ^boolean class? [node class-name] (defn ^boolean class? [node class-name]
(let [class-list (.-classList ^js node)] (let [class-list (.-classList ^js node)]
(.contains ^js class-list class-name))) (.contains ^js class-list class-name)))
(defn get-user-agent [] (defn get-user-agent []
(.-userAgent js/navigator)) (.-userAgent globals/navigator))
(defn active? [node] (defn active? [node]
(= (.-activeElement js/document) node)) (= (.-activeElement globals/document) node))
(defn get-data [^js node ^string attr] (defn get-data [^js node ^string attr]
(.getAttribute node (str "data-" attr))) (.getAttribute node (str "data-" attr)))

View file

@ -29,6 +29,14 @@ goog.scope(function() {
} }
})(); })();
app.util.globals.document = (function() {
if (typeof goog.global.document !== "undefined") {
return goog.global.document;
} else {
return {};
}
})();
app.util.globals.location = (function() { app.util.globals.location = (function() {
if (typeof goog.global.location !== "undefined") { if (typeof goog.global.location !== "undefined") {
return goog.global.location; return goog.global.location;
@ -36,5 +44,13 @@ goog.scope(function() {
return {}; return {};
} }
})(); })();
app.util.globals.navigator = (function() {
if (typeof goog.global.navigator !== "undefined") {
return goog.global.navigator;
} else {
return {};
}
})();
}); });

View file

@ -0,0 +1,89 @@
(ns app.test-helpers
(:require [cljs.test :as t :include-macros true]
[cljs.pprint :refer [pprint]]
[beicon.core :as rx]
[potok.core :as ptk]
[app.common.uuid :as uuid]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.pages :as cp]
[app.common.pages.helpers :as cph]
[app.main.data.workspace :as dw]))
;; ---- Helpers to manage global events
(defn do-update
"Execute an update event and returns the new state."
[event state]
(ptk/update event state))
(defn do-watch
"Execute a watch event and return an observable, that
emits once a list with all new events."
[event state]
(->> (ptk/watch event state nil)
(rx/reduce conj [])))
(defn do-watch-update
"Execute a watch event and return an observable, that
emits once the new state, after all new events applied
in sequence (considering they are all update events)."
[event state]
(->> (do-watch event state)
(rx/map (fn [new-events]
(reduce
(fn [new-state new-event]
(do-update new-event new-state))
state
new-events)))))
;; ---- Helpers to manage pages and objects
(def current-file-id (uuid/next))
(def initial-state
{:current-file-id current-file-id
:current-page-id nil
:workspace-local dw/workspace-local-default
:workspace-data {:id current-file-id
:components {}
:pages []
:pages-index {}}
:workspace-libraries {}})
(defn current-page
[state]
(let [page-id (:current-page-id state)]
(get-in state [:workspace-data :pages-index page-id])))
(defn sample-page
([state] (sample-page state {}))
([state {:keys [id name] :as props
:or {id (uuid/next)
name "page1"}}]
(-> state
(assoc :current-page-id id)
(update :workspace-data
cp/process-changes
[{:type :add-page
:id id
:name name}]))))
(defn sample-shape
([state type] (sample-shape state type {}))
([state type props]
(let [page (current-page state)
frame (cph/get-top-frame (:objects page))
shape (-> (cp/make-minimal-shape type)
(gsh/setup {:x 0 :y 0 :width 1 :height 1})
(merge props))]
(update state :workspace-data
cp/process-changes
[{:type :add-obj
:id (:id shape)
:page-id (:id page)
:frame-id (:id frame)
:obj shape}]))))

View file

@ -2,53 +2,50 @@
(:require [cljs.test :as t :include-macros true] (:require [cljs.test :as t :include-macros true]
[cljs.pprint :refer [pprint]] [cljs.pprint :refer [pprint]]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk] [app.test-helpers :as th]
[app.main.data.workspace.libraries :as dwl])) [app.common.data :as d]
[app.common.uuid :as uuid]
[app.common.pages.helpers :as cph]
[app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.libraries-helpers :as dwlh]))
;; ---- Helpers (t/deftest test-create-page
(t/testing "create page"
(let [state (-> th/initial-state
(th/sample-page))
page (th/current-page state)]
(t/is (= (:name page) "page1")))))
(defn do-update (t/deftest test-create-shape
[state event cb] (t/testing "create shape"
(let [new-state (ptk/update event state)] (let [id (uuid/next)
(cb new-state))) state (-> th/initial-state
(th/sample-page)
(defn do-watch (th/sample-shape :rect {:id id
[state event cb] :name "Rect 1"}))
(->> (ptk/watch event state nil) page (th/current-page state)
(rx/reduce conj []) shape (cph/get-shape page id)]
(rx/subs cb))) (t/is (= (:name shape) "Rect 1")))))
(defn do-watch-update
[state event & cbs]
(do-watch state event
(fn [events]
(t/is (= (count events) (count cbs)))
(reduce
(fn [new-state [event cb]]
(do-update new-state event cb))
state
(map list events cbs)))))
;; ---- Tests
(t/deftest synctest (t/deftest synctest
(t/testing "synctest" (t/testing "synctest"
(let [state {:workspace-local {:color-for-rename "something"}}] (let [state {:workspace-local {:color-for-rename "something"}}
(do-update new-state (->> state
state (th/do-update
dwl/clear-color-for-rename dwl/clear-color-for-rename))]
(fn [new-state]
(t/is (= (get-in new-state [:workspace-local :color-for-rename]) (t/is (= (get-in new-state [:workspace-local :color-for-rename])
nil))))))) nil)))))
(t/deftest asynctest (t/deftest asynctest
(t/testing "asynctest" (t/testing "asynctest"
(t/async done (t/async done
(let [state {} (let [state {}
color {:color "#ffffff"}] color {:color "#ffffff"}]
(do-watch-update (->> state
state (th/do-watch-update
(dwl/add-recent-color color) (dwl/add-recent-color color))
(rx/map
(fn [new-state] (fn [new-state]
(t/is (= (get-in new-state [:workspace-file (t/is (= (get-in new-state [:workspace-file
:data :data
@ -56,6 +53,46 @@
[color])) [color]))
(t/is (= (get-in new-state [:workspace-data (t/is (= (get-in new-state [:workspace-data
:recent-colors]) :recent-colors])
[color])) [color]))))
(done))))))) (rx/subs done))))))
(t/deftest test-add-component
(t/testing "Add a component"
(t/async done
(let [id1 (uuid/next)
state (-> th/initial-state
(th/sample-page)
(th/sample-shape :rect
{:id id1
:name "Rect 1"}))]
(->> state
(th/do-update (dw/select-shape id1))
(th/do-watch-update dwl/add-component)
(rx/map
(fn [new-state]
(let [page (th/current-page new-state)
shape (cph/get-shape page id1)
group (cph/get-shape page (:parent-id shape))
component (cph/get-component
(:component-id group)
(:current-file-id new-state)
(dwlh/get-local-file new-state)
nil)
c-shape (cph/get-shape
component
(:shape-ref shape))
c-group (cph/get-shape
component
(:shape-ref group))]
(t/is (= (:name shape) "Rect 1"))
(t/is (= (:name group) "Component-1"))
(t/is (= (:name component) "Component-1"))
(t/is (= (:name c-shape) "Rect 1"))
(t/is (= (:name c-group) "Component-1")))))
(rx/subs done))))))