Fix state initialization (#1652)

* Add test-utils/components.js and its tests

* Tests: group together tests with/without components

* Tests: factory => closure components

* Tests: add dummy forEach blocks around sections with components

* Tests: render/test-components tweaks

* Tests: Wrap some component definitions in `createComponent()` calls

These are the ones that would have been tedious to automate.
jscodeshift will handle the remaining ones

* Tests: wrap the rest of the components definitions

* Tests: enable the three kind of components in all related files but api/tests/test-route.js

* Add test-utils/components.js to index.html where needed

* Tests: Some more render/tests/test-component.js refactoring

* Tests: enable tests for #1638

* fix #1638

* Test: ensure that recycled components get a fresh state

* Tests: add a test for onbeforeupdate and recycled nodes

* Fix recycled components initialization
fix #1641
This commit is contained in:
Pierre-Yves Gérardy 2017-02-25 21:53:51 +01:00 committed by Isiah Meadows
parent 3786373b58
commit 60e8f307f1
15 changed files with 1615 additions and 1406 deletions

View file

@ -13,6 +13,7 @@
<script src="../../test-utils/pushStateMock.js"></script> <script src="../../test-utils/pushStateMock.js"></script>
<script src="../../test-utils/xhrMock.js"></script> <script src="../../test-utils/xhrMock.js"></script>
<script src="../../test-utils/browserMock.js"></script> <script src="../../test-utils/browserMock.js"></script>
<script src="../../test-utils/component.js"></script>
<script src="../../promise/promise.js"></script> <script src="../../promise/promise.js"></script>
<script src="../../render/vnode.js"></script> <script src="../../render/vnode.js"></script>

View file

@ -1,6 +1,7 @@
"use strict" "use strict"
var o = require("../../ospec/ospec") var o = require("../../ospec/ospec")
var components = require("../../test-utils/components")
var domMock = require("../../test-utils/domMock") var domMock = require("../../test-utils/domMock")
var m = require("../../render/hyperscript") var m = require("../../render/hyperscript")
@ -22,16 +23,6 @@ o.spec("mount", function() {
render = coreRenderer($window).render render = coreRenderer($window).render
}) })
o("throws on invalid `root` DOM node", function() {
var threw = false
try {
mount(null, {view: function() {}})
} catch (e) {
threw = true
}
o(threw).equals(true)
})
o("throws on invalid component", function() { o("throws on invalid component", function() {
var threw = false var threw = false
try { try {
@ -42,42 +33,36 @@ o.spec("mount", function() {
o(threw).equals(true) o(threw).equals(true)
}) })
o("renders into `root` (POJO component)", function() { components.forEach(function(cmp){
mount(root, { o.spec(cmp.kind, function(){
var createComponent = cmp.create
o("throws on invalid `root` DOM node", function() {
var threw = false
try {
mount(null, createComponent({view: function() {}}))
} catch (e) {
threw = true
}
o(threw).equals(true)
})
o("renders into `root`", function() {
mount(root, createComponent({
view : function() { view : function() {
return m("div") return m("div")
} }
}) }))
o(root.firstChild.nodeName).equals("DIV")
})
o("renders into `root` (class component)", function() {
function Cmp(){}
Cmp.prototype.view = function(){return m("div")}
mount(root, Cmp)
o(root.firstChild.nodeName).equals("DIV")
})
o("renders into `root` factory (factory component)", function() {
mount(root, function(){
return {
view : function() {
return m("div")
}
}
})
o(root.firstChild.nodeName).equals("DIV") o(root.firstChild.nodeName).equals("DIV")
}) })
o("mounting null unmounts", function() { o("mounting null unmounts", function() {
mount(root, { mount(root, createComponent({
view : function() { view : function() {
return m("div") return m("div")
} }
}) }))
mount(root, null) mount(root, null)
@ -92,7 +77,7 @@ o.spec("mount", function() {
e.initEvent("click", true, true) e.initEvent("click", true, true)
mount(root, { mount(root, createComponent({
view : function() { view : function() {
return m("div", { return m("div", {
oninit : oninit, oninit : oninit,
@ -100,7 +85,7 @@ o.spec("mount", function() {
onclick : onclick, onclick : onclick,
}) })
} }
}) }))
root.firstChild.dispatchEvent(e) root.firstChild.dispatchEvent(e)
@ -139,7 +124,7 @@ o.spec("mount", function() {
m("#child1") m("#child1")
]) ])
mount(root.childNodes[0], { mount(root.childNodes[0], createComponent({
view : function() { view : function() {
return m("div", { return m("div", {
oninit : oninit0, oninit : oninit0,
@ -147,12 +132,12 @@ o.spec("mount", function() {
onclick : onclick0, onclick : onclick0,
}) })
} }
}) }))
o(oninit0.callCount).equals(1) o(oninit0.callCount).equals(1)
o(onupdate0.callCount).equals(0) o(onupdate0.callCount).equals(0)
mount(root.childNodes[1], { mount(root.childNodes[1], createComponent({
view : function() { view : function() {
return m("div", { return m("div", {
oninit : oninit1, oninit : oninit1,
@ -160,7 +145,7 @@ o.spec("mount", function() {
onclick : onclick1, onclick : onclick1,
}) })
} }
}) }))
o(oninit1.callCount).equals(1) o(oninit1.callCount).equals(1)
o(onupdate1.callCount).equals(0) o(onupdate1.callCount).equals(0)
@ -194,7 +179,7 @@ o.spec("mount", function() {
e.initEvent("click", true, true) e.initEvent("click", true, true)
mount(root, { mount(root, createComponent({
view: function() { view: function() {
return m("div", { return m("div", {
oninit: oninit, oninit: oninit,
@ -204,7 +189,7 @@ o.spec("mount", function() {
} }
}) })
} }
}) }))
root.firstChild.dispatchEvent(e) root.firstChild.dispatchEvent(e)
@ -222,14 +207,14 @@ o.spec("mount", function() {
var onupdate = o.spy() var onupdate = o.spy()
var oninit = o.spy() var oninit = o.spy()
mount(root, { mount(root, createComponent({
view : function() { view : function() {
return m("div", { return m("div", {
oninit: oninit, oninit: oninit,
onupdate: onupdate onupdate: onupdate
}) })
} }
}) }))
o(oninit.callCount).equals(1) o(oninit.callCount).equals(1)
o(onupdate.callCount).equals(0) o(onupdate.callCount).equals(0)
@ -248,7 +233,7 @@ o.spec("mount", function() {
timeout(200) timeout(200)
var i = 0 var i = 0
mount(root, {view: function() {i++}}) mount(root, createComponent({view: function() {i++}}))
var before = i var before = i
redrawService.redraw() redrawService.redraw()
@ -265,4 +250,6 @@ o.spec("mount", function() {
done() done()
},40) },40)
}) })
})
})
}) })

View file

@ -82,7 +82,7 @@ o.spec("route", function() {
}) })
o("routed mount points can redraw synchronously (factory component)", function() { o("routed mount points can redraw synchronously (closure component)", function() {
var view = o.spy() var view = o.spy()
function Cmp() {return {view: view}} function Cmp() {return {view: view}}

View file

@ -20,8 +20,8 @@ module.exports = function($window) {
} }
function createNode(parent, vnode, hooks, ns, nextSibling) { function createNode(parent, vnode, hooks, ns, nextSibling) {
var tag = vnode.tag var tag = vnode.tag
if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)
if (typeof tag === "string") { if (typeof tag === "string") {
if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)
switch (tag) { switch (tag) {
case "#": return createText(parent, vnode, nextSibling) case "#": return createText(parent, vnode, nextSibling)
case "<": return createHTML(parent, vnode, nextSibling) case "<": return createHTML(parent, vnode, nextSibling)
@ -100,7 +100,7 @@ module.exports = function($window) {
} }
return element return element
} }
function createComponent(parent, vnode, hooks, ns, nextSibling) { function initComponent(vnode, hooks) {
var sentinel var sentinel
if (typeof vnode.tag === "function") { if (typeof vnode.tag === "function") {
vnode.state = null vnode.state = null
@ -116,10 +116,13 @@ module.exports = function($window) {
sentinel.$$reentrantLock$$ = true sentinel.$$reentrantLock$$ = true
} }
if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)
initLifecycle(vnode.state, vnode, hooks) initLifecycle(vnode.state, vnode, hooks)
vnode.instance = Vnode.normalize(vnode.state.view(vnode)) vnode.instance = Vnode.normalize(vnode.state.view(vnode))
sentinel.$$reentrantLock$$ = null sentinel.$$reentrantLock$$ = null
}
function createComponent(parent, vnode, hooks, ns, nextSibling) {
initComponent(vnode, hooks)
if (vnode.instance != null) { if (vnode.instance != null) {
if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as arguments") if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as arguments")
var element = createNode(parent, vnode.instance, hooks, ns, nextSibling) var element = createNode(parent, vnode.instance, hooks, ns, nextSibling)
@ -232,11 +235,12 @@ module.exports = function($window) {
if (oldTag === tag) { if (oldTag === tag) {
vnode.state = old.state vnode.state = old.state
vnode.events = old.events vnode.events = old.events
if (shouldUpdate(vnode, old)) return if (!recycling && shouldNotUpdate(vnode, old)) return
if (vnode.attrs != null) {
updateLifecycle(vnode.attrs, vnode, hooks, recycling)
}
if (typeof oldTag === "string") { if (typeof oldTag === "string") {
if (vnode.attrs != null) {
if (recycling) initLifecycle(vnode.attrs, vnode, hooks)
else updateLifecycle(vnode.attrs, vnode, hooks)
}
switch (oldTag) { switch (oldTag) {
case "#": updateText(old, vnode); break case "#": updateText(old, vnode); break
case "<": updateHTML(parent, old, vnode, nextSibling); break case "<": updateHTML(parent, old, vnode, nextSibling); break
@ -306,8 +310,13 @@ module.exports = function($window) {
} }
} }
function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) { function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) {
if (recycling) {
initComponent(vnode, hooks)
} else {
vnode.instance = Vnode.normalize(vnode.state.view(vnode)) vnode.instance = Vnode.normalize(vnode.state.view(vnode))
updateLifecycle(vnode.state, vnode, hooks, recycling) if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks)
updateLifecycle(vnode.state, vnode, hooks)
}
if (vnode.instance != null) { if (vnode.instance != null) {
if (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling) if (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling)
else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns) else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns)
@ -566,11 +575,10 @@ module.exports = function($window) {
if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode) if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode)
if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode)) if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode))
} }
function updateLifecycle(source, vnode, hooks, recycling) { function updateLifecycle(source, vnode, hooks) {
if (recycling) initLifecycle(source, vnode, hooks) if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode))
else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode))
} }
function shouldUpdate(vnode, old) { function shouldNotUpdate(vnode, old) {
var forceVnodeUpdate, forceComponentUpdate var forceVnodeUpdate, forceComponentUpdate
if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") forceVnodeUpdate = vnode.attrs.onbeforeupdate.call(vnode.state, vnode, old) if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") forceVnodeUpdate = vnode.attrs.onbeforeupdate.call(vnode.state, vnode, old)
if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeupdate === "function") forceComponentUpdate = vnode.state.onbeforeupdate(vnode, old) if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeupdate === "function") forceComponentUpdate = vnode.state.onbeforeupdate(vnode, old)

View file

@ -8,6 +8,7 @@
<script src="../../ospec/ospec.js"></script> <script src="../../ospec/ospec.js"></script>
<script src="../../test-utils/callAsync.js"></script> <script src="../../test-utils/callAsync.js"></script>
<script src="../../test-utils/domMock.js"></script> <script src="../../test-utils/domMock.js"></script>
<script src="../../test-utils/component.js"></script>
<script src="../../render/vnode.js"></script> <script src="../../render/vnode.js"></script>
<script src="../../render/trust.js"></script> <script src="../../render/trust.js"></script>

View file

@ -1,6 +1,7 @@
"use strict" "use strict"
var o = require("../../ospec/ospec") var o = require("../../ospec/ospec")
var components = require("../../test-utils/components")
var domMock = require("../../test-utils/domMock") var domMock = require("../../test-utils/domMock")
var vdom = require("../../render/render") var vdom = require("../../render/render")
@ -13,13 +14,17 @@ o.spec("component", function() {
render = vdom($window).render render = vdom($window).render
}) })
components.forEach(function(cmp){
o.spec(cmp.kind, function(){
var createComponent = cmp.create
o.spec("basics", function() { o.spec("basics", function() {
o("works", function() { o("works", function() {
var component = { var component = createComponent({
view: function() { view: function() {
return {tag: "div", attrs: {id: "a"}, text: "b"} return {tag: "div", attrs: {id: "a"}, text: "b"}
} }
} })
var node = {tag: component} var node = {tag: component}
render(root, [node]) render(root, [node])
@ -29,11 +34,11 @@ o.spec("component", function() {
o(root.firstChild.firstChild.nodeValue).equals("b") o(root.firstChild.firstChild.nodeValue).equals("b")
}) })
o("receives arguments", function() { o("receives arguments", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return {tag: "div", attrs: vnode.attrs, text: vnode.text} return {tag: "div", attrs: vnode.attrs, text: vnode.text}
} }
} })
var node = {tag: component, attrs: {id: "a"}, text: "b"} var node = {tag: component, attrs: {id: "a"}, text: "b"}
render(root, [node]) render(root, [node])
@ -43,11 +48,11 @@ o.spec("component", function() {
o(root.firstChild.firstChild.nodeValue).equals("b") o(root.firstChild.firstChild.nodeValue).equals("b")
}) })
o("updates", function() { o("updates", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return {tag: "div", attrs: vnode.attrs, text: vnode.text} return {tag: "div", attrs: vnode.attrs, text: vnode.text}
} }
} })
render(root, [{tag: component, attrs: {id: "a"}, text: "b"}]) render(root, [{tag: component, attrs: {id: "a"}, text: "b"}])
render(root, [{tag: component, attrs: {id: "c"}, text: "d"}]) render(root, [{tag: component, attrs: {id: "c"}, text: "d"}])
@ -57,11 +62,11 @@ o.spec("component", function() {
}) })
o("updates root from null", function() { o("updates root from null", function() {
var visible = false var visible = false
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return visible ? {tag: "div"} : null return visible ? {tag: "div"} : null
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
visible = true visible = true
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -70,11 +75,11 @@ o.spec("component", function() {
}) })
o("updates root from primitive", function() { o("updates root from primitive", function() {
var visible = false var visible = false
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return visible ? {tag: "div"} : false return visible ? {tag: "div"} : false
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
visible = true visible = true
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -83,11 +88,11 @@ o.spec("component", function() {
}) })
o("updates root to null", function() { o("updates root to null", function() {
var visible = true var visible = true
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return visible ? {tag: "div"} : null return visible ? {tag: "div"} : null
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
visible = false visible = false
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -96,11 +101,11 @@ o.spec("component", function() {
}) })
o("updates root to primitive", function() { o("updates root to primitive", function() {
var visible = true var visible = true
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return visible ? {tag: "div"} : false return visible ? {tag: "div"} : false
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
visible = false visible = false
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -108,22 +113,22 @@ o.spec("component", function() {
o(root.firstChild.nodeValue).equals("") o(root.firstChild.nodeValue).equals("")
}) })
o("updates root from null to null", function() { o("updates root from null to null", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return null return null
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.childNodes.length).equals(0) o(root.childNodes.length).equals(0)
}) })
o("removes", function() { o("removes", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return {tag: "div"} return {tag: "div"}
} }
} })
var div = {tag: "div", key: 2} var div = {tag: "div", key: 2}
render(root, [{tag: component, key: 1}, div]) render(root, [{tag: component, key: 1}, div])
render(root, [{tag: "div", key: 2}]) render(root, [{tag: "div", key: 2}])
@ -132,21 +137,21 @@ o.spec("component", function() {
o(root.firstChild).equals(div.dom) o(root.firstChild).equals(div.dom)
}) })
o("svg works when creating across component boundary", function() { o("svg works when creating across component boundary", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return {tag: "g"} return {tag: "g"}
} }
} })
render(root, [{tag: "svg", children: [{tag: component}]}]) render(root, [{tag: "svg", children: [{tag: component}]}])
o(root.firstChild.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg") o(root.firstChild.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg")
}) })
o("svg works when updating across component boundary", function() { o("svg works when updating across component boundary", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return {tag: "g"} return {tag: "g"}
} }
} })
render(root, [{tag: "svg", children: [{tag: component}]}]) render(root, [{tag: "svg", children: [{tag: component}]}])
render(root, [{tag: "svg", children: [{tag: component}]}]) render(root, [{tag: "svg", children: [{tag: component}]}])
@ -155,14 +160,14 @@ o.spec("component", function() {
}) })
o.spec("return value", function() { o.spec("return value", function() {
o("can return fragments", function() { o("can return fragments", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return [ return [
{tag: "label"}, {tag: "label"},
{tag: "input"}, {tag: "input"},
] ]
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.childNodes.length).equals(2) o(root.childNodes.length).equals(2)
@ -170,98 +175,98 @@ o.spec("component", function() {
o(root.childNodes[1].nodeName).equals("INPUT") o(root.childNodes[1].nodeName).equals("INPUT")
}) })
o("can return string", function() { o("can return string", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return "a" return "a"
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeType).equals(3)
o(root.firstChild.nodeValue).equals("a") o(root.firstChild.nodeValue).equals("a")
}) })
o("can return falsy string", function() { o("can return falsy string", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return "" return ""
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeType).equals(3)
o(root.firstChild.nodeValue).equals("") o(root.firstChild.nodeValue).equals("")
}) })
o("can return number", function() { o("can return number", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return 1 return 1
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeType).equals(3)
o(root.firstChild.nodeValue).equals("1") o(root.firstChild.nodeValue).equals("1")
}) })
o("can return falsy number", function() { o("can return falsy number", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return 0 return 0
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeType).equals(3)
o(root.firstChild.nodeValue).equals("0") o(root.firstChild.nodeValue).equals("0")
}) })
o("can return boolean", function() { o("can return boolean", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return true return true
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeType).equals(3)
o(root.firstChild.nodeValue).equals("true") o(root.firstChild.nodeValue).equals("true")
}) })
o("can return falsy boolean", function() { o("can return falsy boolean", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return false return false
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeType).equals(3)
o(root.firstChild.nodeValue).equals("") o(root.firstChild.nodeValue).equals("")
}) })
o("can return null", function() { o("can return null", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return null return null
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.childNodes.length).equals(0) o(root.childNodes.length).equals(0)
}) })
o("can return undefined", function() { o("can return undefined", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return undefined return undefined
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.childNodes.length).equals(0) o(root.childNodes.length).equals(0)
}) })
o("throws a custom error if it returns itself", function() { o("throws a custom error if it returns itself", function() {
// A view that returns its vnode would otherwise trigger an infinite loop // A view that returns its vnode would otherwise trigger an infinite loop
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return vnode return vnode
} }
} })
try { try {
render(root, [{tag: component}]) render(root, [{tag: component}])
} }
@ -272,14 +277,14 @@ o.spec("component", function() {
} }
}) })
o("can update when returning fragments", function() { o("can update when returning fragments", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return [ return [
{tag: "label"}, {tag: "label"},
{tag: "input"}, {tag: "input"},
] ]
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -288,11 +293,11 @@ o.spec("component", function() {
o(root.childNodes[1].nodeName).equals("INPUT") o(root.childNodes[1].nodeName).equals("INPUT")
}) })
o("can update when returning primitive", function() { o("can update when returning primitive", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return "a" return "a"
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -300,25 +305,25 @@ o.spec("component", function() {
o(root.firstChild.nodeValue).equals("a") o(root.firstChild.nodeValue).equals("a")
}) })
o("can update when returning null", function() { o("can update when returning null", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return null return null
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
render(root, [{tag: component}]) render(root, [{tag: component}])
o(root.childNodes.length).equals(0) o(root.childNodes.length).equals(0)
}) })
o("can remove when returning fragments", function() { o("can remove when returning fragments", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return [ return [
{tag: "label"}, {tag: "label"},
{tag: "input"}, {tag: "input"},
] ]
} }
} })
var div = {tag: "div", key: 2} var div = {tag: "div", key: 2}
render(root, [{tag: component, key: 1}, div]) render(root, [{tag: component, key: 1}, div])
@ -328,11 +333,11 @@ o.spec("component", function() {
o(root.firstChild).equals(div.dom) o(root.firstChild).equals(div.dom)
}) })
o("can remove when returning primitive", function() { o("can remove when returning primitive", function() {
var component = { var component = createComponent({
view: function(vnode) { view: function(vnode) {
return "a" return "a"
} }
} })
var div = {tag: "div", key: 2} var div = {tag: "div", key: 2}
render(root, [{tag: component, key: 1}, div]) render(root, [{tag: component, key: 1}, div])
@ -345,7 +350,7 @@ o.spec("component", function() {
o.spec("lifecycle", function() { o.spec("lifecycle", function() {
o("calls oninit", function() { o("calls oninit", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
oninit: function(vnode) { oninit: function(vnode) {
called++ called++
@ -356,7 +361,7 @@ o.spec("component", function() {
view: function() { view: function() {
return {tag: "div", attrs: {id: "a"}, text: "b"} return {tag: "div", attrs: {id: "a"}, text: "b"}
} }
} })
var node = {tag: component} var node = {tag: component}
render(root, [node]) render(root, [node])
@ -368,7 +373,7 @@ o.spec("component", function() {
}) })
o("calls oninit when returning fragment", function() { o("calls oninit when returning fragment", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
oninit: function(vnode) { oninit: function(vnode) {
called++ called++
@ -379,7 +384,7 @@ o.spec("component", function() {
view: function() { view: function() {
return [{tag: "div", attrs: {id: "a"}, text: "b"}] return [{tag: "div", attrs: {id: "a"}, text: "b"}]
} }
} })
var node = {tag: component} var node = {tag: component}
render(root, [node]) render(root, [node])
@ -392,7 +397,7 @@ o.spec("component", function() {
o("calls oninit before view", function() { o("calls oninit before view", function() {
var viewCalled = false var viewCalled = false
render(root, { render(root, createComponent({
tag: { tag: {
view: function() { view: function() {
viewCalled = true viewCalled = true
@ -402,16 +407,16 @@ o.spec("component", function() {
o(viewCalled).equals(false) o(viewCalled).equals(false)
}, },
} }
}) }))
}) })
o("does not calls oninit on redraw", function() { o("does not calls oninit on redraw", function() {
var init = o.spy() var init = o.spy()
var component = { var component = createComponent({
view: function() { view: function() {
return {tag: "div", attrs: {id: "a"}, text: "b"} return {tag: "div", attrs: {id: "a"}, text: "b"}
}, },
oninit: init, oninit: init,
} })
function view() { function view() {
return {tag: component} return {tag: component}
@ -424,7 +429,7 @@ o.spec("component", function() {
}) })
o("calls oncreate", function() { o("calls oncreate", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
oncreate: function(vnode) { oncreate: function(vnode) {
called++ called++
@ -435,7 +440,7 @@ o.spec("component", function() {
view: function() { view: function() {
return {tag: "div", attrs: {id: "a"}, text: "b"} return {tag: "div", attrs: {id: "a"}, text: "b"}
} }
} })
var node = {tag: component} var node = {tag: component}
render(root, [node]) render(root, [node])
@ -447,12 +452,12 @@ o.spec("component", function() {
}) })
o("does not calls oncreate on redraw", function() { o("does not calls oncreate on redraw", function() {
var create = o.spy() var create = o.spy()
var component = { var component = createComponent({
view: function() { view: function() {
return {tag: "div", attrs: {id: "a"}, text: "b"} return {tag: "div", attrs: {id: "a"}, text: "b"}
}, },
oncreate: create, oncreate: create,
} })
function view() { function view() {
return {tag: component} return {tag: component}
@ -465,7 +470,7 @@ o.spec("component", function() {
}) })
o("calls oncreate when returning fragment", function() { o("calls oncreate when returning fragment", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
oncreate: function(vnode) { oncreate: function(vnode) {
called++ called++
@ -476,7 +481,7 @@ o.spec("component", function() {
view: function() { view: function() {
return [{tag: "div", attrs: {id: "a"}, text: "b"}] return [{tag: "div", attrs: {id: "a"}, text: "b"}]
} }
} })
var node = {tag: component} var node = {tag: component}
render(root, [node]) render(root, [node])
@ -488,7 +493,7 @@ o.spec("component", function() {
}) })
o("calls onupdate", function() { o("calls onupdate", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
onupdate: function(vnode) { onupdate: function(vnode) {
called++ called++
@ -499,7 +504,7 @@ o.spec("component", function() {
view: function() { view: function() {
return {tag: "div", attrs: {id: "a"}, text: "b"} return {tag: "div", attrs: {id: "a"}, text: "b"}
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -514,7 +519,7 @@ o.spec("component", function() {
}) })
o("calls onupdate when returning fragment", function() { o("calls onupdate when returning fragment", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
onupdate: function(vnode) { onupdate: function(vnode) {
called++ called++
@ -525,7 +530,7 @@ o.spec("component", function() {
view: function() { view: function() {
return [{tag: "div", attrs: {id: "a"}, text: "b"}] return [{tag: "div", attrs: {id: "a"}, text: "b"}]
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -540,7 +545,7 @@ o.spec("component", function() {
}) })
o("calls onremove", function() { o("calls onremove", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
onremove: function(vnode) { onremove: function(vnode) {
called++ called++
@ -551,7 +556,7 @@ o.spec("component", function() {
view: function() { view: function() {
return {tag: "div", attrs: {id: "a"}, text: "b"} return {tag: "div", attrs: {id: "a"}, text: "b"}
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -564,7 +569,7 @@ o.spec("component", function() {
}) })
o("calls onremove when returning fragment", function() { o("calls onremove when returning fragment", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
onremove: function(vnode) { onremove: function(vnode) {
called++ called++
@ -575,7 +580,7 @@ o.spec("component", function() {
view: function() { view: function() {
return [{tag: "div", attrs: {id: "a"}, text: "b"}] return [{tag: "div", attrs: {id: "a"}, text: "b"}]
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -588,7 +593,7 @@ o.spec("component", function() {
}) })
o("calls onbeforeremove", function() { o("calls onbeforeremove", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
onbeforeremove: function(vnode) { onbeforeremove: function(vnode) {
called++ called++
@ -599,7 +604,7 @@ o.spec("component", function() {
view: function() { view: function() {
return {tag: "div", attrs: {id: "a"}, text: "b"} return {tag: "div", attrs: {id: "a"}, text: "b"}
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -612,7 +617,7 @@ o.spec("component", function() {
}) })
o("calls onbeforeremove when returning fragment", function() { o("calls onbeforeremove when returning fragment", function() {
var called = 0 var called = 0
var component = { var component = createComponent({
onbeforeremove: function(vnode) { onbeforeremove: function(vnode) {
called++ called++
@ -623,7 +628,7 @@ o.spec("component", function() {
view: function() { view: function() {
return [{tag: "div", attrs: {id: "a"}, text: "b"}] return [{tag: "div", attrs: {id: "a"}, text: "b"}]
} }
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -635,12 +640,12 @@ o.spec("component", function() {
o(root.childNodes.length).equals(0) o(root.childNodes.length).equals(0)
}) })
o("does not recycle when there's an onupdate", function() { o("does not recycle when there's an onupdate", function() {
var component = { var component = createComponent({
onupdate: function() {}, onupdate: function() {},
view: function() { view: function() {
return {tag: "div"} return {tag: "div"}
} }
} })
var update = o.spy() var update = o.spy()
var vnode = {tag: component, key: 1} var vnode = {tag: component, key: 1}
var updated = {tag: component, key: 1} var updated = {tag: component, key: 1}
@ -651,41 +656,179 @@ o.spec("component", function() {
o(vnode.dom).notEquals(updated.dom) 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.spec("state", function() {
o("copies state", function() { o("initializes state", function() {
var called = 0 var called = 0
var data = {a: 1} var data = {a: 1}
var component = { var component = createComponent(createComponent({
data: data, data: data,
oninit: init, oninit: init,
view: function() { view: function() {
return "" return ""
} }
} }))
render(root, [{tag: component}]) render(root, [{tag: component}])
function init(vnode) { function init(vnode) {
o(vnode.state.data).deepEquals(data)
o(vnode.state.data).equals(data) o(vnode.state.data).equals(data)
//inherits state via prototype
component.x = 1
o(vnode.state.x).equals(1)
} }
}) })
o("state copy is shallow", function() { o('state proxies to the component object/prototype', function() {
var called = 0 var called = 0
var body = {a: 1} var body = {a: 1}
var data = [body] var data = [body]
var component = { var component = createComponent(createComponent({
data: data, data: data,
oninit: init, oninit: init,
view: function() { view: function() {
return "" return ""
} }
} }))
render(root, [{tag: component}]) render(root, [{tag: component}])
@ -695,141 +838,77 @@ o.spec("component", function() {
} }
}) })
}) })
o.spec("Alternative ways to specify componenents", function() { })
o("Classes can be used as components", function() { })
function MyComponent(vnode){ o.spec("Tests specific to certain component kinds", function() {
o(vnode.state).equals(null) o.spec("state", function() {
o("POJO", function() {
var called = 0
var data = {}
var component = {
data: data,
oninit: init,
view: function() {
return ""
} }
var proto = MyComponent.prototype }
render(root, [{tag: component}])
function init(vnode) {
o(vnode.state.data).equals(data)
//inherits state via prototype
component.x = 1
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
var context var context
proto.oninit = o.spy(function(vnode) { render(root, [{tag: component, attrs: {oninit: oninit}}])
o(this).equals(vnode.state) render(root, [{tag: component, attrs: {oninit: oninit}}])
context = this
})
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 ""
})
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, []) render(root, [])
o(proto.view.callCount).equals(2) o(component.callCount).equals(1)
o(proto.oncreate.callCount).equals(1) o(oninit.callCount).equals(2)
o(proto.onbeforeupdate.callCount).equals(1) o(view.callCount).equals(2)
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("Factory functions can be used as components", function() { o("Closure", function() {
var state, context var state
function component(vnode) { 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(vnode.state).equals(null)
o(oninit.callCount).equals(0)
return state = { return state = {
oninit: o.spy(function(vnode) { view: view
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, [{tag: component, attrs: {oninit: oninit}}])
render(root, [{tag: component, attrs: {oninit: oninit}}])
render(root, []) render(root, [])
o(state.oninit.callCount).equals(1) o(component.callCount).equals(1)
o(state.view.callCount).equals(2) o(oninit.callCount).equals(1)
o(state.oncreate.callCount).equals(1) o(view.callCount).equals(2)
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

@ -2,6 +2,7 @@
var o = require("../../ospec/ospec") var o = require("../../ospec/ospec")
var callAsync = require("../../test-utils/callAsync") var callAsync = require("../../test-utils/callAsync")
var components = require("../../test-utils/components")
var domMock = require("../../test-utils/domMock") var domMock = require("../../test-utils/domMock")
var vdom = require("../../render/render") var vdom = require("../../render/render")
var Promise = require("../../promise/promise") var Promise = require("../../promise/promise")
@ -169,14 +170,17 @@ o.spec("onbeforeremove", function() {
done() done()
}) })
}) })
components.forEach(function(cmp){
o.spec(cmp.kind, function(){
var createComponent = cmp.create
o("finalizes the remove phase asynchronously when promise is returned synchronously from both attrs- and tag.onbeforeremove", function(done) { o("finalizes the remove phase asynchronously when promise is returned synchronously from both attrs- and tag.onbeforeremove", function(done) {
var onremove = o.spy() var onremove = o.spy()
var onbeforeremove = function(){return Promise.resolve()} var onbeforeremove = function(){return Promise.resolve()}
var component = { var component = createComponent({
onbeforeremove: onbeforeremove, onbeforeremove: onbeforeremove,
onremove: onremove, onremove: onremove,
view: function() {}, view: function() {},
} })
render(root, [{tag: component, attrs: {onbeforeremove: onbeforeremove, onremove: onremove}}]) render(root, [{tag: component, attrs: {onbeforeremove: onbeforeremove, onremove: onremove}}])
render(root, []) render(root, [])
callAsync(function() { callAsync(function() {
@ -188,11 +192,11 @@ o.spec("onbeforeremove", function() {
var view = o.spy() var view = o.spy()
var onremove = o.spy() var onremove = o.spy()
var onbeforeremove = function(){return new Promise(function(resolve){callAsync(resolve)})} var onbeforeremove = function(){return new Promise(function(resolve){callAsync(resolve)})}
var component = { var component = createComponent({
onbeforeremove: onbeforeremove, onbeforeremove: onbeforeremove,
onremove: onremove, onremove: onremove,
view: view, view: view,
} })
render(root, [{tag: component}]) render(root, [{tag: component}])
render(root, []) render(root, [])
@ -205,4 +209,6 @@ o.spec("onbeforeremove", function() {
}) })
}) })
}) })
})
})
}) })

View file

@ -1,6 +1,7 @@
"use strict" "use strict"
var o = require("../../ospec/ospec") var o = require("../../ospec/ospec")
var components = require("../../test-utils/components")
var domMock = require("../../test-utils/domMock") var domMock = require("../../test-utils/domMock")
var vdom = require("../../render/render") var vdom = require("../../render/render")
@ -56,86 +57,6 @@ o.spec("onbeforeupdate", function() {
o(root.firstChild.nodeValue).equals("a") o(root.firstChild.nodeValue).equals("a")
}) })
o("prevents update in component", function() {
var component = {
onbeforeupdate: function() {return false},
view: function(vnode) {
return {tag: "div", children: vnode.children}
},
}
var vnode = {tag: component, children: [{tag: "#", children: "a"}]}
var updated = {tag: component, children: [{tag: "#", children: "b"}]}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.firstChild.nodeValue).equals("a")
})
o("prevents update if returning false in component and false in vnode", function() {
var component = {
onbeforeupdate: function() {return false},
view: function(vnode) {
return {tag: "div", attrs: {id: vnode.attrs.id}}
},
}
var vnode = {tag: component, attrs: {id: "a", onbeforeupdate: function() {return false}}}
var updated = {tag: component, attrs: {id: "b", onbeforeupdate: function() {return false}}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("a")
})
o("does not prevent update if returning true in component and true in vnode", function() {
var component = {
onbeforeupdate: function() {return true},
view: function(vnode) {
return {tag: "div", attrs: {id: vnode.attrs.id}}
},
}
var vnode = {tag: component, attrs: {id: "a", onbeforeupdate: function() {return true}}}
var updated = {tag: component, attrs: {id: "b", onbeforeupdate: function() {return true}}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("does not prevent update if returning false in component but true in vnode", function() {
var component = {
onbeforeupdate: function() {return false},
view: function(vnode) {
return {tag: "div", attrs: {id: vnode.attrs.id}}
},
}
var vnode = {tag: component, attrs: {id: "a", onbeforeupdate: function() {return true}}}
var updated = {tag: component, attrs: {id: "b", onbeforeupdate: function() {return true}}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("does not prevent update if returning true in component but false in vnode", function() {
var component = {
onbeforeupdate: function() {return true},
view: function(vnode) {
return {tag: "div", attrs: {id: vnode.attrs.id}}
},
}
var vnode = {tag: component, attrs: {id: "a", onbeforeupdate: function() {return false}}}
var updated = {tag: component, attrs: {id: "b", onbeforeupdate: function() {return false}}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("does not prevent update if returning true", function() { o("does not prevent update if returning true", function() {
var onbeforeupdate = function() {return true} var onbeforeupdate = function() {return true}
var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}}
@ -147,22 +68,6 @@ o.spec("onbeforeupdate", function() {
o(root.firstChild.attributes["id"].nodeValue).equals("b") o(root.firstChild.attributes["id"].nodeValue).equals("b")
}) })
o("does not prevent update if returning true from component", function() {
var component = {
onbeforeupdate: function() {return true},
view: function(vnode) {
return {tag: "div", attrs: vnode.attrs}
},
}
var vnode = {tag: component, attrs: {id: "a"}}
var updated = {tag: component, attrs: {id: "b"}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("accepts arguments for comparison", function() { o("accepts arguments for comparison", function() {
var count = 0 var count = 0
var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}}
@ -184,33 +89,6 @@ o.spec("onbeforeupdate", function() {
o(root.firstChild.attributes["id"].nodeValue).equals("b") o(root.firstChild.attributes["id"].nodeValue).equals("b")
}) })
o("accepts arguments for comparison in component", function() {
var component = {
onbeforeupdate: onbeforeupdate,
view: function(vnode) {
return {tag: "div", attrs: vnode.attrs}
},
}
var count = 0
var vnode = {tag: component, attrs: {id: "a"}}
var updated = {tag: component, attrs: {id: "b"}}
render(root, [vnode])
render(root, [updated])
function onbeforeupdate(vnode, old) {
count++
o(old.attrs.id).equals("a")
o(vnode.attrs.id).equals("b")
return old.attrs.id !== vnode.attrs.id
}
o(count).equals(1)
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("is not called on creation", function() { o("is not called on creation", function() {
var count = 0 var count = 0
var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}}
@ -226,28 +104,6 @@ o.spec("onbeforeupdate", function() {
o(count).equals(0) o(count).equals(0)
}) })
o("is not called on component creation", function() {
var component = {
onbeforeupdate: onbeforeupdate,
view: function(vnode) {
return {tag: "div", attrs: vnode.attrs}
},
}
var count = 0
var vnode = {tag: "div", attrs: {id: "a"}}
var updated = {tag: "div", attrs: {id: "b"}}
render(root, [vnode])
function onbeforeupdate(vnode, old) {
count++
return true
}
o(count).equals(0)
})
o("is called only once on update", function() { o("is called only once on update", function() {
var count = 0 var count = 0
var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}}
@ -264,14 +120,178 @@ o.spec("onbeforeupdate", function() {
o(count).equals(1) o(count).equals(1)
}) })
o("is called only once on component update", function() { o("doesn't fire on recycled nodes", function() {
var component = { 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
o("prevents update in component", function() {
var component = createComponent({
onbeforeupdate: function() {return false},
view: function(vnode) {
return {tag: "div", children: vnode.children}
},
})
var vnode = {tag: component, children: [{tag: "#", children: "a"}]}
var updated = {tag: component, children: [{tag: "#", children: "b"}]}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.firstChild.nodeValue).equals("a")
})
o("prevents update if returning false in component and false in vnode", function() {
var component = createComponent({
onbeforeupdate: function() {return false},
view: function(vnode) {
return {tag: "div", attrs: {id: vnode.attrs.id}}
},
})
var vnode = {tag: component, attrs: {id: "a", onbeforeupdate: function() {return false}}}
var updated = {tag: component, attrs: {id: "b", onbeforeupdate: function() {return false}}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("a")
})
o("does not prevent update if returning true in component and true in vnode", function() {
var component = createComponent({
onbeforeupdate: function() {return true},
view: function(vnode) {
return {tag: "div", attrs: {id: vnode.attrs.id}}
},
})
var vnode = {tag: component, attrs: {id: "a", onbeforeupdate: function() {return true}}}
var updated = {tag: component, attrs: {id: "b", onbeforeupdate: function() {return true}}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("does not prevent update if returning false in component but true in vnode", function() {
var component = createComponent({
onbeforeupdate: function() {return false},
view: function(vnode) {
return {tag: "div", attrs: {id: vnode.attrs.id}}
},
})
var vnode = {tag: component, attrs: {id: "a", onbeforeupdate: function() {return true}}}
var updated = {tag: component, attrs: {id: "b", onbeforeupdate: function() {return true}}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("does not prevent update if returning true in component but false in vnode", function() {
var component = createComponent({
onbeforeupdate: function() {return true},
view: function(vnode) {
return {tag: "div", attrs: {id: vnode.attrs.id}}
},
})
var vnode = {tag: component, attrs: {id: "a", onbeforeupdate: function() {return false}}}
var updated = {tag: component, attrs: {id: "b", onbeforeupdate: function() {return false}}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("does not prevent update if returning true from component", function() {
var component = createComponent({
onbeforeupdate: function() {return true},
view: function(vnode) {
return {tag: "div", attrs: vnode.attrs}
},
})
var vnode = {tag: component, attrs: {id: "a"}}
var updated = {tag: component, attrs: {id: "b"}}
render(root, [vnode])
render(root, [updated])
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("accepts arguments for comparison in component", function() {
var component = createComponent({
onbeforeupdate: onbeforeupdate, onbeforeupdate: onbeforeupdate,
view: function(vnode) { view: function(vnode) {
return {tag: "div", attrs: vnode.attrs} return {tag: "div", attrs: vnode.attrs}
}, },
})
var count = 0
var vnode = {tag: component, attrs: {id: "a"}}
var updated = {tag: component, attrs: {id: "b"}}
render(root, [vnode])
render(root, [updated])
function onbeforeupdate(vnode, old) {
count++
o(old.attrs.id).equals("a")
o(vnode.attrs.id).equals("b")
return old.attrs.id !== vnode.attrs.id
} }
o(count).equals(1)
o(root.firstChild.attributes["id"].nodeValue).equals("b")
})
o("is not called on component creation", function() {
var component = createComponent({
onbeforeupdate: onbeforeupdate,
view: function(vnode) {
return {tag: "div", attrs: vnode.attrs}
},
})
var count = 0
var vnode = {tag: "div", attrs: {id: "a"}}
var updated = {tag: "div", attrs: {id: "b"}}
render(root, [vnode])
function onbeforeupdate(vnode, old) {
count++
return true
}
o(count).equals(0)
})
o("is called only once on component update", function() {
var component = createComponent({
onbeforeupdate: onbeforeupdate,
view: function(vnode) {
return {tag: "div", attrs: vnode.attrs}
},
})
var count = 0 var count = 0
var vnode = {tag: component, attrs: {id: "a"}} var vnode = {tag: component, attrs: {id: "a"}}
var updated = {tag: component, attrs: {id: "b"}} var updated = {tag: component, attrs: {id: "b"}}
@ -286,4 +306,6 @@ o.spec("onbeforeupdate", function() {
o(count).equals(1) o(count).equals(1)
}) })
})
})
}) })

View file

@ -1,6 +1,7 @@
"use strict" "use strict"
var o = require("../../ospec/ospec") var o = require("../../ospec/ospec")
var components = require("../../test-utils/components")
var domMock = require("../../test-utils/domMock") var domMock = require("../../test-utils/domMock")
var vdom = require("../../render/render") var vdom = require("../../render/render")
var m = require("../../render/hyperscript") var m = require("../../render/hyperscript")
@ -80,39 +81,6 @@ o.spec("onremove", function() {
o(remove.this).equals(vnode.state) o(remove.this).equals(vnode.state)
o(remove.args[0]).equals(vnode) o(remove.args[0]).equals(vnode)
}) })
o("calls onremove on nested component", function() {
var spy = o.spy()
var comp = {
view: function() {return m(outer)}
}
var outer = {
view: function() {return m(inner)}
}
var inner = {
onremove: spy,
view: function() {return m("div")}
}
render(root, {tag: comp})
render(root, null)
o(spy.callCount).equals(1)
})
o("calls onremove on nested component child", function() {
var spy = o.spy()
var comp = {
view: function() {return m(outer)}
}
var outer = {
view: function() {return m(inner, m("a", {onremove: spy}))}
}
var inner = {
view: function(vnode) {return m("div", vnode.children)}
}
render(root, {tag: comp})
render(root, null)
o(spy.callCount).equals(1)
})
o("does not set onremove as an event handler", function() { o("does not set onremove as an event handler", function() {
var remove = o.spy() var remove = o.spy()
var vnode = {tag: "div", attrs: {onremove: remove}, children: []} var vnode = {tag: "div", attrs: {onremove: remove}, children: []}
@ -145,4 +113,43 @@ o.spec("onremove", function() {
o(vnode.dom).notEquals(updated.dom) o(vnode.dom).notEquals(updated.dom)
}) })
components.forEach(function(cmp){
o.spec(cmp.kind, function(){
var createComponent = cmp.create
o("calls onremove on nested component", function() {
var spy = o.spy()
var comp = createComponent({
view: function() {return m(outer)}
})
var outer = createComponent({
view: function() {return m(inner)}
})
var inner = createComponent({
onremove: spy,
view: function() {return m("div")}
})
render(root, {tag: comp})
render(root, null)
o(spy.callCount).equals(1)
})
o("calls onremove on nested component child", function() {
var spy = o.spy()
var comp = createComponent({
view: function() {return m(outer)}
})
var outer = createComponent({
view: function() {return m(inner, m("a", {onremove: spy}))}
})
var inner = createComponent({
view: function(vnode) {return m("div", vnode.children)}
})
render(root, {tag: comp})
render(root, null)
o(spy.callCount).equals(1)
})
})
})
}) })

View file

@ -1,6 +1,7 @@
"use strict" "use strict"
var o = require("../../ospec/ospec") var o = require("../../ospec/ospec")
var components = require("../../test-utils/components")
var domMock = require("../../test-utils/domMock") var domMock = require("../../test-utils/domMock")
var vdom = require("../../render/render") var vdom = require("../../render/render")
@ -118,7 +119,7 @@ o.spec("render", function() {
o(oninit.callCount).equals(0) o(oninit.callCount).equals(0)
o(onbeforeupdate.callCount).equals(0) o(onbeforeupdate.callCount).equals(0)
}) })
o("does not try to re-initialize a factory component whose view has thrown", function() { o("does not try to re-initialize a closure component whose view has thrown", function() {
var oninit = o.spy() var oninit = o.spy()
var onbeforeupdate = o.spy() var onbeforeupdate = o.spy()
function A() { function A() {
@ -141,7 +142,7 @@ o.spec("render", function() {
o(oninit.callCount).equals(1) o(oninit.callCount).equals(1)
o(onbeforeupdate.callCount).equals(0) o(onbeforeupdate.callCount).equals(0)
}) })
o("does not try to re-initialize a factory component whose oninit has thrown", function() { o("does not try to re-initialize a closure component whose oninit has thrown", function() {
var oninit = o.spy(function(vnode) {throw new Error("error")}) var oninit = o.spy(function(vnode) {throw new Error("error")})
var onbeforeupdate = o.spy() var onbeforeupdate = o.spy()
function A() { function A() {
@ -164,7 +165,7 @@ o.spec("render", function() {
o(oninit.callCount).equals(1) o(oninit.callCount).equals(1)
o(onbeforeupdate.callCount).equals(0) o(onbeforeupdate.callCount).equals(0)
}) })
o("does not try to re-initialize a factory component whose factory has thrown", function() { o("does not try to re-initialize a closure component whose closure has thrown", function() {
function A() { function A() {
throw new Error("error") throw new Error("error")
} }

View file

@ -1,6 +1,7 @@
"use strict" "use strict"
var o = require("../../ospec/ospec") var o = require("../../ospec/ospec")
var components = require("../../test-utils/components")
var domMock = require("../../test-utils/domMock") var domMock = require("../../test-utils/domMock")
var vdom = require("../../render/render") var vdom = require("../../render/render")
@ -838,38 +839,6 @@ o.spec("updateNodes", function() {
o(root.childNodes[0].nodeName).equals("A") o(root.childNodes[0].nodeName).equals("A")
o(root.childNodes[1].nodeName).equals("B") o(root.childNodes[1].nodeName).equals("B")
}) })
o("fragment child toggles from null when followed by null component then tag", function() {
var component = {view: function() {return null}}
var vnodes = [{tag: "[", children: [{tag: "a"}, {tag: component}, {tag: "b"}]}]
var temp = [{tag: "[", children: [null, {tag: component}, {tag: "b"}]}]
var updated = [{tag: "[", children: [{tag: "a"}, {tag: component}, {tag: "b"}]}]
render(root, vnodes)
render(root, temp)
render(root, updated)
o(root.childNodes.length).equals(2)
o(root.childNodes[0].nodeName).equals("A")
o(root.childNodes[1].nodeName).equals("B")
})
o("fragment child toggles from null in component when followed by null component then tag", function() {
var flag = true
var a = {view: function() {return flag ? {tag: "a"} : null}}
var b = {view: function() {return null}}
var vnodes = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
var temp = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
var updated = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
render(root, vnodes)
flag = false
render(root, temp)
flag = true
render(root, updated)
o(root.childNodes.length).equals(2)
o(root.childNodes[0].nodeName).equals("A")
o(root.childNodes[1].nodeName).equals("S")
})
o("cached, non-keyed nodes skip diff", function () { o("cached, non-keyed nodes skip diff", function () {
var onupdate = o.spy(); var onupdate = o.spy();
var cached = {tag:"a", attrs:{onupdate: onupdate}} var cached = {tag:"a", attrs:{onupdate: onupdate}}
@ -926,7 +895,7 @@ o.spec("updateNodes", function() {
o(update.callCount).equals(2) o(update.callCount).equals(2)
o(remove.callCount).equals(0) o(remove.callCount).equals(0)
}) })
o("component is recreated if key changes to undefined", function () { o("node is recreated if key changes to undefined", function () {
var vnode = {tag: "b", key: 1} var vnode = {tag: "b", key: 1}
var updated = {tag: "b"} var updated = {tag: "b"}
@ -936,4 +905,42 @@ o.spec("updateNodes", function() {
o(vnode.dom).notEquals(updated.dom) o(vnode.dom).notEquals(updated.dom)
}) })
components.forEach(function(cmp){
o.spec(cmp.kind, function(){
var createComponent = cmp.create
o("fragment child toggles from null when followed by null component then tag", function() {
var component = createComponent({view: function() {return null}})
var vnodes = [{tag: "[", children: [{tag: "a"}, {tag: component}, {tag: "b"}]}]
var temp = [{tag: "[", children: [null, {tag: component}, {tag: "b"}]}]
var updated = [{tag: "[", children: [{tag: "a"}, {tag: component}, {tag: "b"}]}]
render(root, vnodes)
render(root, temp)
render(root, updated)
o(root.childNodes.length).equals(2)
o(root.childNodes[0].nodeName).equals("A")
o(root.childNodes[1].nodeName).equals("B")
})
o("fragment child toggles from null in component when followed by null component then tag", function() {
var flag = true
var a = createComponent({view: function() {return flag ? {tag: "a"} : null}})
var b = createComponent({view: function() {return null}})
var vnodes = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
var temp = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
var updated = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
render(root, vnodes)
flag = false
render(root, temp)
flag = true
render(root, updated)
o(root.childNodes.length).equals(2)
o(root.childNodes[0].nodeName).equals("A")
o(root.childNodes[1].nodeName).equals("S")
})
})
})
}) })

27
test-utils/components.js Normal file
View file

@ -0,0 +1,27 @@
module.exports = [
{
kind: 'POJO',
create: function(methods) {
var res = {view: function() {return {tag:'div'}}}
Object.keys(methods || {}).forEach(function(m){res[m] = methods[m]})
return res
}
}, {
kind: 'constructible',
create: function(methods) {
function res(){}
res.prototype.view = function() {return {tag:'div'}}
Object.keys(methods || {}).forEach(function(m){res.prototype[m] = methods[m]})
return res
}
}, {
kind: 'closure',
create: function(methods) {
return function() {
var res = {view: function() {return {tag:'div'}}}
Object.keys(methods || {}).forEach(function(m){res[m] = methods[m]})
return res
}
}
}
]

View file

@ -14,12 +14,14 @@
<script src="../../test-utils/xhrMock.js"></script> <script src="../../test-utils/xhrMock.js"></script>
<script src="../../test-utils/domMock.js"></script> <script src="../../test-utils/domMock.js"></script>
<script src="../../test-utils/browserMock.js"></script> <script src="../../test-utils/browserMock.js"></script>
<script src="../../test-utils/component.js"></script>
<script src="test-callAsync.js"></script> <script src="test-callAsync.js"></script>
<script src="test-parseURL.js"></script> <script src="test-parseURL.js"></script>
<script src="test-pushStateMock.js"></script> <script src="test-pushStateMock.js"></script>
<script src="test-xhrMock.js"></script> <script src="test-xhrMock.js"></script>
<script src="test-domMock.js"></script> <script src="test-domMock.js"></script>
<script src="test-browserMock.js"></script> <script src="test-browserMock.js"></script>
<script src="test-component.js"></script>
<script>require("../../ospec/ospec").run()</script> <script>require("../../ospec/ospec").run()</script>
</body> </body>

View file

@ -0,0 +1,54 @@
"use strict"
var o = require("../../ospec/ospec")
var components = require("../../test-utils/components")
o.spec("test-utils/components", function() {
var test = o.spy(function(component) {
return function() {
o('works', function() {
o(typeof component.kind).equals('string')
var methods = {oninit: function(){}, view: function(){}}
var cmp1, cmp2
if (component.kind === "POJO") {
cmp1 = component.create()
cmp2 = component.create(methods)
} else if (component.kind === "constructible") {
cmp1 = new (component.create())
cmp2 = new (component.create(methods))
} else if (component.kind === "closure") {
cmp1 = component.create()()
cmp2 = component.create(methods)()
} else {
throw new Error("unexpected component kind")
}
o(cmp1 != null).equals(true)
o(typeof cmp1.view).equals("function")
var vnode = cmp1.view()
o(vnode != null).equals(true)
o(vnode).deepEquals({tag: "div"})
if (component.kind !== 'constructible') {
o(cmp2).deepEquals(methods)
} else {
// deepEquals doesn't search the prototype, do it manually
o(cmp2 != null).equals(true)
o(cmp2.view).equals(methods.view)
o(cmp2.oninit).equals(methods.oninit)
}
})
}
})
o.after(function(){
o(test.callCount).equals(3)
})
components.forEach(function(component) {
o.spec(component.kind, test(component))
})
})

View file

@ -2,6 +2,7 @@
var o = require("../ospec/ospec") var o = require("../ospec/ospec")
var browserMock = require("../test-utils/browserMock") var browserMock = require("../test-utils/browserMock")
var components = require("../test-utils/components")
o.spec("api", function() { o.spec("api", function() {
var m var m
@ -68,6 +69,16 @@ o.spec("api", function() {
o(query).equals("a=1&b=2") o(query).equals("a=1&b=2")
}) })
}) })
o.spec("m.request", function() {
o("works", function() {
o(typeof m.request).equals("function") // TODO improve
})
})
o.spec("m.jsonp", function() {
o("works", function() {
o(typeof m.jsonp).equals("function") // TODO improve
})
})
o.spec("m.render", function() { o.spec("m.render", function() {
o("works", function() { o("works", function() {
var root = window.document.createElement("div") var root = window.document.createElement("div")
@ -77,10 +88,14 @@ o.spec("api", function() {
o(root.firstChild.nodeName).equals("DIV") o(root.firstChild.nodeName).equals("DIV")
}) })
}) })
components.forEach(function(cmp){
o.spec(cmp.kind, function(){
var createComponent = cmp.create
o.spec("m.mount", function() { o.spec("m.mount", function() {
o("works", function() { o("works", function() {
var root = window.document.createElement("div") var root = window.document.createElement("div")
m.mount(root, {view: function() {return m("div")}}) m.mount(root, createComponent({view: function() {return m("div")}}))
o(root.childNodes.length).equals(1) o(root.childNodes.length).equals(1)
o(root.firstChild.nodeName).equals("DIV") o(root.firstChild.nodeName).equals("DIV")
@ -90,7 +105,7 @@ o.spec("api", function() {
o("works", function(done) { o("works", function(done) {
var root = window.document.createElement("div") var root = window.document.createElement("div")
m.route(root, "/a", { m.route(root, "/a", {
"/a": {view: function() {return m("div")}} "/a": createComponent({view: function() {return m("div")}})
}) })
setTimeout(function() { setTimeout(function() {
@ -104,7 +119,7 @@ o.spec("api", function() {
var root = window.document.createElement("div") var root = window.document.createElement("div")
m.route.prefix("#") m.route.prefix("#")
m.route(root, "/a", { m.route(root, "/a", {
"/a": {view: function() {return m("div")}} "/a": createComponent({view: function() {return m("div")}})
}) })
setTimeout(function() { setTimeout(function() {
@ -117,7 +132,7 @@ o.spec("api", function() {
o("m.route.get", function(done) { o("m.route.get", function(done) {
var root = window.document.createElement("div") var root = window.document.createElement("div")
m.route(root, "/a", { m.route(root, "/a", {
"/a": {view: function() {return m("div")}} "/a": createComponent({view: function() {return m("div")}})
}) })
setTimeout(function() { setTimeout(function() {
@ -130,7 +145,7 @@ o.spec("api", function() {
timeout(100) timeout(100)
var root = window.document.createElement("div") var root = window.document.createElement("div")
m.route(root, "/a", { m.route(root, "/a", {
"/:id": {view: function() {return m("div")}} "/:id": createComponent({view: function() {return m("div")}})
}) })
setTimeout(function() { setTimeout(function() {
@ -147,7 +162,7 @@ o.spec("api", function() {
o("works", function(done) { o("works", function(done) {
var count = 0 var count = 0
var root = window.document.createElement("div") var root = window.document.createElement("div")
m.mount(root, {view: function() {count++}}) m.mount(root, createComponent({view: function() {count++}}))
setTimeout(function() { setTimeout(function() {
m.redraw() m.redraw()
@ -157,14 +172,6 @@ o.spec("api", function() {
}, FRAME_BUDGET) }, FRAME_BUDGET)
}) })
}) })
o.spec("m.request", function() {
o("works", function() {
o(typeof m.request).equals("function") // TODO improve
})
})
o.spec("m.jsonp", function() {
o("works", function() {
o(typeof m.jsonp).equals("function") // TODO improve
}) })
}) })
}) })