mirror of
https://github.com/penpot/penpot.git
synced 2025-03-20 19:51:23 -05:00
🎉 Add frontend tests for files and events that manage shapes
This commit is contained in:
parent
0cfb66ae16
commit
686814f537
5 changed files with 201 additions and 54 deletions
|
@ -146,6 +146,10 @@
|
|||
shapes (remove #(= (:type %) :frame) (vals objects))]
|
||||
(some contains-shape-fn shapes)))
|
||||
|
||||
(defn get-top-frame
|
||||
[objects]
|
||||
(get objects uuid/zero))
|
||||
|
||||
(defn get-parent
|
||||
"Retrieve the id of the parent for the shape-id (if exists)"
|
||||
[shape-id objects]
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[app.common.exceptions :as ex]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.util.object :as obj]
|
||||
[app.util.globals :as globals]
|
||||
[cuerdas.core :as str]
|
||||
[goog.dom :as dom]))
|
||||
|
||||
|
@ -130,9 +131,9 @@
|
|||
|
||||
(defn create-element
|
||||
([tag]
|
||||
(.createElement js/document tag))
|
||||
(.createElement globals/document tag))
|
||||
([ns tag]
|
||||
(.createElementNS js/document ns tag)))
|
||||
(.createElementNS globals/document ns tag)))
|
||||
|
||||
(defn set-html!
|
||||
[el html]
|
||||
|
@ -201,11 +202,11 @@
|
|||
(defn fullscreen?
|
||||
[]
|
||||
(cond
|
||||
(obj/in? js/document "webkitFullscreenElement")
|
||||
(boolean (.-webkitFullscreenElement js/document))
|
||||
(obj/in? globals/document "webkitFullscreenElement")
|
||||
(boolean (.-webkitFullscreenElement globals/document))
|
||||
|
||||
(obj/in? js/document "fullscreenElement")
|
||||
(boolean (.-fullscreenElement js/document))
|
||||
(obj/in? globals/document "fullscreenElement")
|
||||
(boolean (.-fullscreenElement globals/document))
|
||||
|
||||
:else
|
||||
(ex/raise :type :not-supported
|
||||
|
@ -242,17 +243,17 @@
|
|||
(-> event get-target (.releasePointerCapture (.-pointerId event))))
|
||||
|
||||
(defn get-root []
|
||||
(query js/document "#app"))
|
||||
(query globals/document "#app"))
|
||||
|
||||
(defn ^boolean class? [node class-name]
|
||||
(let [class-list (.-classList ^js node)]
|
||||
(.contains ^js class-list class-name)))
|
||||
|
||||
(defn get-user-agent []
|
||||
(.-userAgent js/navigator))
|
||||
(.-userAgent globals/navigator))
|
||||
|
||||
(defn active? [node]
|
||||
(= (.-activeElement js/document) node))
|
||||
(= (.-activeElement globals/document) node))
|
||||
|
||||
(defn get-data [^js node ^string attr]
|
||||
(.getAttribute node (str "data-" attr)))
|
||||
|
|
|
@ -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() {
|
||||
if (typeof goog.global.location !== "undefined") {
|
||||
return goog.global.location;
|
||||
|
@ -36,5 +44,13 @@ goog.scope(function() {
|
|||
return {};
|
||||
}
|
||||
})();
|
||||
|
||||
app.util.globals.navigator = (function() {
|
||||
if (typeof goog.global.navigator !== "undefined") {
|
||||
return goog.global.navigator;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
|
|
89
frontend/tests/app/test_helpers.cljs
Normal file
89
frontend/tests/app/test_helpers.cljs
Normal 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}]))))
|
||||
|
|
@ -2,60 +2,97 @@
|
|||
(:require [cljs.test :as t :include-macros true]
|
||||
[cljs.pprint :refer [pprint]]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]
|
||||
[app.main.data.workspace.libraries :as dwl]))
|
||||
[app.test-helpers :as th]
|
||||
[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
|
||||
[state event cb]
|
||||
(let [new-state (ptk/update event state)]
|
||||
(cb new-state)))
|
||||
|
||||
(defn do-watch
|
||||
[state event cb]
|
||||
(->> (ptk/watch event state nil)
|
||||
(rx/reduce conj [])
|
||||
(rx/subs cb)))
|
||||
|
||||
(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 test-create-shape
|
||||
(t/testing "create shape"
|
||||
(let [id (uuid/next)
|
||||
state (-> th/initial-state
|
||||
(th/sample-page)
|
||||
(th/sample-shape :rect {:id id
|
||||
:name "Rect 1"}))
|
||||
page (th/current-page state)
|
||||
shape (cph/get-shape page id)]
|
||||
(t/is (= (:name shape) "Rect 1")))))
|
||||
|
||||
(t/deftest synctest
|
||||
(t/testing "synctest"
|
||||
(let [state {:workspace-local {:color-for-rename "something"}}]
|
||||
(do-update
|
||||
state
|
||||
dwl/clear-color-for-rename
|
||||
(fn [new-state]
|
||||
(t/is (= (get-in new-state [:workspace-local :color-for-rename])
|
||||
nil)))))))
|
||||
(let [state {:workspace-local {:color-for-rename "something"}}
|
||||
new-state (->> state
|
||||
(th/do-update
|
||||
dwl/clear-color-for-rename))]
|
||||
(t/is (= (get-in new-state [:workspace-local :color-for-rename])
|
||||
nil)))))
|
||||
|
||||
(t/deftest asynctest
|
||||
(t/testing "asynctest"
|
||||
(t/async done
|
||||
(let [state {}
|
||||
color {:color "#ffffff"}]
|
||||
(do-watch-update
|
||||
state
|
||||
(dwl/add-recent-color color)
|
||||
(fn [new-state]
|
||||
(t/is (= (get-in new-state [:workspace-file
|
||||
:data
|
||||
:recent-colors])
|
||||
[color]))
|
||||
(t/is (= (get-in new-state [:workspace-data
|
||||
:recent-colors])
|
||||
[color]))
|
||||
(done)))))))
|
||||
(->> state
|
||||
(th/do-watch-update
|
||||
(dwl/add-recent-color color))
|
||||
(rx/map
|
||||
(fn [new-state]
|
||||
(t/is (= (get-in new-state [:workspace-file
|
||||
:data
|
||||
:recent-colors])
|
||||
[color]))
|
||||
(t/is (= (get-in new-state [:workspace-data
|
||||
:recent-colors])
|
||||
[color]))))
|
||||
(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))))))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue