This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-02-28
Channels
- # announcements (11)
- # aws (1)
- # babashka (12)
- # babashka-sci-dev (6)
- # beginners (46)
- # biff (15)
- # calva (57)
- # clerk (6)
- # clj-kondo (50)
- # clj-together (1)
- # cljs-dev (14)
- # clojure (89)
- # clojure-doc (1)
- # clojure-europe (36)
- # clojure-nl (1)
- # clojure-norway (50)
- # clojure-spec (4)
- # clojure-uk (1)
- # clojurescript (56)
- # conjure (10)
- # cursive (1)
- # datalevin (2)
- # datomic (3)
- # fulcro (15)
- # honeysql (36)
- # hyperfiddle (74)
- # malli (19)
- # membrane (16)
- # off-topic (33)
- # pathom (6)
- # polylith (2)
- # reagent (14)
- # releases (2)
- # rum (5)
- # shadow-cljs (51)
- # sql (6)
- # tools-build (10)
- # xtdb (6)
Is there a limit to recursive calls? Looks like I caused a stack overflow in the compiler when I tried.
you probably ran out of stack, the compiler is not optimized, increase stack size with the jvm flag in our deps.edn
what “recursive call” where you doing exactly?
Something akin to
{:strs-to-render ["...."]
:recursive-target {:strs-to-render ["...."]}}
(e/defn RenderRecursive
[x]
(dom/div
(dom/text (apply str (:strs-to-render x)))
(when-let [r (:recusive-target x)]
(RenderRecursive. r))))
i don’t even think we support recursive calls, you have to use a workaround to recur by binding, it’s linked at the bottom of the readme
(yet)
btw when i said you may have run out of stack, i meant the electric compiler ran out of stack during the macroexpansion analysis
can electric functions return functions? e.g. I've got a fn that returns two bits of dom that I'd like to render in different locations. e.g.
(defonce !a #?(:clj (atom 4)
:cljs nil))
(e/defn TestReturnsFn
[text]
{:a (fn []
(dom/div
(dom/text (str ":a TestReturnsFn" text))))
:b (fn []
(dom/div
(dom/text (str ":b TestReturnsFn" text))))})
(e/defn OwningFn
[]
(let [a (e/watch !a)]
(e/client
(dom/div
(dom/text "btn")
(ui/button
(e/fn [] (e/server (e/discard (swap! !a inc))))
(dom/text "btn"))
(let [{:keys [a b]} (TestReturnsFn. a)]
;;(a)
;;(b)
;;(b)
nil
)
))))
------ ERROR -------------------------------------------------------------------
File: /Users/jarrett/code/jra/electric-datomic-browser/src/user.cljs:8:3
--------------------------------------------------------------------------------
5 | hyperfiddle.electric-dom2))
6 |
7 | (def electric-main
8 | (hyperfiddle.electric/boot ; Electric macroexpansion - Clojure to signals compiler
---------^----------------------------------------------------------------------
null
Can't set! local var or non-mutable field at line 8 user.cljs
--------------------------------------------------------------------------------
9 | (app.datomic-browser/DatomicBrowser.)))
10 |
11 | (defonce reactor nil)
12 |
--------------------------------------------------------------------------------
The compiler seems upset though.In your example, :a and :b are regular clojure fns. They should be e/fn since you are using electric-dom inside of them.
e/fn, just like e/defn, is called with new
The compiler error message is not helpful, sorry for that. We'll improve it.
The error message now looks like this:
Encountered error when macroexpanding hyperfiddle.electric/boot.
`hyperfiddle.electric-dom2/node` is an Electric var and cannot be bound from a Clojure context.
{:file "user_main.cljc", :line 90, :in [(fn* ([] (dom/div (dom/text (str ":a TestReturnsFn" text)))))]}
FWIW the language unit tests demonstrate a some cool/tricky things, for example we have https://github.com/hyperfiddle/electric/blob/ed8db1ddece174721b16675267ae96b6be4718ae/test/hyperfiddle/electric_test.cljc#L1818-L1836
is there a good example of testing electric components? do you unit test a component or do browser testing like cypress?
Our experimental ui4 controls have headless browser tests, e.g. for https://github.com/hyperfiddle/electric/blob/c9c2d8504092d4a034c6f1cb9e85423964e1299c/test/hyperfiddle/electric_ui4_typeahead_test.cljc. These run with every git push. They were written through a cljs repl with a real browser, so you can write/debug the tests incrementally
@U11SJ6Q0K to be clear, are you looking for UI tests? i.e. testing the DOM views?
if you create components that mix frontend and backend stuff, then I would expect to want to test the whole component, not just dom
https://github.com/hyperfiddle/electric/tree/master/src-docs/user/electric shows how to test electric functions, this is an old language tutorial
here is our https://github.com/hyperfiddle/electric/blob/master/test/hyperfiddle/electric_test.cljc (we have other lower level tests for compiler/runtime which wont be helpful)
I am not sure if this will help you though
If I was using Electric to build something like Electric Datascript, i'd expect the tests to be mostly unit tests like this
If I was using Electric to build a CRUD app, i'd expect the queries to be written in Clojure in the usual way (i.e. this is your problem not an electric problem)
Your question is thus really about how to test the views of the crud app (the query/dom composition), not the queries in isolation. Do I understand?
there’s usually a testing pyramid, most tests are unit tests, then there are integration tests that test these in unison and finally e2e tests
so usually the e2e will be cypress/playwright/etc running against a “real” deployed env, but the middle layer of testing that components work together
the middle query/view component integration tests (is this description accurate?) is the part that I dont have a clear answer for you yet — but here's the thing, I have never seen an answer i liked on any stack. What is the best practice for this in a React.js/GraphQL app?
usually there is none, because the frontend and backend is completely separate… I was sort of hoping that with electric there could theoretically be a good solution for this middle level of tests 🙂
i agree that with the electric primitives we may be able to do better than the status quo
i just don't know how yet
the middle level I have seen is of the “let’s test the APIs we expose to the frontend work properly”
To some extent, the point of the JVM is to trust that the JVM has unit tests in place for memory management, so that userland doesn't need to write tests for that anymore
Electric managed network I think is like that, application programmers shouldn't need to test it
But higher level component tests, like "I made a server streamed paginated grid, use CI to prove that it doesn't crash and that it doesn't leak DOM"
There is a place for that, and the https://github.com/hyperfiddle/electric/blob/c9c2d8504092d4a034c6f1cb9e85423964e1299c/test/hyperfiddle/electric_ui4_typeahead_test.cljc example Peter linked may be a starting point for developing tests like that. But it's an open question, still working out the idioms
For example, our unit tests today don't run a jvm/browser system, they are browser-only or jvm-only and any client/server in the test is loopback
so we'll need to figure that out
but not a deal breaker, usually one needs e2e tests anyway to make sure the final product works in a deployment scenario anyway
but, if there were a great way to do the middle layer of tests, I would write more of those and less of cypress tests (as I don’t really enjoy writing cypress tests)
can i see a snippet of cypress tests that you don't like writing?
can’t show work code, but even the todomvc test suite https://github.com/tatut/cypress-example-todomvc/blob/master/cypress/integration/app_spec.js (that’s not too bad, but still would prefer clojure)
you often end up needing to add either classes or some data-cy
attributes to your markup to make it more easily testable
excellent link ty
I have a query who render some items data. Without e/wrap
only partial information are re-render (see the right window) when some informations are updated.
Now if the query is wrap by e/wrap
it seems like all the items are re-render when an update occurs:
The first video seems to show the right behavior: only what changed is rerendered.
e/wrap
is required for IO blocking operations. Since datascript is in-memory, and if your query is fast enough to not block the main thread, then e/wrap is not necessary.
e/wrap
will start by throwing Pending, then will emit the result of its body.
In the second video, the list rerenders because of Pending is thrown by e/wrap.
To improve your understanding of Pending, a good exercise would be to add (try … (catch Pending …))
around your e/wrapped query, then move this try block higher up in your code, so you can see the difference.
isn’t the previous value in effect while the query is pending? or is that only when pending is caught
Thanks @U2DART3HA for you explanation. With try/catch
, I have the same behaviour. Maybe, I made a mistake. I have something like this:
(e/defn Rooms []
(e/server
(let [rooms (try
(e/wrap (repo/list-room db/db))
(catch Pending _))]
...
(dom/div (dom/props {:class (styles :rooms/room-list-container)})
(e/server
(e/for-by :room/id [{...} rooms]
(e/client ...))))
(catch Pending _)
returns nil, so when Pending is thrown, rooms
is nil.
Therefore e/for-by
will unmount all rows.
When e/wrap
returns the query result, rooms
is now a collection again, so e/for-by
will remount all rows.
@U11SJ6Q0K In Electric, try is reactive. You can think about reactive try as a conditional: • If there is no Pending being thrown ◦ then run the try branch ◦ else also run the catch branch The main difference between reactive try and reactive if, is that the try branch keeps running when a Pending is thrown. This way when the Pending exception is not thrown anymore, the catch branch is discarded.
@J move the try/catch Pending higher. DOM remains attached during Pending (and any other exception). Exceptions do not unmount DOM.
> isn’t the previous value in effect while the query is pending? or is that only when pending is caught @U11SJ6Q0K the answer is yes
I have something like this now:
(e/defn Rooms []
(e/server
(try
(let [rooms (e/wrap (repo/list-room db/db))]
...
(dom/div (dom/props {:class (styles :rooms/room-list-container)})
(e/server
(e/for-by :room/id [{...} rooms]
(e/client ...))
(catch Pending _)))
But still the same behaviour.@UHZPYLPU1 we've just now found another regression related to pending, sorry. Not sure if it impacts what you are seeing or not
@UHZPYLPU1 the Pending regression is fixed in master
Here is a demonstration of correct pending behavior, lightly modified from demo-4-chat. • The document.body flashes yellow due to the Pending thrown at L28, and also L16 (try two tabs)
@UHZPYLPU1 if you're still seeing the lists rebuild, let's please use a common demo as a starting point, like user.demo-4-webview
or user.demo-3-system-properties
or user.demo-4-chat
(whichever fits your situation best)
Thanks @U09K620SG I will take a look!
The pending regression in master is fixed, also increasing confidence that master hot code reloading is stable