Merge pull request #115 from bendrucker/unified-tests
Run all the tests via Electron
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
coverage.json
|
||||
tmp/
|
||||
npm-debug.log*
|
||||
.DS_Store
|
||||
|
||||
@@ -3,6 +3,13 @@ node_js:
|
||||
- '6'
|
||||
sudo: false
|
||||
language: node_js
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- xvfb
|
||||
before_install:
|
||||
- export DISPLAY=':99.0'
|
||||
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
|
||||
script: npm run test:cov
|
||||
after_script: npm i -g codecov.io && cat ./coverage/lcov.info | codecov
|
||||
env:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const history = require('sheet-router/history')
|
||||
const sheetRouter = require('sheet-router')
|
||||
const document = require('global/document')
|
||||
const onReady = require('document-ready')
|
||||
const href = require('sheet-router/href')
|
||||
const hash = require('sheet-router/hash')
|
||||
const hashMatch = require('hash-match')
|
||||
@@ -40,7 +41,7 @@ function choo (opts) {
|
||||
const state = _store.state({ state: serverState })
|
||||
const router = createRouter(_defaultRoute, _routes, createSend)
|
||||
const tree = router(route, state)
|
||||
return tree.toString()
|
||||
return tree.outerHTML || tree.toString()
|
||||
|
||||
function createSend () {
|
||||
return function send () {
|
||||
@@ -61,14 +62,14 @@ function choo (opts) {
|
||||
_store.model(appInit(startOpts))
|
||||
const createSend = _store.start(startOpts)
|
||||
_router = start._router = createRouter(_defaultRoute, _routes, createSend)
|
||||
const state = _store.state()
|
||||
const state = _store.state({state: {}})
|
||||
|
||||
if (!selector) {
|
||||
const tree = _router(state.location.pathname, state)
|
||||
_rootNode = tree
|
||||
return tree
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', function (event) {
|
||||
onReady(function onReady () {
|
||||
const oldTree = document.querySelector(selector)
|
||||
assert.ok(oldTree, 'could not query selector: ' + selector)
|
||||
const newTree = _router(state.location.pathname, state)
|
||||
@@ -81,7 +82,6 @@ function choo (opts) {
|
||||
// (obj, obj, obj, str, fn) -> null
|
||||
function render (data, state, prev, name, createSend) {
|
||||
if (opts.onState) opts.onState(data, state, prev, name, createSend)
|
||||
if (state === prev) return
|
||||
|
||||
const newTree = _router(state.location.pathname, state, prev)
|
||||
_rootNode = yo.update(_rootNode, newTree)
|
||||
|
||||
+12
-3
@@ -5,13 +5,13 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"deps": "dependency-check . && dependency-check . --extra --no-dev -i xhr",
|
||||
"test:electron": "browserify tests/**/*.js -t es2020 -p proxyquire-universal | tape-run",
|
||||
"test:cov": "browserify tests/**/*.js -t es2020 -p proxyquire-universal -p tape-istanbul/plugin | tape-run | tape-istanbul && istanbul report",
|
||||
"test:server": "standard && npm run deps && NODE_ENV=test node tests/server/*",
|
||||
"test:server:cov": "standard && npm run deps && NODE_ENV=test istanbul cover tests/server/*",
|
||||
"test:browser": "standard && npm run deps && NODE_ENV=test zuul tests/browser/*",
|
||||
"test:browser:local": "standard && npm run deps && NODE_ENV=test zuul --local 8080 -- tests/browser/*",
|
||||
"test:cov": "npm run test:server:cov",
|
||||
"preversion": "if [ ! -z $SKIP_TEST ]; then npm run test:browser; fi",
|
||||
"test": "npm run test:server"
|
||||
"test": "npm run test:electron"
|
||||
},
|
||||
"repository": "yoshuawuyts/choo",
|
||||
"keywords": [
|
||||
@@ -25,6 +25,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"barracks": "^7.0.3",
|
||||
"document-ready": "~1.0.1",
|
||||
"global": "^4.3.0",
|
||||
"hash-match": "^1.0.2",
|
||||
"sheet-router": "^3.1.0",
|
||||
@@ -33,20 +34,28 @@
|
||||
"yo-yo": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"append-child": "~1.0.0",
|
||||
"bankai": "^2.0.2",
|
||||
"browserify": "^13.0.1",
|
||||
"browserify-istanbul": "^2.0.0",
|
||||
"bundle-collapser": "^1.2.1",
|
||||
"dependency-check": "^2.5.1",
|
||||
"es2020": "^1.0.1",
|
||||
"geval": "~2.1.1",
|
||||
"insert-css": "^0.2.0",
|
||||
"istanbul": "^0.4.4",
|
||||
"karma-sauce-launcher": "^1.0.0",
|
||||
"min-document": "~2.18.0",
|
||||
"proxyquire": "~1.7.10",
|
||||
"proxyquire-universal": "~1.0.8",
|
||||
"proxyquireify": "~3.2.0",
|
||||
"server-router": "^2.1.0",
|
||||
"sheetify": "^5.0.0",
|
||||
"standard": "^7.1.0",
|
||||
"tachyons": "^4.0.0-beta.19",
|
||||
"tape": "^4.5.1",
|
||||
"tape-istanbul": "~1.0.2",
|
||||
"tape-run": "~2.1.4",
|
||||
"yo-yoify": "^3.1.0",
|
||||
"zuul": "toddself/zuul"
|
||||
}
|
||||
|
||||
+51
-52
@@ -1,61 +1,60 @@
|
||||
const tape = require('tape')
|
||||
const test = require('tape')
|
||||
const append = require('append-child')
|
||||
const choo = require('../../')
|
||||
const view = require('../../html')
|
||||
|
||||
tape('should render on the client', function (t) {
|
||||
t.test('state should not be mutable', function (t) {
|
||||
t.plan(4)
|
||||
test('state is immutable', function (t) {
|
||||
t.plan(4)
|
||||
|
||||
const app = choo()
|
||||
const state = {
|
||||
foo: 'baz',
|
||||
beep: 'boop'
|
||||
}
|
||||
const app = choo()
|
||||
const state = {
|
||||
foo: 'baz',
|
||||
beep: 'boop'
|
||||
}
|
||||
|
||||
app.model({
|
||||
state: state,
|
||||
namespace: 'test',
|
||||
reducers: {
|
||||
'no-reducer-mutate': (action, state) => {
|
||||
return {}
|
||||
},
|
||||
'mutate-on-return': (action, state) => {
|
||||
delete action.type
|
||||
return action
|
||||
}
|
||||
app.model({
|
||||
state: state,
|
||||
namespace: 'test',
|
||||
reducers: {
|
||||
'no-reducer-mutate': (action, state) => {
|
||||
return {}
|
||||
},
|
||||
effects: {
|
||||
'triggers-reducers': (action, state, send) => {
|
||||
send('test:mutate-on-return', {beep: 'barp'})
|
||||
}
|
||||
'mutate-on-return': (action, state) => {
|
||||
delete action.type
|
||||
return action
|
||||
}
|
||||
})
|
||||
|
||||
let loop = -1
|
||||
|
||||
const asserts = [
|
||||
(state) => t.deepEqual(state, {foo: 'baz', beep: 'boop'}, 'intial state'),
|
||||
(state) => t.deepEqual(state, {foo: 'baz', beep: 'boop'}, 'no change in state'),
|
||||
(state) => t.deepEqual(state, {foo: 'oof', beep: 'boop'}, 'change in state from reducer'),
|
||||
(state) => t.deepEqual(state, {foo: 'oof', beep: 'barp'}, 'change in state from effect')
|
||||
]
|
||||
|
||||
const triggers = [
|
||||
(send) => send('test:no-reducer-mutate'),
|
||||
(send) => send('test:mutate-on-return', {foo: 'oof'}),
|
||||
(send) => send('test:triggers-reducers')
|
||||
]
|
||||
|
||||
app.router((route) => [
|
||||
route('/', function (params, state, send) {
|
||||
++loop
|
||||
asserts[loop] && asserts[loop](state.test)
|
||||
setTimeout(() => triggers[loop] && triggers[loop](send), 5)
|
||||
return view`<div><span class="test">${state.foo}:${state.beep}</span></div>`
|
||||
})
|
||||
])
|
||||
|
||||
const tree = app.start()
|
||||
document.body.appendChild(tree)
|
||||
},
|
||||
effects: {
|
||||
'triggers-reducers': (action, state, send, done) => {
|
||||
send('test:mutate-on-return', {beep: 'barp'}, done)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let loop = -1
|
||||
|
||||
const asserts = [
|
||||
(state) => t.deepEqual(state, {foo: 'baz', beep: 'boop'}, 'intial state'),
|
||||
(state) => t.deepEqual(state, {foo: 'baz', beep: 'boop'}, 'no change in state'),
|
||||
(state) => t.deepEqual(state, {foo: 'oof', beep: 'boop'}, 'change in state from reducer'),
|
||||
(state) => t.deepEqual(state, {foo: 'oof', beep: 'barp'}, 'change in state from effect')
|
||||
]
|
||||
|
||||
const triggers = [
|
||||
(send) => send('test:no-reducer-mutate'),
|
||||
(send) => send('test:mutate-on-return', {foo: 'oof'}),
|
||||
(send) => send('test:triggers-reducers')
|
||||
]
|
||||
|
||||
app.router((route) => [
|
||||
route('/', function (state, prev, send) {
|
||||
++loop
|
||||
asserts[loop] && asserts[loop](state.test)
|
||||
setTimeout(() => triggers[loop] && triggers[loop](send), 5)
|
||||
return view`<div><span class="test">${state.foo}:${state.beep}</span></div>`
|
||||
})
|
||||
])
|
||||
|
||||
const tree = app.start()
|
||||
t.on('end', append(tree))
|
||||
})
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
const test = require('tape')
|
||||
const choo = require('../../')
|
||||
|
||||
test('freeze (default)', function (t) {
|
||||
t.plan(2)
|
||||
const app = choo()
|
||||
|
||||
app.model({
|
||||
state: {
|
||||
foo: 'bar'
|
||||
}
|
||||
})
|
||||
|
||||
app.router((route) => [
|
||||
route('/', function (state, prev, send) {
|
||||
state.foo = ''
|
||||
t.equal(state.foo, 'bar', 'cannot modify property')
|
||||
state.bar = 'baz'
|
||||
t.equal(state.bar, undefined, 'cannot add property')
|
||||
})
|
||||
])
|
||||
|
||||
app.start()
|
||||
})
|
||||
|
||||
test('noFreeze', function (t) {
|
||||
t.plan(2)
|
||||
const app = choo({noFreeze: true})
|
||||
|
||||
app.model({
|
||||
state: {
|
||||
foo: 'bar'
|
||||
}
|
||||
})
|
||||
|
||||
app.router((route) => [
|
||||
route('/', function (state, prev, send) {
|
||||
state.foo = ''
|
||||
t.equal(state.foo, '', 'can modify property')
|
||||
state.bar = 'baz'
|
||||
t.equal(state.bar, 'baz', 'can add property')
|
||||
})
|
||||
])
|
||||
|
||||
app.start()
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
const test = require('tape')
|
||||
const append = require('append-child')
|
||||
const choo = require('../../')
|
||||
const view = require('../../html')
|
||||
|
||||
test('hooks', function (t) {
|
||||
t.plan(9)
|
||||
|
||||
const app = choo({
|
||||
onError: function (err) {
|
||||
t.equal(err.message, 'effect error', 'onError: receives err')
|
||||
},
|
||||
onAction: function (action, state, name, caller, createSend) {
|
||||
if (name === 'explodes') return
|
||||
t.deepEqual(action, {foo: 'bar'}, 'onAction: action data')
|
||||
t.equal(state.clicks, 0, 'onAction: current state: 0 clicks')
|
||||
t.equal(name, 'click', 'onAction: action name')
|
||||
t.equal(caller, '/', 'onAction: caller name')
|
||||
t.equal(typeof createSend, 'function', 'onAction: createSend fn')
|
||||
},
|
||||
onState: function (action, state, prev, createSend) {
|
||||
t.deepEqual(action, {foo: 'bar'}, 'onState: action data')
|
||||
t.deepEqual(state.clicks, 1, 'onState: new state: 1 clicks')
|
||||
t.deepEqual(prev.clicks, 0, 'onState: prev state: 0 clicks')
|
||||
}
|
||||
})
|
||||
|
||||
app.model({
|
||||
state: {
|
||||
clicks: 0
|
||||
},
|
||||
reducers: {
|
||||
click: (action, state) => ({clicks: state.clicks + 1})
|
||||
},
|
||||
effects: {
|
||||
explodes: (action, state, send, done) => {
|
||||
setTimeout(() => done(new Error('effect error')), 5)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var sent = false
|
||||
app.router((route) => [
|
||||
route('/', function (state, prev, send) {
|
||||
if (!sent) {
|
||||
send('click', {foo: 'bar'})
|
||||
send('explodes')
|
||||
}
|
||||
sent = true
|
||||
return view`<span></span>`
|
||||
})
|
||||
])
|
||||
|
||||
const tree = app.start()
|
||||
t.on('end', append(tree))
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
const test = require('tape')
|
||||
const onReady = require('document-ready')
|
||||
const append = require('append-child')
|
||||
const choo = require('../../')
|
||||
const view = require('../../html')
|
||||
|
||||
test('rehydration', function (t) {
|
||||
t.plan(2)
|
||||
|
||||
const app = choo()
|
||||
|
||||
app.router((route) => [
|
||||
route('/', function (state, prev, send) {
|
||||
return view`<div id="app-root" onclick=${() => send('test')}>Hello world!</span>`
|
||||
})
|
||||
])
|
||||
|
||||
var node = document.createElement('div')
|
||||
node.innerHTML = app.toString('/')
|
||||
node = node.childNodes[0]
|
||||
t.on('end', append(node))
|
||||
|
||||
app.start('#app-root')
|
||||
|
||||
onReady(function () {
|
||||
t.equal(node.innerHTML, 'Hello world!', 'same content')
|
||||
t.equal(typeof node.onclick, 'function', 'attaches dom listeners')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,166 @@
|
||||
const test = require('tape')
|
||||
const Event = require('geval/event')
|
||||
const proxyquire = require('proxyquire')
|
||||
const append = require('append-child')
|
||||
const view = require('../../html')
|
||||
|
||||
test('routing', function (t) {
|
||||
t.test('history', function (t) {
|
||||
t.plan(3)
|
||||
|
||||
const history = Event()
|
||||
const choo = proxyquire('../..', {
|
||||
'sheet-router/history': history.listen
|
||||
})
|
||||
|
||||
const app = choo()
|
||||
|
||||
app.model({
|
||||
state: {
|
||||
user: null
|
||||
},
|
||||
reducers: {
|
||||
set: (action, state) => ({user: action.id})
|
||||
},
|
||||
effects: {
|
||||
open: function (action, state, send, done) {
|
||||
t.deepEqual(action, {id: 1})
|
||||
send('set', {id: 1}, function (err) {
|
||||
if (err) return done(err)
|
||||
history.broadcast('https://foo.com/users/1')
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.router('/users', (route) => [
|
||||
route('/users', parentView, [
|
||||
route('/:user', childView)
|
||||
])
|
||||
])
|
||||
|
||||
const tree = app.start()
|
||||
t.on('end', append(tree))
|
||||
|
||||
t.equal(tree.innerHTML.trim(), 'Open')
|
||||
tree.onclick()
|
||||
|
||||
function parentView (state, prev, send) {
|
||||
return view`
|
||||
<button onclick=${() => send('open', {id: 1})}>
|
||||
Open
|
||||
</button>
|
||||
`
|
||||
}
|
||||
|
||||
function childView (state, prev, send) {
|
||||
t.equal(state.user, 1)
|
||||
return view`<button>${state.user}</button>`
|
||||
}
|
||||
})
|
||||
|
||||
t.test('hash', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const hash = Event()
|
||||
const choo = proxyquire('../..', {
|
||||
'sheet-router/hash': hash.listen
|
||||
})
|
||||
|
||||
const app = choo()
|
||||
|
||||
app.model({
|
||||
state: {
|
||||
user: null
|
||||
},
|
||||
reducers: {
|
||||
set: (action, state) => ({user: action.id})
|
||||
},
|
||||
effects: {
|
||||
open: function (action, state, send, done) {
|
||||
send('set', {id: 1}, function (err) {
|
||||
if (err) return done(err)
|
||||
hash.broadcast('#users/1')
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.router('/users', (route) => [
|
||||
route('/users', parentView, [
|
||||
route('/:user', childView)
|
||||
])
|
||||
])
|
||||
|
||||
const tree = app.start({hash: true})
|
||||
t.on('end', append(tree))
|
||||
|
||||
tree.onclick()
|
||||
|
||||
function parentView (state, prev, send) {
|
||||
return view`
|
||||
<button onclick=${() => send('open', {id: 1})}>
|
||||
Open
|
||||
</button>
|
||||
`
|
||||
}
|
||||
|
||||
function childView (state, prev, send) {
|
||||
t.equal(state.user, 1)
|
||||
return view`<button>${state.user}</button>`
|
||||
}
|
||||
})
|
||||
|
||||
t.test('disabling history', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const choo = proxyquire('../..', {
|
||||
'sheet-router/history': () => t.fail('history listener attached')
|
||||
})
|
||||
|
||||
const app = choo()
|
||||
|
||||
app.router('/', (route) => [
|
||||
route('/', function () {
|
||||
t.pass('rendered')
|
||||
})
|
||||
])
|
||||
|
||||
app.start({history: false})
|
||||
})
|
||||
|
||||
t.test('disabling href', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const choo = proxyquire('../..', {
|
||||
'sheet-router/href': () => t.fail('href listener attached')
|
||||
})
|
||||
|
||||
const app = choo()
|
||||
|
||||
app.router('/', (route) => [
|
||||
route('/', function () {
|
||||
t.pass('rendered')
|
||||
})
|
||||
])
|
||||
|
||||
app.start({href: false})
|
||||
})
|
||||
|
||||
t.test('viewless nesting', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const choo = require('../..')
|
||||
const app = choo()
|
||||
|
||||
app.router('/users/123', (route) => [
|
||||
route('/users', [
|
||||
route('/:user', function (state) {
|
||||
t.deepEqual(state.params, {user: '123'})
|
||||
})
|
||||
])
|
||||
])
|
||||
|
||||
app.start()
|
||||
})
|
||||
})
|
||||
+22
-8
@@ -1,9 +1,10 @@
|
||||
const tape = require('tape')
|
||||
const test = require('tape')
|
||||
const minDocument = require('min-document')
|
||||
const choo = require('../../')
|
||||
const view = require('../../html')
|
||||
|
||||
tape('should render on the server', function (t) {
|
||||
t.test('should render a static response', function (t) {
|
||||
test('server', function (t) {
|
||||
t.test('renders a static html response', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const app = choo()
|
||||
@@ -16,7 +17,20 @@ tape('should render on the server', function (t) {
|
||||
t.equal(html, expected, 'strings are equal')
|
||||
})
|
||||
|
||||
t.test('should accept a state object', function (t) {
|
||||
t.test('renders without a real DOM', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const app = choo()
|
||||
app.router((route) => [
|
||||
route('/', () => minDocument.createElement('div'))
|
||||
])
|
||||
|
||||
const html = app.toString('/')
|
||||
const expected = '<div></div>'
|
||||
t.equal(html, expected, 'strings are equal')
|
||||
})
|
||||
|
||||
t.test('receives a state object', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const app = choo()
|
||||
@@ -31,7 +45,7 @@ tape('should render on the server', function (t) {
|
||||
t.equal(html, expected, 'strings are equal')
|
||||
})
|
||||
|
||||
t.test('should extend flat existing models', function (t) {
|
||||
t.test('extends flat existing models', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const app = choo()
|
||||
@@ -48,7 +62,7 @@ tape('should render on the server', function (t) {
|
||||
t.equal(html, expected, 'strings are equal')
|
||||
})
|
||||
|
||||
t.test('should extend namespaced existing models', function (t) {
|
||||
t.test('extends namespaced existing models', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const app = choo()
|
||||
@@ -75,7 +89,7 @@ tape('should render on the server', function (t) {
|
||||
t.equal(html, expected, 'strings are equal')
|
||||
})
|
||||
|
||||
t.test('should throw if called without route', function (t) {
|
||||
t.test('throws if called without route', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const app = choo()
|
||||
@@ -88,7 +102,7 @@ tape('should render on the server', function (t) {
|
||||
t.throws(app.toString.bind(null), /route must be a string/)
|
||||
})
|
||||
|
||||
t.test('should throw if calling send()', function (t) {
|
||||
t.test('throws when calling send()', function (t) {
|
||||
t.plan(1)
|
||||
|
||||
const app = choo()
|
||||
|
||||
Reference in New Issue
Block a user