This is more a talk than an issue, but :
As it could be "easy" to test functional and API endpoint, this is not easy to really test a SPA app.
So, this is an easy question to ask, but not easy to answer : How would you (unit) test an hyper'app ?
The app function do not return anything to test, here is not API to call action, to get the view or to set the state.
I think you could write a mixin that exposes those things to the outside world.
The action and render events give the state before & after an action is fired.
Instead of console logging, you could copy the values to a global variable, or however you see fit.
Similarly, you could copy a reference to the app's actions to an outside variable and call them at will.
Here's a quick example on Webpackbin: https://www.webpackbin.com/bins/-KneKtpfy77QkrqfCI83
It seems functional for me, at least in Chrome. I had to wrap the "test" action calls in a setTimeout to allow hyperapp to initialize.
Mixin code:
export default (testObject) => (app) => ({
events: {
loaded: (state, actions) => {
testObject.actions = actions
},
update: (state) => {}, //console.log(state), //before action
render: (state) => {
testObject.state = state
}
}
})
App code
import {app, h} from 'hyperapp'
import TestMixin from './test-mixin'
let testObject = {}
app({
state: 1,
mixins: [TestMixin(testObject)],
actions: {inc: (state) => state+1 },
view: (state, actions) => h('div', null, [
h('H1', null, 'Hello, the state is ' + state),
h('button', {onclick: actions.inc}, 'Increment')
])
})
setTimeout(() => {
testObject.actions.inc()
console.log(testObject.state) // 2
testObject.actions.inc()
console.log(testObject.state) // 3
testObject.actions.inc()
console.log(testObject.state) // 4
testObject.actions.inc()
console.log(testObject.state) // 5
testObject.actions.inc()
console.log(testObject.state) // 6
},100)
Sound interesting, but if the app already have events registered, the mixin ones will be overridden, right ?
No. Methods in mixin won't be overridden until you use some kind of external api like jQuery's .extend(). I've seen this behaviour in React earlier!
or you could use a mocker to test specific use cases!
With hyperapp you are just writing pure functions. They are really easy to test. I don't think it makes sense to do anything with hyperapp to unit test you app. Most of the examples show putting view, actions, etc in an anonymous object in the call app(). In a real app you wouldn't really want to do this. I would do something like this (keep in mind I don't have any experience with hyperapp)
// app.js
import { h } from 'hyperapp'
export const state = 1
export const actions = {
inc: (state) => state + 1
}
export const view = (state, actions) => {
return h('button', { onclick: actions.inc }, state)
}
// app.test.js
import { state, actions, view } from './app'
test('state starts at 1', () => {
expect(state).toBe(1)
})
test('inc action returns state + 1', () => {
expect(actions.inc(1)).toBe(2)
expect(actions.inc(46)).toBe(47)
})
test('view matches snapshots', () => {
expect(view(2, actions)).toMatchSnapshot()
expect(view(47, actions)).toMatchSnapshot()
})
// init.js
import { app } from 'hyperapp'
import { state, view, actions } from './app'
app({ root: document.getElementById('app'), state, view, actions })
@brentburg You are the one :+1:
Thanks @brentburg, that sounds about right.
@Swizz Is it okay to close here?
Yes. Thank you, everybody.
Just FYI: you can test Hyperapp components and entire apps using Cypress test runner using this little helper module: https://github.com/bahmutov/cypress-hyperapp-unit-test
Most helpful comment
With hyperapp you are just writing pure functions. They are really easy to test. I don't think it makes sense to do anything with hyperapp to unit test you app. Most of the examples show putting view, actions, etc in an anonymous object in the call
app(). In a real app you wouldn't really want to do this. I would do something like this (keep in mind I don't have any experience with hyperapp)