Merge branch 'next' of https://github.com/lhorie/mithril.js into strict-mode

This commit is contained in:
Isiah Meadows 2017-03-03 18:27:24 -05:00
commit d4a0417dde
7 changed files with 336 additions and 205 deletions

View file

@ -655,6 +655,149 @@ o.spec("component", function() {
o(vnode.dom).notEquals(updated.dom)
})
o("lifecycle timing megatest (for a single component)", function() {
var methods = {
view: o.spy(function() {
return ""
})
}
var attrs = {}
var hooks = [
"oninit", "oncreate", "onbeforeupdate",
"onupdate", "onbeforeremove", "onremove"
]
hooks.forEach(function(hook) {
// the `attrs` hooks are called before the component ones
attrs[hook] = o.spy(function() {
o(attrs[hook].callCount).equals(methods[hook].callCount + 1)
})
methods[hook] = o.spy(function() {
o(attrs[hook].callCount).equals(methods[hook].callCount)
})
})
var component = createComponent(methods)
o(methods.view.callCount).equals(0)
o(methods.oninit.callCount).equals(0)
o(methods.oncreate.callCount).equals(0)
o(methods.onbeforeupdate.callCount).equals(0)
o(methods.onupdate.callCount).equals(0)
o(methods.onbeforeremove.callCount).equals(0)
o(methods.onremove.callCount).equals(0)
hooks.forEach(function(hook) {
o(attrs[hook].callCount).equals(methods[hook].callCount)(hook)
})
render(root, [{tag: component, attrs: attrs}])
o(methods.view.callCount).equals(1)
o(methods.oninit.callCount).equals(1)
o(methods.oncreate.callCount).equals(1)
o(methods.onbeforeupdate.callCount).equals(0)
o(methods.onupdate.callCount).equals(0)
o(methods.onbeforeremove.callCount).equals(0)
o(methods.onremove.callCount).equals(0)
hooks.forEach(function(hook) {
o(attrs[hook].callCount).equals(methods[hook].callCount)(hook)
})
render(root, [{tag: component, attrs: attrs}])
o(methods.view.callCount).equals(2)
o(methods.oninit.callCount).equals(1)
o(methods.oncreate.callCount).equals(1)
o(methods.onbeforeupdate.callCount).equals(1)
o(methods.onupdate.callCount).equals(1)
o(methods.onbeforeremove.callCount).equals(0)
o(methods.onremove.callCount).equals(0)
hooks.forEach(function(hook) {
o(attrs[hook].callCount).equals(methods[hook].callCount)(hook)
})
render(root, [])
o(methods.view.callCount).equals(2)
o(methods.oninit.callCount).equals(1)
o(methods.oncreate.callCount).equals(1)
o(methods.onbeforeupdate.callCount).equals(1)
o(methods.onupdate.callCount).equals(1)
o(methods.onbeforeremove.callCount).equals(1)
o(methods.onremove.callCount).equals(1)
hooks.forEach(function(hook) {
o(attrs[hook].callCount).equals(methods[hook].callCount)(hook)
})
})
o("hook state and arguments validation", function(){
var methods = {
view: o.spy(function(vnode) {
o(this).equals(vnode.state)
return ""
})
}
var attrs = {}
var hooks = [
"oninit", "oncreate", "onbeforeupdate",
"onupdate", "onbeforeremove", "onremove"
]
hooks.forEach(function(hook) {
attrs[hook] = o.spy(function(vnode){
o(this).equals(vnode.state)(hook)
})
methods[hook] = o.spy(function(vnode){
o(this).equals(vnode.state)
})
})
var component = createComponent(methods)
render(root, [{tag: component, attrs: attrs}])
render(root, [{tag: component, attrs: attrs}])
render(root, [])
hooks.forEach(function(hook) {
o(attrs[hook].this).equals(methods.view.this)(hook)
o(methods[hook].this).equals(methods.view.this)(hook)
})
o(methods.view.args.length).equals(1)
o(methods.oninit.args.length).equals(1)
o(methods.oncreate.args.length).equals(1)
o(methods.onbeforeupdate.args.length).equals(2)
o(methods.onupdate.args.length).equals(1)
o(methods.onbeforeremove.args.length).equals(1)
o(methods.onremove.args.length).equals(1)
hooks.forEach(function(hook) {
o(methods[hook].args.length).equals(attrs[hook].args.length)(hook)
})
})
o("recycled components get a fresh state", function() {
var step = 0
var firstState
var view = o.spy(function(vnode) {
if (step === 0) {
firstState = vnode.state
} else {
o(vnode.state).notEquals(firstState)
}
return {tag: "div"}
})
var component = createComponent({view: view})
render(root, [{tag: "div", children: [{tag: component, key: 1}]}])
var child = root.firstChild.firstChild
render(root, [])
step = 1
render(root, [{tag: "div", children: [{tag: component, key: 1}]}])
o(child).equals(root.firstChild.firstChild)
o(view.callCount).equals(2)
})
})
o.spec("state", function() {
o("initializes state", function() {
@ -673,7 +816,7 @@ o.spec("component", function() {
o(vnode.state.data).equals(data)
}
})
o('state "copy" is shallow', function() {
o("state proxies to the component object/prototype", function() {
var body = {a: 1}
var data = [body]
var component = createComponent(createComponent({
@ -695,10 +838,9 @@ o.spec("component", function() {
})
})
o.spec("Tests specific to certain component kinds", function() {
o.spec("POJO state", function() {
o("copies state", function() {
var data = {a: 1}
o.spec("state", function() {
o("POJO", function() {
var data = {}
var component = {
data: data,
oninit: init,
@ -717,142 +859,50 @@ o.spec("component", function() {
o(vnode.state.x).equals(1)
}
})
})
o("Constructible", function() {
var oninit = o.spy()
var component = o.spy(function(vnode){
o(vnode.state).equals(null)
o(oninit.callCount).equals(0)
})
var view = o.spy(function(){
o(this instanceof component).equals(true)
return ""
})
component.prototype.view = view
component.prototype.oninit = oninit
o("Classes can be used as components", function() {
function MyComponent(vnode){
o(vnode.state).equals(null)
}
var proto = MyComponent.prototype
render(root, [{tag: component, attrs: {oninit: oninit}}])
render(root, [{tag: component, attrs: {oninit: oninit}}])
render(root, [])
var context
proto.oninit = o.spy(function(vnode) {
o(this).equals(vnode.state)
context = this
o(component.callCount).equals(1)
o(oninit.callCount).equals(2)
o(view.callCount).equals(2)
})
proto.oncreate = o.spy()
proto.onbeforeupdate = o.spy()
proto.onupdate = o.spy()
proto.onbeforeremove = o.spy()
proto.onremove = o.spy()
proto.view = o.spy(function() {
return ""
o("Closure", function() {
var state
var oninit = o.spy()
var view = o.spy(function() {
o(this).equals(state)
return ""
})
var component = o.spy(function(vnode) {
o(vnode.state).equals(null)
o(oninit.callCount).equals(0)
return state = {
view: view
}
})
render(root, [{tag: component, attrs: {oninit: oninit}}])
render(root, [{tag: component, attrs: {oninit: oninit}}])
render(root, [])
o(component.callCount).equals(1)
o(oninit.callCount).equals(1)
o(view.callCount).equals(2)
})
render(root, [{tag: MyComponent}])
o(context instanceof MyComponent).equals(true)
o(proto.view.callCount).equals(1)
o(proto.oncreate.callCount).equals(1)
o(proto.onbeforeupdate.callCount).equals(0)
o(proto.onupdate.callCount).equals(0)
o(proto.onbeforeremove.callCount).equals(0)
o(proto.onremove.callCount).equals(0)
render(root, [{tag: MyComponent}])
o(proto.view.callCount).equals(2)
o(proto.oncreate.callCount).equals(1)
o(proto.onbeforeupdate.callCount).equals(1)
o(proto.onupdate.callCount).equals(1)
o(proto.onbeforeremove.callCount).equals(0)
o(proto.onremove.callCount).equals(0)
render(root, [])
o(proto.view.callCount).equals(2)
o(proto.oncreate.callCount).equals(1)
o(proto.onbeforeupdate.callCount).equals(1)
o(proto.onupdate.callCount).equals(1)
o(proto.onbeforeremove.callCount).equals(1)
o(proto.onremove.callCount).equals(1)
o(proto.oninit.this).equals(context)
o(proto.view.this).equals(context)
o(proto.oncreate.this).equals(context)
o(proto.onbeforeupdate.this).equals(context)
o(proto.onupdate.this).equals(context)
o(proto.onbeforeremove.this).equals(context)
o(proto.onremove.this).equals(context)
o(proto.oninit.args.length).equals(1)
o(proto.view.args.length).equals(1)
o(proto.oncreate.args.length).equals(1)
o(proto.onbeforeupdate.args.length).equals(2)
o(proto.onupdate.args.length).equals(1)
o(proto.onbeforeremove.args.length).equals(1)
o(proto.onremove.args.length).equals(1)
})
o("Closure functions can be used as components", function() {
var state, context
function component(vnode) {
o(vnode.state).equals(null)
return state = {
oninit: o.spy(function(vnode) {
o(this).equals(vnode.state)
context = this
}),
oncreate: o.spy(),
onbeforeupdate: o.spy(),
onupdate: o.spy(),
onbeforeremove: o.spy(),
onremove: o.spy(),
view: o.spy(function() {
return ""
})
}
}
render(root, [{tag: component}])
o(state).equals(context)
o(state.oninit.callCount).equals(1)
o(state.view.callCount).equals(1)
o(state.oncreate.callCount).equals(1)
o(state.onbeforeupdate.callCount).equals(0)
o(state.onupdate.callCount).equals(0)
o(state.onbeforeremove.callCount).equals(0)
o(state.onremove.callCount).equals(0)
render(root, [{tag: component}])
o(state.oninit.callCount).equals(1)
o(state.view.callCount).equals(2)
o(state.oncreate.callCount).equals(1)
o(state.onbeforeupdate.callCount).equals(1)
o(state.onupdate.callCount).equals(1)
o(state.onbeforeremove.callCount).equals(0)
o(state.onremove.callCount).equals(0)
render(root, [])
o(state.oninit.callCount).equals(1)
o(state.view.callCount).equals(2)
o(state.oncreate.callCount).equals(1)
o(state.onbeforeupdate.callCount).equals(1)
o(state.onupdate.callCount).equals(1)
o(state.onbeforeremove.callCount).equals(1)
o(state.onremove.callCount).equals(1)
o(state.oninit.this).equals(state)
o(state.view.this).equals(state)
o(state.oncreate.this).equals(state)
o(state.onbeforeupdate.this).equals(state)
o(state.onupdate.this).equals(state)
o(state.onbeforeremove.this).equals(state)
o(state.onremove.this).equals(state)
o(state.oninit.args.length).equals(1)
o(state.view.args.length).equals(1)
o(state.oncreate.args.length).equals(1)
o(state.onbeforeupdate.args.length).equals(2)
o(state.onupdate.args.length).equals(1)
o(state.onbeforeremove.args.length).equals(1)
o(state.onremove.args.length).equals(1)
})
})
})

View file

@ -119,6 +119,21 @@ o.spec("onbeforeupdate", function() {
o(count).equals(1)
})
o("doesn't fire on recycled nodes", function() {
var onbeforeupdate = o.spy()
var vnodes = [{tag: "div", key: 1}]
var temp = []
var updated = [{tag: "div", key: 1, attrs: {onbeforeupdate: onbeforeupdate}}]
render(root, vnodes)
render(root, temp)
render(root, updated)
o(vnodes[0].dom).equals(updated[0].dom)
o(updated[0].dom.nodeName).equals("DIV")
o(onbeforeupdate.callCount).equals(0)
})
components.forEach(function(cmp){
o.spec(cmp.kind, function(){
var createComponent = cmp.create