Fork me on GitHub
#shadow-cljs
<
2023-02-07
>
Shuai Lin11:02:33

Hey @thheller for :target :esm can we output each entry as a separate ESM file, much similar to :npm-modules?

:app-esm {:target :esm
          :modules {:main {:entries [app.mod1 app.mod2]}}}

thheller11:02:07

if you must have one module per namespace then :npm-module is the way to go

thheller11:02:42

:esm also really expects exports, otherwise its pretty useless in an interop scenario

thheller11:02:50

but you'd do it like this

{:target :esm
 :modules {:base {:entries []}
           :app.mod1 {:entries [app.mod1] :depends-on #{:base}}
           :app.mod2 {:entries [app.mod2] :depends-on #{:base}}}}

thheller11:02:08

so all the shared stuff goes into :base

Shuai Lin11:02:51

Guess I could hack a bit with the build hooks to scan the -test$ namespaces, then rewrite the :modules config to the above format

thheller11:02:11

as I said. if you must have one namespace per file

thheller11:02:18

then you should really only consider :npm-module

thheller11:02:53

it is possible :esm. but far too much trouble doing everything manually

thheller11:02:17

I have some time in a bit, so I can see if I can figure out your :npm-module problem

Shuai Lin11:02:47

Thx! I'll try with npm-module for now.

Shuai Lin11:02:33

btw :npm-module doesn't support browser runtimes, right?

thheller11:02:37

maybe you should start explaining what you are trying to do exactly?

thheller11:02:10

:npm-module supports browsers, but only with more processing by third party tools as browser can't load commonjs files otherwise

Shuai Lin11:02:07

there was a typo, I wanted to say "not support browser runtimes"

Shuai Lin11:02:38

So I uses the :npm-module to output things like app.foo-test.cljs to app.foo-test.js and cypress test running would load it and execute it in the browser

thheller12:02:04

and why are you looking into worker-info?

Shuai Lin12:02:52

I'm following the logic of the code to check why the browser page doesn't connect back to shadow-cljs as a runtime, maybe I'm looking at the wrong places

Shuai Lin12:02:03

btw the :preloads is also not picked up in npm-modules

thheller12:02:40

why do you want the runtime to connect back? tests and repls usually don't play well with each other

thheller12:02:56

I also do not know how cypress handles runtimes and re-running tests

thheller12:02:23

so, if you just want a REPL but let the tests run on their own I recommend running a separate shadow-cljs browser-repl or so

Shuai Lin12:02:20

> tests and repls usually don't play well with each other yeah most time they don't, but in my case it's a pretty good match. With the :esm target I can use shadow-cljs's reload for fast test-rerunning

Shuai Lin12:02:51

e.g. in ^:dev/after-load to kick a re-run

thheller12:02:04

preloads and runtimes are a bit more complicated with :npm-module since there is no controlled entry point

thheller12:02:11

since you can just load any namespace via JS

Shuai Lin12:02:17

which is much faster than the builtin liveReload of cypress (which comes from webpack)

thheller12:02:43

so you'd have something like require("./cljs-out/your.preload") in some prepended configuration or so

thheller12:02:19

require("./cljs-out/shadow.cljs.devtools.client.browser") is the ns for the REPL/hot-reload runtime

Shuai Lin12:02:45

:thinking_face:

Shuai Lin12:02:09

Thanks for the tips, I'll think about it. Maybe I'll simply go the way of using hooks to crunch the esm modules config like the one you mentioned above

{:target :esm
 :modules {:base {:entries []}
           :app.mod1 {:entries [app.mod1] :depends-on #{:base}}
           :app.mod2 {:entries [app.mod2] :depends-on #{:base}}}}

thheller12:02:43

hooks can't do that

thheller12:02:10

maybe :browser-test works with cypress? I really never looked into cypress much

Shuai Lin12:02:25

probably not, cypress has its own tests runner (one based on mocha) so tests have to be defined with mocha's primitives

thheller12:02:52

well you can define your own runner with :browser-test. doesn't have to be cljs.test

Shuai Lin12:02:31

but there would be a bunch of other stuff to wire up together, assertions, matchers, etc. so that's an another rabbit hole. Beside cypress is really a totally different beast in conducting the tests, so I'd rather avoid it.

thheller12:02:36

I really know very little about cypress and how it loads things, so really can't comment much from the cypress side

thheller12:02:53

thats not really what I'm talking about there

thheller12:02:16

:browser-test just generates JS, with the assumption that it is executed in a browser environment

thheller12:02:23

it makes no further assumptions whatsoever

thheller12:02:32

however you use cypress now you'd use it then?

thheller12:02:47

I assume there is some kind of npm package you import?

Shuai Lin12:02:44

no npm package, all stuff is injected into the window object by cypress

Shuai Lin12:02:52

I'll definitely try that

thheller12:02:13

do you have an example cypress setup?

thheller12:02:25

like with :npm-module?

Shuai Lin12:02:31

I'll create a repro today

thheller12:02:53

I do remember trying something with cypress, but can't remember how far I got

Shuai Lin12:02:42

It's pretty fascinating for doing react component testing. Otherwiser shadow's builtin browser-test is great enough for normal frontend unit tests

Shuai Lin12:02:41

Being a js land tool it's by nature not a native fit for cljs, e.g. assert matchers for cljs data structures has to be custom registered

Shuai Lin12:02:28

but once these rough egdes are smoothed it would be like a dream land for component based TDD

thheller12:02:15

oh right it was one of those things that expects files in certain locations and so on

Shuai Lin13:02:00

This is what I'm using cypress for, i.e. component testing

thheller14:02:10

can't see what you are pressing. do you just save the file and cypress triggers running it or how does cypress get the new results?

thheller14:02:30

or is cypress not watching at all and it just runs via js/it ...?

thheller14:02:20

I can see shadow-cljs reloading something, but can't tell if cypress does anything 😛

Shuai Lin14:02:37

Well it's a "customized" version of cypress. The official cypress would actually reload the page completely each time the test ns is rebuilt.

thheller14:02:02

is that configurable or actually hacking the npm package?

Shuai Lin14:02:55

not configurable at all, but hacking the bundled js file in cypress electron app

thheller06:02:52

I now remember why I abandoned looking into cypress more

thheller07:02:12

the bundler configuration seems hardcoded to webpack/vite and can't be extended/replaced

Shuai Lin07:02:23

it does supports custom devserver, but the protocol is not publicly documented

thheller07:02:02

ah, maybe that can be used for something

thheller08:02:57

do you maybe have a clue what JS provides the describe it etc functions?

thheller08:02:11

I have something running but I don't know where to get those from 😛

thheller09:02:26

alright, found them

thheller09:02:36

now we are getting somewhere 😉

thheller10:02:57

(def cy js/parent.Cypress)
(.onSpecWindow cy js/window #js [])
(.action cy "app:window:before:load" js/window)

thheller10:02:06

seems to make everything work, but I can do better I think

thheller10:02:18

I got it running completely with hot-reload and without vite/webpack

thheller10:02:40

or any other hacks to the electron app

thheller10:02:59

will try some more later but looks promising now. no more hacks 😉

Shuai Lin07:02:36

Just to confirm, you are playing with cypress component test, not e2e test, right?

thheller07:02:12

yes, I played with component tests but abandoned it after discovering that the same trick cannot be done for e2e

thheller07:02:25

and I couldn't figure out how to tell cypress to re-run the test after a hot-reload. since there is no documentation for any of this, at least nothing I could find

thheller07:02:55

got tired of reverse engineering the webpack integration

thheller07:02:14

I'm sure its just a dumb function call somewhere but I couldn't find it

Shuai Lin08:02:56

For e2e it's simple

(defn ^:dev/before-load click-cypress-ui-restart-button
  []
  (when-not (= js/window.parent js/window)
    (-> (js/window.parent.document.querySelector "button.restart")
        .click)))
IMHO it's okay for the app iframe to do a full reload in e2e tests, since most of the e2e tests takes much longer and the benefit of hot-reloading over full page reload is dwarfed.

Shuai Lin08:02:15

emm, on a second thought I agree it might have greater benefit than I have estimated if we have hot reloading in e2e tests, e.g. we can skip lots of time consuming fixtures like logins, cleanups etc. :thinking_face:

thheller08:02:40

e2e can't replace the bundler though

thheller08:02:49

there is no devServer option equivalent as far as I can tell

thheller08:02:00

so it always uses webpack no matter what

Shuai Lin08:02:05

yeah it would be more complex than CT. In CT there are two windows: top (cypress UI) & specWindow (where it/describe etc. and app code runs)

Shuai Lin08:02:35

In e2e there are 3: top (cypress UI), specWindow (where it/describe etc. runs), and the app iframe

Shuai Lin11:02:11

and I'd like to have app.mod1.js and app.mod2.js under <output-dir>

Shuai Lin11:02:53

right now if I specify two modules it asks me to specify the deps between them

:app-esm {:target :esm
          :modules {:app.mod1 {:entries [app.mod1]}
                    :app.mod2 {:entries [app.mod2]}}}