Hyperapp: The spinoffs: Hyperapp 🎒

Created on 9 Feb 2017  Β·  17Comments  Β·  Source: jorgebucaran/hyperapp

Just fyi for anyone interested, the funness of this repo inspired me to make something similar. The differences being that the view rendering is decoupled, so you can plug in any render function, and it's all es5 β€” no build tools required. Thanks @jbucaran for making a cool thing.

https://github.com/nichoth/h-app

Discussion

All 17 comments

@nichoth By the way, HyperApp is also all ES5, no build tools required. We're using rollup to generate the UMD bundles that users grab when embedding via an <script> tag, but that's another thing.

EDIT: BTW, I tried to see what's in https://github.com/nichoth/model-message, but it's 404ing.

Oh! I didn't realize that about es version β€” that's important. I think the titles of the PRs threw me off πŸ˜„ . btw, I fixed the package.json so it has the right repo

The es6 was a false alarm :). The thing I would really like to solve is finding an easy way to run automated tests on browser applications. I'm not an expert with elm β€” I don't know how tests work β€” but compile time type checking would catch a majority of the bugs I have day to day. JS is in a weird place where unit tests often fill the role of type checking. My interest in decoupling model & view is because I'm trying to find a way to easily write automated app tests. One idea is to declare all the events as data (the .msg object), so we can easily test the model against the events with mock input. I'm just kind of thinking out loud here, would be interested in hearing what other's experiences are like.

Do you mean HyperApp tests or your app's tests?

@jbucaran I mean my app's tests (tests of the application code, not the dependencies). So my-app would consume hyperapp, for example, and we need to test my-app, which is not a dependency of anything. Dependencies are easier because the interface is not a UI via the dom.

It is totally possible to write automated tests for an application… but the amount of (tedious) labor required can be prohibitive.

My interest in decoupling model & view

ELI5, what do you mean by decoupling in this context and how does it apply to HyperApp?

I ask, because I see that in your Counter example, you've split the application into counter.js and app.js, but you can do the same in HyperApp from day one.

  • app.js
const { app } = require("hyperapp")
const Counter = require("./counter")

app(Counter)
  • counter.js
const { html } = require("hyperapp")

module.exports = options => ({
    model: options.initialCount,
    update: {
        add: model => model + 1,
        sub: model => model - 1
    },
    view: (model, msg) => html`
        <div>
            <button onclick=${msg.add}>+</button>
            <h1>${model}</h1>
            <button onclick=${msg.sub} disabled=${model <= 0}>-</button>
        </div>`
})

It is decoupled because we provide defaults that can be overridden. The render function is curried before the view state. So the default is

require('h-app')(someModel)
// this starts a loop where the dom triggers re-rendering itself
// after the model changes

But the files are factored in such a way that you can do

var App = require('h-app/app')
var app = App(mySpecialRenderer)

app(myModel)

function mySpecialRenderer (tree) {
    // ...
    // tree is the return value from myModel.view
}

This necessitates an observable interface for the app state (via observ-struct in my lib), so the renderer can subscribe to changes, whereas in hyperapp the render function is baked in.

In the same spirit, you can do require('h-app/model), which is a function that returns a writable and an observable interface (a stream, basically) β€” writable via model.msg, and observable via model.state. So in relation to tests, this means we can do

var model = require('h-app/model')(MyModel)
Object.keys(model.msg).forEach(function (k) {
    model.msg[k](mockInput[k])
})

model.state(function onChange (state) {
    assert(/*...*/)
})

The next experiment I'm doing is factoring everything as a pull-stream, since everything is basically a stream anyway :)

What you want to decouple the render function?

If that's so, then I can understand where you're coming from?

I totally understand the value of modules, decoupling, etc., but I've been the victim of too much decoupling, too much options, too much "build your own framework" kind of thing too.

I think this is worth exploring, so please share your findings back here. πŸ™

Yes, basically. The end goal is being able to write tests, the hypothesis is that we can expose the app state without using the Dom.

I have to go bicycle in the rain to the coop before it closes, but will almost certainly revisit these thoughts. Would be interested in hearing other people experiences pain points, and goals with browser applications.

@nichoth The end goal is being able to write tests

This implies you can't write tests now, which is not correct.

I'm also considering separating the render function in order support stuff like Electron, kinda like it went with Snabbdom and I think I can split things up and still stay under 1kb, since we're using rollup.

@jbucaran what would your application tests look like in hyperapp?

Would probably use jest like we are with HyperApp.

I'm juggling a lot of things at once, so I don't have a ready repo to share with you. Perhaps you could contribute w/ one?

yes It's one of my main goals at the moment :) . Where I'm at is that writing tests is 100% possible, but often too laborious to be worthwhile/feasible, and also not obvious as to what/how to test. Part of this varies depending on what kind of experiences we are coming from with applications and teams of developers.

I do have some beef with frameworks like jest, in that it seems too complicated, and I don't want to be dependent on something that needs a large corporation maintaining it because it is so vast. Ideally I would like to not use any framework that takes more than a weekend to read and understand.

I was unsure about jest at first, for pretty much the same reasons, but after being introduced to it by @maraisr, the more I use it, the more enjoyable I find it. I also wanted to try something new and the tooling is minimal.

In the future, when we make render document agnostic, πŸ€” I may switch back to tape.

But I have to admit I'm pretty happy with jest so far.

screen shot 2017-02-09 at 15 36 05

Hi @nichoth! How's h-app coming along? πŸ‘‹

I think we can close here, since this is not really an issue with hyperapp.

Yeah sounds good. I have been experimenting more with different APIs. I made a traditional todomvc with pull-streams, which I like. https://github.com/nichoth/todomvc-pull-stream . The important part is pull-stream-model, which provides some structure/convention.

So main thing was to find a nice way of writing tests, which I still haven't gotten around to, but will soon. In this case we are adding tests to an existing large app that doesn't have any.

The idea is that all the app events we care about are serialized to static objects that can be logged. So in the todo app, https://github.com/nichoth/todomvc-pull-stream/blob/master/app.js#L33 , you can log events at any point in the pipeline. We want to log them after any async operations, since that introduces randomness. In the example it's just the browser console, but in real life we have a small utility that sends events to a websocket server that logs them. Then you can replay the log in an automated way and check that the events are valid and the resulting state is ok.

Sounds like mad science :) πŸ‘

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dmitrykurmanov picture dmitrykurmanov  Β·  4Comments

joshuahiggins picture joshuahiggins  Β·  4Comments

jorgebucaran picture jorgebucaran  Β·  4Comments

Mytrill picture Mytrill  Β·  4Comments

jorgebucaran picture jorgebucaran  Β·  3Comments