mirror of
https://github.com/penpot/penpot.git
synced 2026-07-20 04:58:12 +00:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
06e6671813
@ -487,7 +487,13 @@
|
||||
:fn (mg/resource "app/migrations/sql/0149-mod-file-library-rel-synced-at.sql")}
|
||||
|
||||
{:name "0150-mod-http-session-v2"
|
||||
:fn (mg/resource "app/migrations/sql/0150-mod-http-session-v2.sql")}])
|
||||
:fn (mg/resource "app/migrations/sql/0150-mod-http-session-v2.sql")}
|
||||
|
||||
{:name "0150-mod-storage-object-table"
|
||||
:fn (mg/resource "app/migrations/sql/0150-mod-storage-object-table.sql")}
|
||||
|
||||
{:name "0151-mod-file-tagged-object-thumbnail-table"
|
||||
:fn (mg/resource "app/migrations/sql/0151-mod-file-tagged-object-thumbnail-table.sql")}])
|
||||
|
||||
(defn apply-migrations!
|
||||
[pool name migrations]
|
||||
|
||||
@ -0,0 +1 @@
|
||||
CREATE INDEX IF NOT EXISTS storage_object__metadata_upload_id__idx ON storage_object ((metadata->>'~:upload-id')) WHERE deleted_at IS NULL;
|
||||
@ -0,0 +1 @@
|
||||
CREATE INDEX IF NOT EXISTS file_tagged_object_thumbnail__object_id__idx ON file_tagged_object_thumbnail (object_id);
|
||||
@ -202,7 +202,8 @@
|
||||
:enable-token-import-from-library
|
||||
:enable-render-switch
|
||||
:enable-render-wasm-info
|
||||
:enable-available-viewer-wasm])
|
||||
:enable-available-viewer-wasm
|
||||
:enable-background-blur])
|
||||
|
||||
(defn parse
|
||||
[& flags]
|
||||
|
||||
@ -332,8 +332,9 @@
|
||||
(defn setup!
|
||||
[{:as config}]
|
||||
(run! (fn [[logger level]]
|
||||
(let [logger (if (keyword? logger) (name logger) logger)]
|
||||
(l/set-level! logger level)))
|
||||
(let [logger (if (keyword? logger) (name logger) logger)
|
||||
level (level->int level)]
|
||||
(.set ^js/Map loggers logger level)))
|
||||
config)))
|
||||
|
||||
(defmacro raw!
|
||||
|
||||
@ -32,8 +32,8 @@
|
||||
"lint:clj": "clj-kondo --parallel --lint ../common/src src/",
|
||||
"lint:js": "exit 0",
|
||||
"lint:scss": "pnpm exec stylelint '{src,resources}/**/*.scss'",
|
||||
"build:test": "clojure -M:dev:shadow-cljs compile test",
|
||||
"test": "pnpm run build:wasm && pnpm run build:test && node target/tests/test.js",
|
||||
"build:test": "pnpm run build:wasm && clojure -M:dev:shadow-cljs compile test",
|
||||
"test": "[ -f target/tests/test.js ] || pnpm run build:test; node target/tests/test.js",
|
||||
"test:quiet": "node ./scripts/test-quiet.js",
|
||||
"test:storybook": "vitest run --project=storybook",
|
||||
"watch:test": "mkdir -p target/tests && concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests --exec 'node target/tests/test.js'\"",
|
||||
|
||||
@ -245,7 +245,7 @@ test.describe("Background blur", () => {
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.mockConfigFlags(["enable-background-blur", tokenInputFlag]);
|
||||
await workspace.mockConfigFlags([tokenInputFlag]);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-background-blur.json");
|
||||
|
||||
@ -283,7 +283,7 @@ test.describe("Background blur", () => {
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.mockConfigFlags(["enable-background-blur", tokenInputFlag]);
|
||||
await workspace.mockConfigFlags([tokenInputFlag]);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-background-blur.json");
|
||||
|
||||
@ -321,7 +321,7 @@ test.describe("Background blur", () => {
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.mockConfigFlags(["enable-background-blur", tokenInputFlag]);
|
||||
await workspace.mockConfigFlags([tokenInputFlag]);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-background-blur.json");
|
||||
|
||||
@ -356,7 +356,8 @@ test.describe("Background blur", () => {
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
// No enable-background-blur flag
|
||||
// background-blur flag is active by default; disable it explicitly here
|
||||
await workspace.mockConfigFlags(["disable-background-blur"]);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-background-blur.json");
|
||||
|
||||
|
||||
@ -399,6 +399,7 @@
|
||||
(fn [_]
|
||||
;; Start the event collection pipeline
|
||||
(->> (rx/merge
|
||||
(rx/filter (ptk/type? ::force-persist) stream)
|
||||
(->> (rx/from-atom buffer)
|
||||
(rx/filter #(pos? (count %)))
|
||||
(rx/debounce 2000))
|
||||
@ -416,6 +417,7 @@
|
||||
(rx/map (constantly chunk))))))
|
||||
(rx/take-until stopper)
|
||||
(rx/subs! (fn [chunk]
|
||||
(st/emit! (ptk/data-event ::chunk-persisted {:chunk chunk}))
|
||||
(swap! buffer remove-from-buffer (count chunk)))
|
||||
(fn [cause]
|
||||
(l/error :hint "unexpected error on audit persistence" :cause cause))
|
||||
|
||||
@ -104,7 +104,9 @@
|
||||
instead of recursing until the call-stack is exhausted."
|
||||
[error]
|
||||
(if @handling-error?
|
||||
(.error js/console "[on-error] re-entrant call suppressed" error)
|
||||
(do
|
||||
(js/console.error "[on-error] re-entrant call suppressed")
|
||||
(ex/print-throwable error))
|
||||
(do
|
||||
(vreset! handling-error? true)
|
||||
(try
|
||||
|
||||
@ -21,8 +21,6 @@
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(log/set-level! :trace)
|
||||
|
||||
(def global-enabled-features
|
||||
(cfeat/get-enabled-features cf/flags))
|
||||
|
||||
|
||||
@ -281,7 +281,6 @@
|
||||
:bounds bounds
|
||||
:zoom zoom}]
|
||||
[:> size-display* {:selrect selected-selrect :zoom zoom}]
|
||||
[:> selection-size-badge* {:selrect selected-selrect :zoom zoom}]
|
||||
|
||||
(if (or (not hover-shape) (not hover-selected-shape?))
|
||||
(when (and frame (not= uuid/zero (:id frame)))
|
||||
|
||||
@ -45,6 +45,7 @@
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc shortcuts*
|
||||
@ -958,17 +959,24 @@
|
||||
ev-name (if (= next-renderer :wasm)
|
||||
"enable-webgl-rendering"
|
||||
"disable-webgl-rendering")]
|
||||
|
||||
(if (cf/external-feature-flag "renderer-hard-reload" "test")
|
||||
;; Bare RPC + hard reload: skips `du/update-profile-props`, so
|
||||
;; `features/recompute-features` is not run here; bootstrap
|
||||
;; after reload resolves render-wasm/v1 from the saved profile.
|
||||
(do
|
||||
(st/emit! (ev/event {::ev/name ev-name
|
||||
::ev/origin "workspace:menu"}))
|
||||
(->> (rp/cmd! :update-profile-props {:props {:renderer next-renderer}})
|
||||
(rx/subs! (fn [_] (dom/reload-current-window true))
|
||||
(->> (rx/zip
|
||||
(rp/cmd! :update-profile-props {:props {:renderer next-renderer}})
|
||||
(rx/filter (ptk/type? ::ev/chunk-persisted) st/stream))
|
||||
(rx/timeout 2000 (rx/of :timeout))
|
||||
(rx/subs! (fn [_]
|
||||
(dom/reload-current-window true))
|
||||
(fn [_]
|
||||
(st/emit! (ntf/error (tr "errors.generic")))))))
|
||||
(st/emit! (ntf/error (tr "errors.generic"))))))
|
||||
(st/emit! (ev/event {::ev/name ev-name
|
||||
::ev/origin "workspace:menu"})
|
||||
(ptk/data-event ::ev/force-persist {})))
|
||||
|
||||
;; `update-profile-props` WatchEvent calls
|
||||
;; `features/recompute-features`.
|
||||
(st/emit! (ev/event {::ev/name ev-name
|
||||
|
||||
@ -681,7 +681,7 @@
|
||||
;; only overlay the dotted extensions. Drag and edit hide the WASM line
|
||||
;; and require the full SVG line.
|
||||
show-line? (not= mode :hover)
|
||||
show-pill? (or (= mode :edit) (= mode :hover))
|
||||
show-pill? (or (= mode :edit) (= mode :hover) (= mode :drag))
|
||||
editing? (= mode :edit)
|
||||
|
||||
on-key-down
|
||||
|
||||
@ -187,8 +187,8 @@
|
||||
event (#'thumbnails/clear-thumbnail-batch)]
|
||||
(ptk/update event state)
|
||||
(mock/with-mocks
|
||||
{#'app.main.repo/cmd! mock/rpc-cmd!-mock
|
||||
#'app.util.timers/schedule-on-idle mock/schedule-on-idle-mock}
|
||||
{app.main.repo/cmd! mock/rpc-cmd-mock
|
||||
app.util.timers/schedule-on-idle mock/schedule-on-idle-mock}
|
||||
(fn [done']
|
||||
(->> (ptk/watch event state nil)
|
||||
(rx/reduce conj [])
|
||||
@ -211,8 +211,8 @@
|
||||
event (#'thumbnails/clear-thumbnail-batch)]
|
||||
(ptk/update event state)
|
||||
(mock/with-mocks
|
||||
{#'app.main.repo/cmd! mock/rpc-cmd!-mock
|
||||
#'app.util.timers/schedule-on-idle mock/schedule-on-idle-mock}
|
||||
{app.main.repo/cmd! mock/rpc-cmd-mock
|
||||
app.util.timers/schedule-on-idle mock/schedule-on-idle-mock}
|
||||
(fn [done']
|
||||
(->> (ptk/watch event state nil)
|
||||
(rx/reduce conj [])
|
||||
@ -242,9 +242,9 @@
|
||||
event (#'thumbnails/clear-thumbnail-batch)]
|
||||
(ptk/update event state)
|
||||
(mock/with-mocks
|
||||
{#'app.main.repo/cmd! (fn [_ _] (rx/of nil))
|
||||
#'app.util.webapi/revoke-uri mock/revoke-uri-mock
|
||||
#'app.util.timers/schedule-on-idle mock/schedule-on-idle-mock}
|
||||
{app.main.repo/cmd! (fn [_ _] (rx/of nil))
|
||||
app.util.webapi/revoke-uri mock/revoke-uri-mock
|
||||
app.util.timers/schedule-on-idle mock/schedule-on-idle-mock}
|
||||
(fn [done']
|
||||
(->> (ptk/watch event state nil)
|
||||
(rx/reduce conj [])
|
||||
@ -262,8 +262,8 @@
|
||||
state {}]
|
||||
(ptk/update event state)
|
||||
(mock/with-mocks
|
||||
{#'app.main.repo/cmd! mock/rpc-cmd!-mock
|
||||
#'app.util.timers/schedule-on-idle mock/schedule-on-idle-mock}
|
||||
{app.main.repo/cmd! mock/rpc-cmd-mock
|
||||
app.util.timers/schedule-on-idle mock/schedule-on-idle-mock}
|
||||
(fn [done']
|
||||
(->> (ptk/watch event state nil)
|
||||
(rx/reduce conj [])
|
||||
@ -284,7 +284,7 @@
|
||||
event (thumbnails/clear-thumbnail file-id object-id)]
|
||||
(ptk/update event state)
|
||||
(mock/with-mocks
|
||||
{#'beicon.v2.core/timer mock/timer-mock}
|
||||
{beicon.v2.core/timer mock/timer-mock}
|
||||
(fn [done']
|
||||
(->> (ptk/watch event state nil)
|
||||
(rx/reduce conj [])
|
||||
|
||||
136
frontend/test/frontend_tests/helpers/mock.cljc
Normal file
136
frontend/test/frontend_tests/helpers/mock.cljc
Normal file
@ -0,0 +1,136 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns frontend-tests.helpers.mock
|
||||
"Async-first mocking primitives for ClojureScript tests.
|
||||
|
||||
Uses `set!` to install and restore mocks so that they remain active
|
||||
across asynchronous boundaries (Promises, RxJS subscriptions, callbacks).
|
||||
`with-redefs` cannot be used here because it restores bindings when the
|
||||
body exits, which is too early for async code.
|
||||
|
||||
Recording atoms (`rpc-calls`, `revoked-uris`) persist across the entire
|
||||
test lifecycle, making captured data inspectable regardless of whether
|
||||
callbacks fire synchronously or asynchronously.
|
||||
|
||||
The `with-mocks` helper wraps the lifecycle:
|
||||
1. Reset recording atoms
|
||||
2. Save original var values, install mocks via `set!`
|
||||
3. Execute `(test-fn inner-done)`
|
||||
4. `inner-done` restores originals and calls `outer-done`
|
||||
(typically `cljs.test/async`'s done).
|
||||
|
||||
Usage: `(with-mocks {ns/sym mock-fn, ...} test-fn done)`"
|
||||
#?(:cljs (:require
|
||||
[beicon.v2.core :as rx]))
|
||||
#?(:cljs (:require-macros [frontend-tests.helpers.mock])))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Macro (compile-time, Clojure only)
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
#?(:clj
|
||||
(defmacro with-mocks
|
||||
"Resets recording atoms, installs `mocks` via `set!`, then
|
||||
calls `(test-fn inner-done)`. Original var values are restored
|
||||
when `inner-done` is called.
|
||||
|
||||
`mocks` is a map of sym → mock-fn
|
||||
(e.g. `{app.main.repo/cmd! mock-fn}`).
|
||||
|
||||
`inner-done` restores the originals and calls `outer-done` (the
|
||||
`cljs.test/async` `done` callback).
|
||||
|
||||
Example:
|
||||
|
||||
(t/deftest my-async-test
|
||||
(t/async done
|
||||
(mock/with-mocks
|
||||
{app.main.repo/cmd! mock/rpc-cmd-mock}
|
||||
(fn [done']
|
||||
(->> (some-async-flow)
|
||||
(rx/subs!
|
||||
(fn [v] ...)
|
||||
(fn [err] (done'))
|
||||
(fn [] (done'))))))))"
|
||||
[mocks test-fn outer-done]
|
||||
(let [entries (map identity mocks)
|
||||
gen-pairs (mapv (fn [[qsym _mock]]
|
||||
{:qsym qsym
|
||||
:osym (gensym "orig-")})
|
||||
entries)
|
||||
;; [orig-123 app.main.repo/cmd!, orig-456 app.util.timers/schedule-on-idle, ...]
|
||||
let-bindings (vec (mapcat
|
||||
(fn [{:keys [qsym osym]}]
|
||||
[osym qsym])
|
||||
gen-pairs))
|
||||
install-exprs (mapv
|
||||
(fn [[_qsym mock-fn] {:keys [qsym]}]
|
||||
`(set! ~qsym ~mock-fn))
|
||||
entries
|
||||
gen-pairs)
|
||||
restore-exprs (mapv
|
||||
(fn [{:keys [qsym osym]}]
|
||||
`(set! ~qsym ~osym))
|
||||
gen-pairs)]
|
||||
`(do
|
||||
(frontend-tests.helpers.mock/reset-state!)
|
||||
(let ~let-bindings
|
||||
~@install-exprs
|
||||
(~test-fn (fn inner-done# []
|
||||
~@restore-exprs
|
||||
(~outer-done))))))))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Runtime (ClojureScript only)
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
#?(:cljs
|
||||
(do
|
||||
;; Recording atoms
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
(def rpc-calls
|
||||
"Atom accumulating mocked `rp/cmd!` calls as `{:cmd kw :params map}`."
|
||||
(atom []))
|
||||
|
||||
(def revoked-uris
|
||||
"Atom accumulating URIs passed to `wapi/revoke-uri`."
|
||||
(atom []))
|
||||
|
||||
;; Mock implementations
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
(defn rpc-cmd-mock
|
||||
"Records [cmd params] in [[rpc-calls]], returns `(rx/of nil)`."
|
||||
[cmd params]
|
||||
(swap! rpc-calls conj {:cmd cmd :params params})
|
||||
(rx/of nil))
|
||||
|
||||
(defn revoke-uri-mock
|
||||
"Records `uri` in [[revoked-uris]]."
|
||||
[uri]
|
||||
(swap! revoked-uris conj uri))
|
||||
|
||||
(defn schedule-on-idle-mock
|
||||
"Calls `f` immediately instead of deferring to the idle queue."
|
||||
[f]
|
||||
(f))
|
||||
|
||||
(defn timer-mock
|
||||
"Returns `(rx/of :immediate)` so debounce timers fire instantly
|
||||
during tests."
|
||||
[_ms]
|
||||
(rx/of :immediate))
|
||||
|
||||
;; Lifecycle
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
(defn reset-state!
|
||||
"Clear all recording atoms. Called automatically by [[with-mocks]]."
|
||||
[]
|
||||
(reset! rpc-calls [])
|
||||
(reset! revoked-uris []))))
|
||||
@ -1,103 +0,0 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns frontend-tests.helpers.mock
|
||||
"Async-first mocking primitives for ClojureScript tests.
|
||||
|
||||
Uses `with-redefs` — the standard CLJS mechanism for rebinding Vars
|
||||
within a dynamic scope. Recording atoms (`rpc-calls`, `revoked-uris`)
|
||||
persist across the entire test lifecycle, making captured data
|
||||
inspectable regardless of whether callbacks fire synchronously or
|
||||
asynchronously.
|
||||
|
||||
The `with-mocks` helper wraps the lifecycle:
|
||||
1. Reset recording atoms
|
||||
2. Install mocks via `with-redefs`
|
||||
3. Execute `(test-fn inner-done)`
|
||||
4. `inner-done` calls `outer-done` (typically `cljs.test/async`'s done)
|
||||
|
||||
Since all mock functions return synchronous `rx/of` observables,
|
||||
callbacks always fire within the `with-redefs` body."
|
||||
(:require
|
||||
[beicon.v2.core :as rx]))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Recording atoms
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
(def rpc-calls
|
||||
"Atom accumulating mocked `rp/cmd!` calls as `{:cmd kw :params map}`."
|
||||
(atom []))
|
||||
|
||||
(def revoked-uris
|
||||
"Atom accumulating URIs passed to `wapi/revoke-uri`."
|
||||
(atom []))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Mock implementations
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
(defn rpc-cmd!-mock
|
||||
"Records [cmd params] in [[rpc-calls]], returns `(rx/of nil)`."
|
||||
[cmd params]
|
||||
(swap! rpc-calls conj {:cmd cmd :params params})
|
||||
(rx/of nil))
|
||||
|
||||
(defn revoke-uri-mock
|
||||
"Records `uri` in [[revoked-uris]]."
|
||||
[uri]
|
||||
(swap! revoked-uris conj uri))
|
||||
|
||||
(defn schedule-on-idle-mock
|
||||
"Calls `f` immediately instead of deferring to the idle queue."
|
||||
[f]
|
||||
(f))
|
||||
|
||||
(defn timer-mock
|
||||
"Returns `(rx/of :immediate)` so debounce timers fire instantly
|
||||
during tests."
|
||||
[_ms]
|
||||
(rx/of :immediate))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Lifecycle
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
(defn reset!
|
||||
"Clear all recording atoms. Called automatically by [[with-mocks]]."
|
||||
[]
|
||||
(reset! rpc-calls [])
|
||||
(reset! revoked-uris []))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Public API
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
|
||||
(defn with-mocks
|
||||
"Resets recording atoms, installs `mocks` via `with-redefs`, then
|
||||
calls `(test-fn inner-done)`.
|
||||
|
||||
`mocks` is a map of `Var → mock-fn` (e.g. `{#'rp/cmd! mock-fn}`).
|
||||
`inner-done` tears down the `with-redefs` (by returning) and calls
|
||||
`outer-done` (the `cljs.test/async` `done` callback).
|
||||
|
||||
Example:
|
||||
|
||||
(t/deftest my-async-test
|
||||
(t/async done
|
||||
(mock/with-mocks
|
||||
{#'rp/cmd! mock/rpc-cmd!-mock}
|
||||
(fn [done']
|
||||
(->> (some-async-flow)
|
||||
(rx/subs!
|
||||
(fn [v] ...)
|
||||
(fn [err] (done'))
|
||||
(fn [] (done'))))))))"
|
||||
[mocks test-fn outer-done]
|
||||
(reset!)
|
||||
(apply with-redefs (mapcat identity mocks)
|
||||
(test-fn (fn inner-done []
|
||||
(outer-done)))))
|
||||
@ -12,6 +12,7 @@
|
||||
[app.main.data.workspace.layout :as layout]
|
||||
[app.main.features :as features]
|
||||
[beicon.v2.core :as rx]
|
||||
[cljs.test :as t]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def ^private initial-state
|
||||
@ -26,7 +27,8 @@
|
||||
|
||||
(defn- on-error
|
||||
[cause]
|
||||
(js/console.log "STORE ERROR" (.-stack cause))
|
||||
(js/console.error "STORE ERROR" (.-stack cause))
|
||||
(t/do-report {:type :error :message "Store error" :actual cause})
|
||||
(when-let [data (some-> cause ex-data ::sm/explain)]
|
||||
(pprint (sm/humanize-explain data))))
|
||||
|
||||
@ -57,10 +59,13 @@
|
||||
(rx/last)
|
||||
(rx/tap (fn [_]
|
||||
(completed-cb @store)))
|
||||
(rx/subs! (fn [_] (done))
|
||||
(rx/subs! (fn [_] nil)
|
||||
(fn [cause]
|
||||
(js/console.log "[error]:" cause))
|
||||
(done)
|
||||
(js/console.error "[error]:" cause)
|
||||
(t/do-report {:type :error :message "Stream error" :actual cause}))
|
||||
(fn [_]
|
||||
(done)
|
||||
#_(js/console.debug "[complete]"))))
|
||||
|
||||
(doseq [event events]
|
||||
|
||||
@ -169,7 +169,7 @@
|
||||
(t/is (= (get c-frame1' :r4) 50)))))))]
|
||||
|
||||
(tohs/run-store-async
|
||||
store step2 events identity))))
|
||||
store (constantly nil) events step2))))
|
||||
|
||||
(t/deftest remove-token-in-main
|
||||
(t/async
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
[app.main.data.workspace.pages :as dwp]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.mock :as mock]
|
||||
[frontend-tests.helpers.pages :as thp]
|
||||
[frontend-tests.helpers.state :as ths]))
|
||||
|
||||
@ -135,274 +136,286 @@
|
||||
|
||||
(t/deftest main-and-first-level-copy-1
|
||||
(t/async done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
;; ==== Action
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
;; ==== Action
|
||||
|
||||
|
||||
;; For each main and first level copy:
|
||||
;; - Duplicate it two times with copy-paste.
|
||||
events
|
||||
(concat
|
||||
(duplicate-each-main-and-first-level-copy file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]
|
||||
;; For each main and first level copy:
|
||||
;; - Duplicate it two times with copy-paste.
|
||||
events
|
||||
(concat
|
||||
(duplicate-each-main-and-first-level-copy file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 18)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 18)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest main-and-first-level-copy-2
|
||||
(t/async
|
||||
done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
;; ==== Action
|
||||
(t/async done
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
;; ==== Action
|
||||
|
||||
|
||||
;; For each main and first level copy:
|
||||
;; - Duplicate it two times with copy-paste.
|
||||
events
|
||||
(concat
|
||||
(duplicate-each-main-and-first-level-copy file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the nearest main and check propagation to duplicated.
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"}))]
|
||||
;; For each main and first level copy:
|
||||
;; - Duplicate it two times with copy-paste.
|
||||
events
|
||||
(concat
|
||||
(duplicate-each-main-and-first-level-copy file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the nearest main and check propagation to duplicated.
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#222222") 15)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#222222") 15)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest main-and-first-level-copy-3
|
||||
(t/async
|
||||
done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
;; ==== Action
|
||||
(t/async done
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
;; ==== Action
|
||||
|
||||
|
||||
;; For each main and first level copy:
|
||||
;; - Duplicate it two times with copy-paste.
|
||||
events
|
||||
(concat
|
||||
(duplicate-each-main-and-first-level-copy file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the nearest main and check propagation to duplicated.
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
|
||||
(set-color-bottom-shape :frame-composed-2 file {:color "#333333"}))]
|
||||
;; For each main and first level copy:
|
||||
;; - Duplicate it two times with copy-paste.
|
||||
events
|
||||
(concat
|
||||
(duplicate-each-main-and-first-level-copy file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the nearest main and check propagation to duplicated.
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
|
||||
(set-color-bottom-shape :frame-composed-2 file {:color "#333333"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#333333") 12)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#333333") 12)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest main-and-first-level-copy-4
|
||||
(t/async
|
||||
done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
;; ==== Action
|
||||
(t/async done
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
;; ==== Action
|
||||
|
||||
|
||||
;; For each main and first level copy:
|
||||
;; - Duplicate it two times with copy-paste.
|
||||
events
|
||||
(concat
|
||||
(duplicate-each-main-and-first-level-copy file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the nearest main and check propagation to duplicated.
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
|
||||
(set-color-bottom-shape :frame-composed-2 file {:color "#333333"})
|
||||
(set-color-bottom-shape :frame-composed-3 file {:color "#444444"}))]
|
||||
;; For each main and first level copy:
|
||||
;; - Duplicate it two times with copy-paste.
|
||||
events
|
||||
(concat
|
||||
(duplicate-each-main-and-first-level-copy file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the nearest main and check propagation to duplicated.
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
|
||||
(set-color-bottom-shape :frame-composed-2 file {:color "#333333"})
|
||||
(set-color-bottom-shape :frame-composed-3 file {:color "#444444"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#444444") 6)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#444444") 6)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest copy-nested-in-main-1
|
||||
(t/async
|
||||
done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
(t/async done
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
|
||||
;; - Duplicate it two times, keeping the duplicated inside the same main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-simple-nested-in-main-and-group file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
|
||||
;; - Duplicate it two times, keeping the duplicated inside the same main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-simple-nested-in-main-and-group file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check propagation to all copies.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 28)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check propagation to all copies.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 28)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest copy-nested-in-main-2
|
||||
(t/async
|
||||
done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
(t/async done
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
|
||||
;; - Duplicate it two times, keeping the duplicated inside the same main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-simple-nested-in-main-and-group file)
|
||||
;; - Change color of the nearest main
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"}))]
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
|
||||
;; - Duplicate it two times, keeping the duplicated inside the same main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-simple-nested-in-main-and-group file)
|
||||
;; - Change color of the nearest main
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check propagation to duplicated.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#222222") 9)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check propagation to duplicated.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#222222") 9)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest copy-nested-in-main-3
|
||||
(t/async
|
||||
done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
(t/async done
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
|
||||
;; - Duplicate it two times, keeping the duplicated inside the same main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-simple-nested-in-main-and-group file)
|
||||
;; - Change color of the copy you duplicated from.
|
||||
(set-color-bottom-shape :group-3 file {:color "#333333"}))]
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
|
||||
;; - Duplicate it two times, keeping the duplicated inside the same main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-simple-nested-in-main-and-group file)
|
||||
;; - Change color of the copy you duplicated from.
|
||||
(set-color-bottom-shape :group-3 file {:color "#333333"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check that it's NOT PROPAGATED.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#333333") 2)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check that it's NOT PROPAGATED.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#333333") 2)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest copy-nested-1
|
||||
(t/async
|
||||
done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
(t/async done
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
|
||||
;; main and copy:
|
||||
;; - Duplicate it two times, moving the duplicates out of the main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-copy-nested-and-group-out-of-the-main file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
|
||||
;; main and copy:
|
||||
;; - Duplicate it two times, moving the duplicates out of the main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-copy-nested-and-group-out-of-the-main file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check propagation to all copies.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 20)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check propagation to all copies.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 20)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest copy-nested-2
|
||||
(t/async
|
||||
done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
|
||||
;; main and copy:
|
||||
;; - Duplicate it two times, moving the duplicates out of the main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-copy-nested-and-group-out-of-the-main file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the previous main
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
|
||||
(set-color-bottom-shape :group-3 file {:color "#333333"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check that it's NOT PROPAGATED.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 11))
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#222222") 7))
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#333333") 2)))))))))
|
||||
|
||||
(t/deftest copy-nested-3
|
||||
(t/async done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
|
||||
;; main and copy:
|
||||
;; - Duplicate it two times, moving the duplicates to another page
|
||||
events
|
||||
(concat
|
||||
(duplicate-copy-nested-and-group-out-of-the-main file :target-page-label :page-2)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the previous main
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
|
||||
(set-color-bottom-shape :group-3 file {:color "#333333"}))]
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
|
||||
;; main and copy:
|
||||
;; - Duplicate it two times, moving the duplicates out of the main.
|
||||
events
|
||||
(concat
|
||||
(duplicate-copy-nested-and-group-out-of-the-main file)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the previous main
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
|
||||
(set-color-bottom-shape :group-3 file {:color "#333333"}))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (-> (ths/get-file-from-state new-state)
|
||||
(cthf/switch-to-page :page-2))]
|
||||
(cthf/validate-file! file')
|
||||
;; Check that it's NOT PROPAGATED.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 10))
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#222222") 4))
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#333333") 0)))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)]
|
||||
(cthf/validate-file! file')
|
||||
;; Check that it's NOT PROPAGATED.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 11))
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#222222") 7))
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#333333") 2)))))))
|
||||
done)))
|
||||
|
||||
;; FIXME: this test does not calls done consistently, so it break all the test suite
|
||||
#_(t/deftest copy-nested-3
|
||||
(t/async done
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
|
||||
;; main and copy:
|
||||
;; - Duplicate it two times, moving the duplicates to another page
|
||||
events
|
||||
(concat
|
||||
(duplicate-copy-nested-and-group-out-of-the-main file :target-page-label :page-2)
|
||||
;; - Change color of Simple1
|
||||
(set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
|
||||
;; - Change color of the previous main
|
||||
(set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
|
||||
(set-color-bottom-shape :group-3 file {:color "#333333"}))]
|
||||
`(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (-> (ths/get-file-from-state new-state)
|
||||
(cthf/switch-to-page :page-2))]
|
||||
(cthf/validate-file! file')
|
||||
;; Check that it's NOT PROPAGATED.
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#111111") 10))
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#222222") 4))
|
||||
(t/is (= (count-shapes file' "rect-simple-1" "#333333") 0)))))))
|
||||
done)))
|
||||
|
||||
(t/deftest duplicate-page-integrity-frame-group-component
|
||||
;; This test covers the bug fixed in 2.9.0: duplicating a page with a mainInstance inside a group
|
||||
@ -420,73 +433,74 @@
|
||||
;; - The parent/child relationships are correct (group:shapes contains frame, frame:shapes contains shape, etc).
|
||||
;; - The duplicated page contains an instance of the component whose main is in the original page.
|
||||
(t/async done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [file (-> (cthf/sample-file :file1 :page-label :page-1)
|
||||
(ctho/add-group :group-1 {:name "group-1"})
|
||||
(ctho/add-frame :frame-1 :parent-label :group-1 {:name "frame-1"})
|
||||
(cths/add-sample-shape :shape-1 :parent-label :frame-1 {:name "shape-1"})
|
||||
(cthc/make-component :component-1 :frame-1))
|
||||
page-id (cthf/current-page-id file)
|
||||
store (ths/setup-store file)
|
||||
events [(dwp/duplicate-page page-id)]]
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)
|
||||
pages-vec (get-in file' [:data :pages])
|
||||
pages-index (get-in file' [:data :pages-index])
|
||||
new-page-id (first (remove #(= page-id %) pages-vec))
|
||||
new-page (get pages-index new-page-id)
|
||||
new-objects (:objects new-page)
|
||||
group (some #(when (= (:name %) "group-1") %) (vals new-objects))
|
||||
frame (some #(when (= (:name %) "frame-1") %) (vals new-objects))
|
||||
shape (some #(when (= (:name %) "shape-1") %) (vals new-objects))]
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [file (-> (cthf/sample-file :file1 :page-label :page-1)
|
||||
(ctho/add-group :group-1 {:name "group-1"})
|
||||
(ctho/add-frame :frame-1 :parent-label :group-1 {:name "frame-1"})
|
||||
(cths/add-sample-shape :shape-1 :parent-label :frame-1 {:name "shape-1"})
|
||||
(cthc/make-component :component-1 :frame-1))
|
||||
page-id (cthf/current-page-id file)
|
||||
store (ths/setup-store file)
|
||||
events [(dwp/duplicate-page page-id)]]
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)
|
||||
pages-vec (get-in file' [:data :pages])
|
||||
pages-index (get-in file' [:data :pages-index])
|
||||
new-page-id (first (remove #(= page-id %) pages-vec))
|
||||
new-page (get pages-index new-page-id)
|
||||
new-objects (:objects new-page)
|
||||
group (some #(when (= (:name %) "group-1") %) (vals new-objects))
|
||||
frame (some #(when (= (:name %) "frame-1") %) (vals new-objects))
|
||||
shape (some #(when (= (:name %) "shape-1") %) (vals new-objects))]
|
||||
|
||||
(t/is group "Group exists in duplicated page")
|
||||
(t/is frame "Frame exists in duplicated page")
|
||||
(t/is shape "Shape exists in duplicated page")
|
||||
(t/is (some #(= (:id frame) %) (:shapes group)) "Group's :shapes contains frame's id")
|
||||
(t/is (some #(= (:id shape) %) (:shapes frame)) "Frame's :shapes contains shape's id")
|
||||
(t/is (= (:parent-id frame) (:id group)) "Frame's parent is group")
|
||||
(t/is (= (:parent-id shape) (:id frame)) "Shape's parent is frame")
|
||||
(t/is group "Group exists in duplicated page")
|
||||
(t/is frame "Frame exists in duplicated page")
|
||||
(t/is shape "Shape exists in duplicated page")
|
||||
(t/is (some #(= (:id frame) %) (:shapes group)) "Group's :shapes contains frame's id")
|
||||
(t/is (some #(= (:id shape) %) (:shapes frame)) "Frame's :shapes contains shape's id")
|
||||
(t/is (= (:parent-id frame) (:id group)) "Frame's parent is group")
|
||||
(t/is (= (:parent-id shape) (:id frame)) "Shape's parent is frame")
|
||||
|
||||
;; Check the duplicated page must contain an instance of the component whose main is in the original page
|
||||
(let [original-page (get pages-index page-id)
|
||||
original-main (some #(when (:component-root %) %) (vals (:objects original-page)))
|
||||
instance (some #(when (:component-root %) %) (vals (:objects new-page)))
|
||||
component-id (:component-id original-main)]
|
||||
(t/is (ctk/instance-of? instance (:id file) component-id)
|
||||
(str "Duplicated page contains an instance of the original main component (component-id: " component-id ")")))
|
||||
;; Check the duplicated page must contain an instance of the component whose main is in the original page
|
||||
(let [original-page (get pages-index page-id)
|
||||
original-main (some #(when (:component-root %) %) (vals (:objects original-page)))
|
||||
instance (some #(when (:component-root %) %) (vals (:objects new-page)))
|
||||
component-id (:component-id original-main)]
|
||||
(t/is (ctk/instance-of? instance (:id file) component-id)
|
||||
(str "Duplicated page contains an instance of the original main component (component-id: " component-id ")"))))))))
|
||||
done)))
|
||||
|
||||
(done))))))))
|
||||
|
||||
(defn- setup-swapped-copies-file
|
||||
"Creates a file with a component with two levels of nested copies inside. The component
|
||||
has one copy, and inside it, the topmost nested copy is swapped with a second component,
|
||||
also with one nested copy inside.
|
||||
|
||||
{:frame-simple-1} [:name Frame1] # [Component :simple-1]
|
||||
:rect-simple-1 [:name Rect1]
|
||||
{:frame-simple-1} [:name Frame1] # [Component :simple-1]
|
||||
:rect-simple-1 [:name Rect1]
|
||||
|
||||
{:frame-composed-1} [:name frame-composed-1] # [Component :composed-1]
|
||||
:copy-simple-1-in-composed-1 [:name Frame1] @--> frame-simple-1
|
||||
<no-label #000894> [:name Rect1] ---> rect-simple-1
|
||||
{:frame-composed-1} [:name frame-composed-1] # [Component :composed-1]
|
||||
:copy-simple-1-in-composed-1 [:name Frame1] @--> frame-simple-1
|
||||
<no-label #000894> [:name Rect1] ---> rect-simple-1
|
||||
|
||||
{:frame-composed-2} [:name frame-composed-2] # [Component :composed-2]
|
||||
:copy-composed-1-in-composed-2 [:name frame-composed-1] @--> frame-composed-1
|
||||
<no-label #000899> [:name Frame1] @--> copy-simple-1-in-composed-1
|
||||
<no-label #00089a> [:name Rect1] ---> <no-label #000894>
|
||||
{:frame-composed-2} [:name frame-composed-2] # [Component :composed-2]
|
||||
:copy-composed-1-in-composed-2 [:name frame-composed-1] @--> frame-composed-1
|
||||
<no-label #000899> [:name Frame1] @--> copy-simple-1-in-composed-1
|
||||
<no-label #00089a> [:name Rect1] ---> <no-label #000894>
|
||||
|
||||
{:frame-simple-2} [:name Frame1] # [Component :simple-2]
|
||||
:rect-simple-2 [:name Rect1]
|
||||
{:frame-simple-2} [:name Frame1] # [Component :simple-2]
|
||||
:rect-simple-2 [:name Rect1]
|
||||
|
||||
{:frame-composed-3} [:name frame-composed-3] # [Component :composed-3]
|
||||
:copy-simple-2-in-composed-3 [:name Frame1] @--> frame-simple-2
|
||||
<no-label #0008a4> [:name Rect1] ---> rect-simple-2
|
||||
{:frame-composed-3} [:name frame-composed-3] # [Component :composed-3]
|
||||
:copy-simple-2-in-composed-3 [:name Frame1] @--> frame-simple-2
|
||||
<no-label #0008a4> [:name Rect1] ---> rect-simple-2
|
||||
|
||||
:copy-composed-2 [:name frame-composed-2] #--> [Component :composed-2] frame-composed-2
|
||||
:swapped-composed-3 [:name frame-composed-3, :swap-slot-label :copy-composed-1-in-composed-2] @--> frame-composed-3
|
||||
:swapped-simple-2-frame [:name Frame1] @--> copy-simple-2-in-composed-3
|
||||
:copy-composed-2 [:name frame-composed-2] #--> [Component :composed-2] frame-composed-2
|
||||
:swapped-composed-3 [:name frame-composed-3, :swap-slot-label :copy-composed-1-in-composed-2] @--> frame-composed-3
|
||||
:swapped-simple-2-frame [:name Frame1] @--> copy-simple-2-in-composed-3
|
||||
:swapped-simple-2-rect [:name Rect1] ---> <no-label #0008a4>
|
||||
"
|
||||
[]
|
||||
@ -525,42 +539,44 @@
|
||||
|
||||
(t/deftest duplicate-swapped-copies
|
||||
(t/async done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [;; ==== Setup
|
||||
file (setup-swapped-copies-file)
|
||||
store (ths/setup-store file)
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [;; ==== Setup
|
||||
file (setup-swapped-copies-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
;; Copy to the clipboard all the shapes in the swapped copy one by one,
|
||||
;; and paste them outside the copy, under uuid/zero
|
||||
events (concat (copy-paste-shape :copy-composed-2 file :target-container-id uuid/zero)
|
||||
(copy-paste-shape :swapped-composed-3 file :target-container-id uuid/zero)
|
||||
(copy-paste-shape :swapped-simple-2-frame file :target-container-id uuid/zero)
|
||||
(copy-paste-shape :swapped-simple-2-rect file :target-container-id uuid/zero))]
|
||||
;; ==== Action
|
||||
;; Copy to the clipboard all the shapes in the swapped copy one by one,
|
||||
;; and paste them outside the copy, under uuid/zero
|
||||
events (concat (copy-paste-shape :copy-composed-2 file :target-container-id uuid/zero)
|
||||
(copy-paste-shape :swapped-composed-3 file :target-container-id uuid/zero)
|
||||
(copy-paste-shape :swapped-simple-2-frame file :target-container-id uuid/zero)
|
||||
(copy-paste-shape :swapped-simple-2-rect file :target-container-id uuid/zero))]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)
|
||||
page' (cthf/current-page file')]
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)
|
||||
page' (cthf/current-page file')]
|
||||
|
||||
(cthf/validate-file! file')
|
||||
(cthf/validate-file! file')
|
||||
|
||||
;; ==== Check
|
||||
;; Shape count breakdown (including page root):
|
||||
;; page root: 1
|
||||
;; :simple-1 main: 2 shapes (frame + rect)
|
||||
;; :composed-1 main: 3 shapes (frame + instance-of-simple-1 + its child)
|
||||
;; :composed-2 main: 4 shapes (frame + instance-of-composed-1 + its descendants)
|
||||
;; :simple-2 main: 2 shapes (frame + rect)
|
||||
;; :composed-3 main: 3 shapes (frame + instance-of-simple-2 + its child)
|
||||
;; copy of :composed-2 (with swapped child): 4 shapes
|
||||
;; pasted copy of :copy-composed-2: 4 shapes
|
||||
;; pasted copy of :composed-3: 3 shapes
|
||||
;; pasted copy of :swapped-simple-2-frame: 2 shapes
|
||||
;; pasted copy of :swapped-simple-2-rect: 1 shapes
|
||||
;; Total = 1 + 2 + 3 + 4 + 2 + 3 + 4 + 4 + 3 + 2 + 1 = 29
|
||||
(t/is (= (count (:objects page')) 29)))))))))
|
||||
;; ==== Check
|
||||
;; Shape count breakdown (including page root):
|
||||
;; page root: 1
|
||||
;; :simple-1 main: 2 shapes (frame + rect)
|
||||
;; :composed-1 main: 3 shapes (frame + instance-of-simple-1 + its child)
|
||||
;; :composed-2 main: 4 shapes (frame + instance-of-composed-1 + its descendants)
|
||||
;; :simple-2 main: 2 shapes (frame + rect)
|
||||
;; :composed-3 main: 3 shapes (frame + instance-of-simple-2 + its child)
|
||||
;; copy of :composed-2 (with swapped child): 4 shapes
|
||||
;; pasted copy of :copy-composed-2: 4 shapes
|
||||
;; pasted copy of :composed-3: 3 shapes
|
||||
;; pasted copy of :swapped-simple-2-frame: 2 shapes
|
||||
;; pasted copy of :swapped-simple-2-rect: 1 shapes
|
||||
;; Total = 1 + 2 + 3 + 4 + 2 + 3 + 4 + 4 + 3 + 2 + 1 = 29
|
||||
(t/is (= (count (:objects page')) 29)))))))
|
||||
done)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Tests for issue-14302
|
||||
@ -588,48 +604,52 @@
|
||||
onto an empty page produced incorrect line breaks because stale
|
||||
position-data from the source page was preserved."
|
||||
(t/async done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [file (setup-board-with-texts)
|
||||
store (ths/setup-store file)
|
||||
;; copy-paste-shape handles initialize-page + select-shape (needed
|
||||
;; for calculate-paste-position) + paste-shapes + return to page-1
|
||||
events (copy-paste-shape :frame-1 file
|
||||
:target-page-label :page-2
|
||||
:target-container-id uuid/zero)]
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [file (setup-board-with-texts)
|
||||
store (ths/setup-store file)
|
||||
;; copy-paste-shape handles initialize-page + select-shape (needed
|
||||
;; for calculate-paste-position) + paste-shapes + return to page-1
|
||||
events (copy-paste-shape :frame-1 file
|
||||
:target-page-label :page-2
|
||||
:target-container-id uuid/zero)]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)
|
||||
page-2 (cthf/get-page file' :page-2)
|
||||
pasted-texts (->> (vals (:objects page-2))
|
||||
(filter #(= :text (:type %))))]
|
||||
(t/is (= 2 (count pasted-texts))
|
||||
"Both text shapes are pasted onto the empty page")
|
||||
(t/is (every? #(nil? (:position-data %)) pasted-texts)
|
||||
"Pasted text shapes have nil position-data so they get remeasured"))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)
|
||||
page-2 (cthf/get-page file' :page-2)
|
||||
pasted-texts (->> (vals (:objects page-2))
|
||||
(filter #(= :text (:type %))))]
|
||||
(t/is (= 2 (count pasted-texts))
|
||||
"Both text shapes are pasted onto the empty page")
|
||||
(t/is (every? #(nil? (:position-data %)) pasted-texts)
|
||||
"Pasted text shapes have nil position-data so they get remeasured"))))))
|
||||
done)))
|
||||
|
||||
(t/deftest paste-to-same-page-clears-text-position-data
|
||||
"Position-data is stripped on paste regardless of destination page."
|
||||
(t/async done
|
||||
(with-redefs [uuid/next cthi/next-uuid]
|
||||
(let [file (setup-board-with-texts)
|
||||
store (ths/setup-store file)
|
||||
events (copy-paste-shape :frame-1 file
|
||||
:target-container-id uuid/zero)]
|
||||
(mock/with-mocks {uuid/next cthi/next-uuid}
|
||||
(fn [done']
|
||||
(let [file (setup-board-with-texts)
|
||||
store (ths/setup-store file)
|
||||
events (copy-paste-shape :frame-1 file
|
||||
:target-container-id uuid/zero)]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)
|
||||
page-1' (cthf/get-page file' :page-1)
|
||||
;; Pasted copies have IDs not registered in idmap — they show
|
||||
;; up as "<no-label …>" strings from cthi/label.
|
||||
pasted-texts (->> (vals (:objects page-1'))
|
||||
(filter #(= :text (:type %)))
|
||||
(remove #(keyword? (cthi/label (:id %)))))]
|
||||
(t/is (= 2 (count pasted-texts))
|
||||
"Two new text shapes are pasted onto the same page")
|
||||
(t/is (every? #(nil? (:position-data %)) pasted-texts)
|
||||
"Pasted text shapes have nil position-data"))))))))
|
||||
(ths/run-store
|
||||
store done' events
|
||||
(fn [new-state]
|
||||
(let [file' (ths/get-file-from-state new-state)
|
||||
page-1' (cthf/get-page file' :page-1)
|
||||
;; Pasted copies have IDs not registered in idmap — they show
|
||||
;; up as "<no-label …>" strings from cthi/label.
|
||||
pasted-texts (->> (vals (:objects page-1'))
|
||||
(filter #(= :text (:type %)))
|
||||
(remove #(keyword? (cthi/label (:id %)))))]
|
||||
(t/is (= 2 (count pasted-texts))
|
||||
"Two new text shapes are pasted onto the same page")
|
||||
(t/is (every? #(nil? (:position-data %)) pasted-texts)
|
||||
"Pasted text shapes have nil position-data"))))))
|
||||
done)))
|
||||
|
||||
|
||||
@ -126,11 +126,11 @@
|
||||
;; (e.g. the notification emit itself throws).
|
||||
;; Without the re-entrancy guard this would recurse indefinitely.
|
||||
(when (= 1 @reentrant-call-count)
|
||||
(errors/on-error {:type ::test-reentrant :hint "secondary"})))
|
||||
(errors/on-error (ex-info "test" {:type ::test-reentrant :hint "secondary"}))))
|
||||
|
||||
(t/deftest on-error-reentrancy-guard-prevents-recursion
|
||||
(t/testing "a second on-error call while handling an error is suppressed by the guard"
|
||||
(reset! reentrant-call-count 0)
|
||||
(errors/on-error {:type ::test-reentrant :hint "first"})
|
||||
(errors/on-error (ex-info "test" {:type ::test-reentrant :hint "first"}))
|
||||
;; The guard must have allowed only the first invocation through.
|
||||
(t/is (= 1 @reentrant-call-count))))
|
||||
|
||||
@ -16,8 +16,13 @@
|
||||
[app.util.object :as obj]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.state :as ths]
|
||||
[frontend-tests.helpers.wasm :as thw]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(t/use-fixtures :each
|
||||
{:before (fn [] (thw/setup-wasm-mocks!))
|
||||
:after thw/teardown-wasm-mocks!})
|
||||
|
||||
(defn- setup
|
||||
"Creates a file with two pages (page1 as current) and a plugin context."
|
||||
[]
|
||||
@ -47,11 +52,13 @@
|
||||
;; Regression: the flow proxy returned by createFlow (and obtained later via
|
||||
;; page.flows) must expose a valid board proxy for `startingBoard`, carrying
|
||||
;; the plugin id rather than a corrupted handle. See issue #10203.
|
||||
(let [{:keys [context board-id]} (setup-with-board)
|
||||
^js page (.-currentPage context)
|
||||
^js board (.getShapeById page (str board-id))
|
||||
^js flow (.createFlow page "flow1" board)
|
||||
^js sb (.-startingBoard flow)]
|
||||
(let [result (setup-with-board)
|
||||
^js context (:context result)
|
||||
board-id (:board-id result)
|
||||
^js page (.-currentPage context)
|
||||
^js board (.getShapeById page (str board-id))
|
||||
^js flow (.createFlow page "flow1" board)
|
||||
^js sb (.-startingBoard flow)]
|
||||
(t/is (shape/shape-proxy? sb))
|
||||
(t/is (= (str board-id) (.-id sb)))
|
||||
(t/is (= plugin-id (obj/get sb "$plugin")))
|
||||
@ -71,24 +78,26 @@
|
||||
(ptk/emit! store (ptk/data-event ::dwpg/initialized page-id)))
|
||||
|
||||
(t/deftest test-open-page-returns-promise
|
||||
(let [{:keys [context]} (setup)
|
||||
^js pages (.. context -currentFile -pages)
|
||||
^js page2 (aget pages 1)]
|
||||
(let [^js context (:context (setup))
|
||||
^js pages (.. context -currentFile -pages)
|
||||
^js page2 (aget pages 1)]
|
||||
(t/is (instance? js/Promise (.openPage context page2)))))
|
||||
|
||||
(t/deftest test-open-page-new-window-returns-promise
|
||||
(let [{:keys [context]} (setup)
|
||||
^js pages (.. context -currentFile -pages)
|
||||
^js page2 (aget pages 1)]
|
||||
(let [^js context (:context (setup))
|
||||
^js pages (.. context -currentFile -pages)
|
||||
^js page2 (aget pages 1)]
|
||||
(t/is (instance? js/Promise (.openPage context page2 true)))))
|
||||
|
||||
(t/deftest test-open-page-invalid-arg-returns-nil
|
||||
(let [{:keys [context]} (setup)]
|
||||
(let [^js context (:context (setup))]
|
||||
(t/is (nil? (.openPage context "not-a-page")))))
|
||||
|
||||
(t/deftest test-open-page-resolves-when-page-changes
|
||||
(t/async done
|
||||
(let [{:keys [store context]} (setup)
|
||||
(let [result (setup)
|
||||
store (:store result)
|
||||
^js context (:context result)
|
||||
^js pages (.. context -currentFile -pages)
|
||||
^js page2 (aget pages 1)
|
||||
page2-id (obj/get page2 "$id")]
|
||||
@ -102,17 +111,19 @@
|
||||
|
||||
(t/deftest test-flows-returns-empty-array-when-no-flows
|
||||
;; page.flows must always return an array, even when the page has no flows
|
||||
(let [{:keys [context]} (setup)
|
||||
^js pages (.. context -currentFile -pages)
|
||||
^js page1 (aget pages 0)
|
||||
^js flows (.-flows page1)]
|
||||
(let [^js context (:context (setup))
|
||||
^js pages (.. context -currentFile -pages)
|
||||
^js page1 (aget pages 0)
|
||||
^js flows (.-flows page1)]
|
||||
(t/is (array? flows))
|
||||
(t/is (= 0 (.-length flows)))))
|
||||
|
||||
(t/deftest test-open-page-does-not-resolve-for-wrong-page
|
||||
;; Promise should not resolve when a different page is initialized
|
||||
(t/async done
|
||||
(let [{:keys [store context]} (setup)
|
||||
(let [result (setup)
|
||||
store (:store result)
|
||||
^js context (:context result)
|
||||
^js pages (.. context -currentFile -pages)
|
||||
^js page1 (aget pages 0)
|
||||
^js page2 (aget pages 1)
|
||||
|
||||
@ -55,59 +55,63 @@
|
||||
|
||||
(enable-console-print!)
|
||||
|
||||
(defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
|
||||
(defmethod t/report [:cljs.test/default :begin-test-var] [m]
|
||||
(let [v (:var m)]
|
||||
(println (str " ▸ " (:ns (meta v)) "/" (:name (meta v))))))
|
||||
|
||||
(defmethod t/report [:cljs.test/default :end-run-tests] [m]
|
||||
(if (cljs.test/successful? m)
|
||||
(.exit js/process 0)
|
||||
(.exit js/process 1)))
|
||||
|
||||
(def test-namespaces
|
||||
'[frontend-tests.basic-shapes-test
|
||||
frontend-tests.code-gen-style-test
|
||||
frontend-tests.copy-as-svg-test
|
||||
frontend-tests.data.nitrate-test
|
||||
frontend-tests.data.repo-test
|
||||
frontend-tests.errors-test
|
||||
frontend-tests.main-errors-test
|
||||
frontend-tests.data.uploads-test
|
||||
frontend-tests.data.viewer-test
|
||||
frontend-tests.data.workspace-colors-test
|
||||
frontend-tests.data.workspace-interactions-test
|
||||
frontend-tests.data.workspace-mcp-test
|
||||
frontend-tests.data.workspace-media-test
|
||||
frontend-tests.data.workspace-shortcuts-test
|
||||
frontend-tests.data.workspace-texts-test
|
||||
frontend-tests.data.workspace-thumbnails-test
|
||||
frontend-tests.helpers-shapes-test
|
||||
frontend-tests.logic.comp-remove-swap-slots-test
|
||||
frontend-tests.logic.components-and-tokens
|
||||
frontend-tests.logic.copying-and-duplicating-test
|
||||
frontend-tests.logic.frame-guides-test
|
||||
frontend-tests.logic.groups-test
|
||||
frontend-tests.logic.pasting-in-containers-test
|
||||
frontend-tests.plugins.context-shapes-test
|
||||
frontend-tests.plugins.page-active-validation-test
|
||||
frontend-tests.plugins.interactions-test
|
||||
frontend-tests.plugins.format-test
|
||||
frontend-tests.plugins.page-test
|
||||
frontend-tests.plugins.parser-test
|
||||
frontend-tests.plugins.text-test
|
||||
frontend-tests.plugins.tokens-test
|
||||
frontend-tests.plugins.utils-test
|
||||
frontend-tests.svg-fills-test
|
||||
frontend-tests.tokens.import-export-test
|
||||
frontend-tests.tokens.logic.token-actions-test
|
||||
frontend-tests.tokens.logic.token-data-test
|
||||
frontend-tests.tokens.logic.token-remapping-test
|
||||
frontend-tests.tokens.style-dictionary-test
|
||||
frontend-tests.tokens.token-errors-test
|
||||
frontend-tests.tokens.workspace-tokens-remap-test
|
||||
frontend-tests.ui.ds-controls-numeric-input-test
|
||||
frontend-tests.render-wasm.process-objects-test
|
||||
frontend-tests.util-object-test
|
||||
frontend-tests.util-range-tree-test
|
||||
frontend-tests.util-simple-math-test
|
||||
frontend-tests.util-webapi-test
|
||||
frontend-tests.worker-snap-test])
|
||||
['frontend-tests.code-gen-style-test
|
||||
'frontend-tests.copy-as-svg-test
|
||||
'frontend-tests.data.nitrate-test
|
||||
'frontend-tests.data.repo-test
|
||||
'frontend-tests.data.uploads-test
|
||||
'frontend-tests.data.viewer-test
|
||||
'frontend-tests.data.workspace-colors-test
|
||||
'frontend-tests.data.workspace-interactions-test
|
||||
'frontend-tests.data.workspace-mcp-test
|
||||
'frontend-tests.data.workspace-media-test
|
||||
'frontend-tests.data.workspace-shortcuts-test
|
||||
'frontend-tests.data.workspace-texts-test
|
||||
'frontend-tests.data.workspace-thumbnails-test
|
||||
'frontend-tests.errors-test
|
||||
'frontend-tests.helpers-shapes-test
|
||||
'frontend-tests.logic.comp-remove-swap-slots-test
|
||||
'frontend-tests.logic.components-and-tokens
|
||||
'frontend-tests.logic.copying-and-duplicating-test
|
||||
'frontend-tests.logic.frame-guides-test
|
||||
'frontend-tests.logic.groups-test
|
||||
'frontend-tests.logic.pasting-in-containers-test
|
||||
'frontend-tests.main-errors-test
|
||||
'frontend-tests.plugins.context-shapes-test
|
||||
'frontend-tests.plugins.format-test
|
||||
'frontend-tests.plugins.interactions-test
|
||||
'frontend-tests.plugins.page-active-validation-test
|
||||
'frontend-tests.plugins.page-test
|
||||
'frontend-tests.plugins.parser-test
|
||||
'frontend-tests.plugins.text-test
|
||||
'frontend-tests.plugins.tokens-test
|
||||
'frontend-tests.plugins.utils-test
|
||||
'frontend-tests.render-wasm.process-objects-test
|
||||
'frontend-tests.svg-fills-test
|
||||
'frontend-tests.tokens.import-export-test
|
||||
'frontend-tests.tokens.logic.token-actions-test
|
||||
'frontend-tests.tokens.logic.token-data-test
|
||||
'frontend-tests.tokens.logic.token-remapping-test
|
||||
'frontend-tests.tokens.style-dictionary-test
|
||||
'frontend-tests.tokens.token-errors-test
|
||||
'frontend-tests.tokens.workspace-tokens-remap-test
|
||||
'frontend-tests.ui.ds-controls-numeric-input-test
|
||||
'frontend-tests.util-object-test
|
||||
'frontend-tests.util-range-tree-test
|
||||
'frontend-tests.util-simple-math-test
|
||||
'frontend-tests.util-webapi-test
|
||||
'frontend-tests.worker-snap-test
|
||||
'frontend-tests.basic-shapes-test])
|
||||
|
||||
(assert (every? find-ns-obj test-namespaces)
|
||||
"test-namespaces contains a namespace that isn't required in runner.cljs")
|
||||
@ -221,18 +225,18 @@
|
||||
:once-fixtures (:once fixtures)
|
||||
:each-fixtures (:each fixtures))
|
||||
summary (volatile! {:test 0 :pass 0 :fail 0 :error 0 :type :summary})]
|
||||
|
||||
(t/set-env! env)
|
||||
|
||||
(t/run-block
|
||||
(concat [(fn [] (t/set-env! env))]
|
||||
(t/test-vars-block vars)
|
||||
(concat (t/test-vars-block vars)
|
||||
[(fn []
|
||||
(vswap! summary
|
||||
(partial merge-with +)
|
||||
(:report-counters (t/get-and-clear-env!))))
|
||||
(:report-counters (t/get-current-env))))
|
||||
(fn []
|
||||
(t/set-env! env)
|
||||
(t/do-report @summary)
|
||||
(t/report (assoc @summary :type :end-run-tests))
|
||||
(t/clear-env!))]))))
|
||||
(t/report @summary)
|
||||
(t/report (assoc @summary :type :end-run-tests)))]))))
|
||||
|
||||
(defn- run-focused-test!
|
||||
[focus]
|
||||
@ -252,8 +256,8 @@
|
||||
|
||||
:else
|
||||
(do
|
||||
(when-let [level (:log-level options)]
|
||||
(l/setup! {:app level}))
|
||||
(l/setup! {:app (or (:log-level options) :warn)})
|
||||
|
||||
(if (:focus options)
|
||||
(run-focused-test! (:focus options))
|
||||
(run-test-vars! (map #(selected-tests {:ns %}) test-namespaces)))))))
|
||||
|
||||
@ -22,11 +22,16 @@
|
||||
(json/encode {:type :json-verbose}))]
|
||||
(->> (rx/of json)
|
||||
(dwti/import-file-stream "core")
|
||||
(rx/take 1)
|
||||
(rx/subs! (fn [tokens-lib]
|
||||
(t/is (instance? ctob/TokensLib tokens-lib))
|
||||
(t/is (= "red" (-> tokens-lib
|
||||
(ctob/get-token-by-name "core" "color")
|
||||
(:value))))
|
||||
(:value)))))
|
||||
(fn [err]
|
||||
(t/do-report {:type :error :message "Stream error" :actual err})
|
||||
(done))
|
||||
(fn []
|
||||
(done))))))))
|
||||
|
||||
(t/deftest reference-errors-test
|
||||
|
||||
@ -107,13 +107,14 @@ pub extern "C" fn set_canvas_background(raw_color: u32) -> Result<()> {
|
||||
#[wasm_error]
|
||||
pub extern "C" fn render(timestamp: i32, flags: u8) -> Result<FrameType> {
|
||||
with_state!(state, {
|
||||
let render_state = get_render_state();
|
||||
state.rebuild_touched_tiles();
|
||||
// Drain the throttled modifier-tile invalidation accumulated
|
||||
// since the previous rAF. set_modifiers skips this work during
|
||||
// interactive_transform; we do it once here, with the current
|
||||
// modifier set, so the cost is paid once per rAF rather than
|
||||
// once per pointer move.
|
||||
if get_render_state().options.is_interactive_transform() {
|
||||
if render_state.options.is_interactive_transform() {
|
||||
// Collect into an owned Vec to release the immutable borrow on
|
||||
// `state.shapes` before the mutable `rebuild_modifier_tiles` call.
|
||||
let ids = state.shapes.modifier_ids().to_vec();
|
||||
@ -121,7 +122,8 @@ pub extern "C" fn render(timestamp: i32, flags: u8) -> Result<FrameType> {
|
||||
state.rebuild_modifier_tiles(&ids)?;
|
||||
}
|
||||
}
|
||||
let frame_type = if flags & RenderFlag::Partial as u8 == RenderFlag::Partial as u8 {
|
||||
let is_partial = flags & RenderFlag::Partial as u8 == RenderFlag::Partial as u8;
|
||||
let frame_type = if is_partial && !render_state.preserve_target_during_render {
|
||||
state
|
||||
.continue_render_loop(timestamp)
|
||||
.map_err(|_| Error::RecoverableError("Error rendering".to_string()))?
|
||||
|
||||
@ -2187,7 +2187,6 @@ impl RenderState {
|
||||
// quality pass completes.
|
||||
self.surfaces
|
||||
.reset_interactive_transform(self.background_color);
|
||||
self.surfaces.seed_backbuffer_from_target();
|
||||
self.interactive_target_seeded = false;
|
||||
} else {
|
||||
self.reset_canvas();
|
||||
|
||||
@ -907,17 +907,6 @@ impl Surfaces {
|
||||
self.tile_atlas.canvas().clear(skia::Color::TRANSPARENT);
|
||||
}
|
||||
|
||||
/// Seed `Backbuffer` from `Target` (last presented frame).
|
||||
pub fn seed_backbuffer_from_target(&mut self) {
|
||||
let sampling_options = self.sampling_options;
|
||||
self.target.draw(
|
||||
self.backbuffer.canvas(),
|
||||
(0.0, 0.0),
|
||||
sampling_options,
|
||||
Some(&skia::Paint::default()),
|
||||
);
|
||||
}
|
||||
|
||||
fn reset_from_target(&mut self, target: skia::Surface) -> Result<()> {
|
||||
let dim = (target.width(), target.height());
|
||||
self.target = target;
|
||||
@ -1631,15 +1620,13 @@ impl TileTextureCache {
|
||||
}
|
||||
|
||||
pub fn add(&mut self, tile_viewbox: &TileViewbox, tile: &Tile) -> TileAtlasTextureRef {
|
||||
if self.grid.len() > TEXTURES_CACHE_CAPACITY {
|
||||
// First we try to remove the obsolete tiles.
|
||||
self.gc();
|
||||
}
|
||||
// Evict against the real slot count (`provider.length`), not the
|
||||
// hardcoded capacity — otherwise the guard never fires and the atlas
|
||||
// fills up until `allocate()` has no slot left.
|
||||
let capacity = self.provider.length.min(TEXTURES_CACHE_CAPACITY);
|
||||
|
||||
// If we still have a texture capacity problem, then
|
||||
// we try to remove all of those tiles that aren't
|
||||
// visible.
|
||||
if self.grid.len() > TEXTURES_CACHE_CAPACITY {
|
||||
if self.grid.len() >= capacity {
|
||||
self.gc();
|
||||
self.gc_non_visible(tile_viewbox);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user