Fork me on GitHub
#clojurescript
<
2021-04-20
>
johanmynhardt09:04:16

I'm stuck. I'm trying to read an ES6 file that basically just contains a data structure. If it was JSON I could parse it that way. I suspect this is possible, but I'm not a ClojureScript expert and don't know where to start. My Google search results are not too fruitful and I'm wondering if I'm going at it in the wrong way?

thheller09:04:09

read it at runtime or use it during compile time to use in your app?

johanmynhardt09:04:18

I would say compile time, although it's an intermediate step to use it in Clojure. I'm trying to do infrequent conversions from such .js files to .edn for use in my main project (Clojure), so I figure it can be considered to be compile time. I started looking at ClojureScript because I couldn't find tools just in Clojure and figured ClojureScript does this all the time, somehow, if I'm not misunderstanding. I just can't figure out how exactly I would go about it.

p-himik10:04:30

Assuming you're using shadow-cljs, it should be possible, I think: https://shadow-cljs.github.io/docs/UsersGuide.html#_requiring_js

p-himik10:04:51

You just require that JS file and use it via JS interop of just convert it with js->clj.

johanmynhardt10:04:55

🙏 Thank you, this is most helpful. I'm going to have a look. I've parsed it by hand using regex as the structure was simple enough but it's not sustainable 😅 I have to get a real solution.

👍 4
johanmynhardt18:04:40

Nice, so I got some time tonight to play around/figure out doing this. Wish I had rather spent Friday doing this! 🙈

Aron13:04:04

What do people use for UI test automation? Because I am struggling with puppeteer, I get different results in headful and headless mode and it's impossible to know what's different.

Ronny Li14:04:00

Somewhat off-topic but is anyone here using https://headlessui.dev/? What are your thoughts? We use Tailwind CSS and think it's brilliant so we're biased to think that Headless will be great too.

dnolen15:04:37

@ashnur fwiw I don't believe in UI test automation

dnolen15:04:41

too much work too brittle

👍 9
dnolen15:04:27

a better approach to higher quality UI work is to adopt a pure functional UI

dnolen15:04:51

and for all components to be fully tested for the meaningful states in total isolation from all business logic

dnolen15:04:07

Storybook.js and Devcards permit this style of UI development

dnolen15:04:35

then any UI issue that crops up had to been business logic

dnolen15:04:38

not components

dnolen15:04:57

and the business logic is easier to understand/discern

dnolen15:04:45

QA is necessary of course

dnolen15:04:02

and maybe UI test automation can catch some QA issues - but I doubt it

dnolen15:04:13

any serious app needs to run on too many different platforms, too many differents OSes etc.

dnolen15:04:20

it's a waste of time

dnolen15:04:00

meaning the effort to set it all up is just enormous

dnolen15:04:10

what I'm saying above which is prevent writing bad code in the first place

dnolen15:04:29

and then when you get to QA and human has to drive it - it's meaningful testing - not BS

dnolen15:04:49

I'll reiterate something I've said before

dnolen15:04:12

Devcards or Storybook.js is probably the most important change I've made to UI development since the appearance of React

dnolen15:04:44

nothing else I've tried has been as transformative since ClojureScript+React

Aron15:04:00

Thanks for taking the time to answer @dnolen. I am not writing these tests to achieve, as you write, 'higher quality UI', I am trying to not have to do these simple checks manually because it takes much more time like that.

dnolen15:04:22

sure but I'm just saying - you're not going to save any time

dnolen15:04:39

well maybe you will, or think you will

dnolen15:04:54

but I've never been able to get it save me any time in 16 years

2
Aron15:04:11

I am going to lose quality of life if I have to repeat the same 10000 actions every day

Aron15:04:14

so even if you are right and I am wrong about how much time it takes overall, the time I spent waiting for the tests (and writing them) to run is qualitatively different than the time I spent manually testing (and making humanly understandable but professionally very deteriorating mistakes)

dnolen15:04:14

but the question you should be asking

Stuart15:04:19

Agree with dnolan, automated ui testing ime is always greatly more effort than reward.

2
dnolen15:04:22

is why do you need to go through a flow

dnolen15:04:30

again and again if it's not QA

dnolen15:04:33

i.e. pre-ship time

Aron15:04:37

because if I repeat the same actions, I will make mistakes, and get many more false positives and false negatives

dnolen15:04:46

everything you do should be setup so you don't have retest anything

Aron15:04:47

it is pre-ship time

dnolen15:04:18

right there is no good answer for pre-ship time that I've seen other than manual QA

dnolen15:04:26

maybe AI one day

Aron15:04:50

sorry, I can't restrain myself when people idolize AI like that

Aron15:04:15

but that's beside the point, I see that people do not have the same problems as I have, again

dnolen15:04:26

that was meant to be joke 😉 and half-serious

Aron15:04:31

I know, I shouldn't be trying to create a fixed experience in the first place, people on the other side, using their own browsers should decide how the interface looks like, not us

Aron15:04:06

but that's again, beside the point. I have very good experience with UI automation, I even used it in property based testing

Aron15:04:16

this is the first time I have a redundant problem like this

dvingo15:04:18

another issue is when testing advanced compilation code - how can one do this without using a headless setup?

Aron15:04:21

in like 7 years

Aron15:04:42

@dnolen fww, i did try to use devcards and storybooks but 1. neither worked well with react hooks the way I tried 2. after I did the nice components there, I had to insert my react code into the actual Django templates from which they are served and they got wrapped into this minimized css that the company had for 7 years now and it's not being touched, don't even have the un-minimized source for it afaik, and of course the major business logic is not how things look but how things interact, and since it's not just a REST api but also cookies and other parts of the page define the context, I really didn't see any point to not develop the app inside the page where it will appear anyway. This way at least I can test it when I am working on it. But because testing means testing with all kinds of different users that I have to create manually because there is no other way, most of the time I am not actually testing my work, just "preparing the backend state" for testing, because that defines fully the frontend state.

Aron15:04:01

I did build a functional UI on datascript and mori and mercury in 2015 2016, the two packages had to be compiled together, and it was really verbose to use them from js at the time, but it worked nicely, didn't have to test the UI by actually clicking on things to see if they work

dnolen16:04:54

@ashnur huh I don't understand your point about React Hooks, we use those those w/o issue

dnolen16:04:07

but note we just write our components in JS

raspasov20:04:55

So you write JSX as opposed to using React CLJS interop?

dnolen16:04:54

I just don't believe in writing components where they are going to be anymore

dnolen16:04:04

it just NxN problems

dnolen16:04:52

In your case that N may be large - and maybe, in this instance UI automation is going to get you something

Aron16:04:34

yeah, I am not testing components, I have about a dozen files, each just opens the page, performs a bunch of user actions on the whole page and reads content of the page

Aron16:04:42

I see zero sense in testing components out of their context, they are never actually used like that in practice.

lilactown16:04:51

we use CLJS + hooks + storybook in our apps

Aron16:04:10

how do you write reusable stateful components

lilactown16:04:30

we also use react-testing-library to test our components in isolation

Aron16:04:31

think something that handles a predictive input with arbitrary input text allowed

lilactown16:04:11

🤷:skin-tone-2: same way we would build any reusable stateful component. use local state inside the component, provide props to control the behavior

Aron16:04:41

yeah, so where is the local state, in a use-state hook in the top component?

lilactown16:04:43

the link to headlessui.dev above is a good example of that

Aron16:04:37

so, when you are in your editor, there is probably some repl connected to the browser runtime env?

lilactown16:04:17

um, yes when I'm developing in our app project I typically have a REPL connected

lilactown16:04:41

the REPL connection doesn't work with our storybook setup, but we mainly use it for documentation of our design library

Aron16:04:57

and can you read current state of any shared component through your editor? I've been trying to see an example of that, especially with Storybooks

lilactown16:04:50

I typically use React DevTools + the JS console to inspect the state of React components

lilactown16:04:22

I have experimented with adding REPL support for inspecting the React tree but haven't found a really good interface to use it yet

Aron16:04:23

I use (tap>) and shadow-cljs has a web ui where it is nicely browseable

Aron16:04:31

and of course react devtools

Aron16:04:55

the only solution is to not use hooks, this is why I said I couldn't make it work

Aron16:04:05

@dnolen ☝️:skin-tone-2:

Aron16:04:25

I only mean state hooks, not effect hooks

Aron16:04:59

so obviously use-ref would work fine, but it's really a hassle to set it up, and I am very much dissillusioned with react since they removed resuming

lilactown16:04:44

sounds like it's not that it didn't work i.e. you couldn't build an app with it, but that you didn't like the developer experience

lilactown16:04:49

which is fair

lilactown16:04:42

I find that using component local state is architecturally better for us so I'm willing to live without the ability to easily use the REPL for inspecting local component state

Aron16:04:48

well, you have to choose, if I am not using react hooks the obvious choice is to not use react at all or use reagent

Aron16:04:04

my issue with local state is this page https://reactjs.org/docs/lifting-state-up.html

Aron16:04:37

> I want to highlight that in typical React apps that rely on `setState()` this is the single most common kind of React-specific refactoring that you would do on a daily basis.

Aron16:04:01

I just don't want to.

dnolen16:04:04

hrm I'm doing mostly React Native these days

dnolen16:04:14

I didn't find any of the React Native "tools" useful

dnolen16:04:49

so I don't know - I'm a minimalist

Aron16:04:04

can I ask the same question from you? do you use atoms or something else for reusable component state?

Aron16:04:22

reusable in the sense that the same namespace is used multiple times throughout the page with different state, but the logic is implemented only once and then gets required and filled out by other components

dnolen16:04:22

all components are written in JS, verified on the device and simulators via Storybook.js

dnolen16:04:28

all styling and theming is done via React context

dnolen16:04:48

all components are Pure Components, using hooks for state only for internal stuff

yes 3
dnolen16:04:54

everything comes in via props

yes 3
dnolen16:04:09

everything most compose and must be representable in Storybook.js

dnolen16:04:23

if you cannot create the state in Storybook - then it can't be created at all

dnolen16:04:30

once this cycle is done

dnolen16:04:39

we just adapt them to Reagent components

dnolen16:04:54

then the "app" is only like a thousand lines of ClojureScript code

dnolen16:04:11

even you have many screens and many states

dnolen16:04:01

the ClojureScript code has no styling, no theming

dnolen16:04:07

no presentation logic of any kind

dnolen16:04:21

other than the highest level structure

dnolen16:04:47

all routing is done via 10 lines of core.match

dnolen16:04:11

any internal route is handled because none of the Pure Components have subview order

dnolen16:04:48

so you can make the flow state machine w/ a ClojureScript map

dnolen16:04:54

and change it to be any order you want

dnolen17:04:15

the only thing of interest here is that it's shockingly simple

dnolen17:04:45

and probably this has lead to fewest bugs I've encountered on a UI project myself

dnolen17:04:02

nearly all issues stem from larger integration problems - but that's to be expected

lilactown17:04:37

sounds like a dream project 😄

dnolen17:04:56

it would be nice to replace the Storybook.js stuff w/ ClojureScript but in the end it really doesn't matter much

dnolen17:04:20

if you write only Pure Components it's feels more or less like ClojureScript

dnolen17:04:33

no data transformation of any kind is allowed

dnolen17:04:57

because of the ES6 iteration protocols, everything more or less works

dnolen17:04:39

we have one tiny clever thing w/ React Context which we inject from CLJS to convert props at Pure Component boundary (since this is nested React components not nested Reagent components)

dnolen17:04:48

but that was a couple of lines of code

lilactown17:04:01

we did the same thing at my last company. design library in JS + storybook, product used CLJS

lilactown17:04:15

however, the major reason we did that is we had other consumers of the design lib that had been written in JS

dpsutton17:04:04

was wondering about that boundary between js objects/arrays and cljs immutable types

lilactown19:04:37

what do you mean?

dpsutton20:04:41

when calling cljs functions from js, ensuring that arrays becomes vectors, objects become maps, etc

lilactown20:04:37

it depends. I typically don't see the point in transforming to and from unless it's a necessary to interface with a particular part of a system

lilactown20:04:34

i.e. if I'm getting some JSON from an endpoint and want to operate on it immutably in my UI, I'll do that transformation once on receiving and store it that way

lilactown20:04:29

or, if I'm using some 3rd party library that takes as input a JS object based on some immutable data I have in hand, then I'll manually write the construction of the JS object

lilactown20:04:40

clj->js and js->clj is a huge code smell to me IMO

lilactown20:04:14

sometimes it doesn't matter, but too often it can lead to errors or performance problems

dpsutton21:04:10

we have a query analyzer that we added in to the frontend. and it of course "speaks" clojure, being built in the backend. and the frontend is a react js app. there's a huge boundary of arrays and objects and those js types can't really flow through the backend

dpsutton21:04:37

so it makes sense to put a boundary layer, strings to keywords in some places, arrays to vectors, etc

lilactown21:04:42

yes, in that case I would make the layer very explicit

lilactown21:04:55

it's very tempting to just (js->clj x :keywordize-keys true) and call it a day, but it makes it much more difficult to control the translation

lilactown21:04:28

you might find that the way the UI expresses certain things doesn't quite add up to what the analyzer expects and you end up having to translate it after the fact anyway

dpsutton17:04:15

i imagine cljs-bean comes in handy?

djblue17:04:20

An approach we are taking at work for e2e ui testings is allowing the test runner to introspect on the client state. This allows us to assert directly on app state instead of derived views. That also allows us to synchronize our ui interactions when we know the app is in a good state. Of course, the big assumption we make is that the mapping between app state and views is correct, which we mostly validate via unit tests.

djblue17:04:12

Essentially the code looks like:

(defn e2e-test [driver user-story]
  (loop
    (let [state       (get-state driver)
          thing-to-do (next-thing-to-do state user-story)]
      (do-the-thing! driver thing-to-do)
      (when-not (done? driver user-story)
        (recur)))))

djblue18:04:29

Another point of note here is that the user story itself is actually a subset of the app state. This means we can leverage our UI to also update the user story when things do change.

djblue17:04:20

The nice thing here is that the user-story and state are both just data and the thing to do can be wait for now cause I can't currently do anything

Aron17:04:00

I thought about that, but because this is embedded in a django app and sometimes I need to check if things become disabled or enabled, checking the state might not be enough

dnolen17:04:04

@djblue right's more or less what I'm trying to suggest

dnolen17:04:32

if your components are in Storybook.js that's strong evidence of the mapping

💯 3
dnolen17:04:56

then you can just write simple tests about state transitions about data

dnolen18:04:02

there are limits for our app though

dnolen18:04:14

because we have a lot of native code integration, remote services

dnolen18:04:26

and I'm not fan of mocked tests

💯 3
djblue18:04:41

In my code example above the driver is a handle into a browser that's connected to our development environment so the app has access to all necessary services and we don't need to mock anything, and we can test actual integration at every level

fsd18:04:05

Hello There, I am having problem setting the dates in ClojureScript, for some reason the application is keeps on setting previous date.

{
:start-date (js/Date. start-date) 
:end-date (js/Date. end-date)
}
Is this the correct way of setting the time? When I change my computer timezone to different location, the application timing starts working fine.
(println start-date)
-> #time/date "2021-05-10"

(js/console.log (js/Date. start-date))
Sun May 09 2021 20:00:00 GMT-0400 (Eastern Daylight Time)
Any help would be appreciated

p-himik18:04:11

What do you mean by "setting the time"? Setting it where, exactly?

fsd18:04:25

for example I have #time/date “2021-04-21” I am trying to convert this to as javascript new Date()

p-himik18:04:30

It is that js/Date, most likely. It's just that println prints it that way. It's OK for println and js/console.log to output things differently.

fsd18:04:37

is there any js/Date build in function in clojurescript that converts as following #inst “2021-04-21T00:00:00.000-00:00”

p-himik18:04:22

I don't understand what you mean. You need some function to convert X to Y. What's (type X)? What's (type Y)? Just in case, here's my REPL:

cljs.user=> (js/Date.)
#inst "2021-04-20T18:58:34.940-00:00"

raspasov20:04:37

If you’re doing anything more than a trivial thing around dates, consider using a library.

raspasov20:04:21

Works the same both in CLJ and CLJS, which is great.