From ee6d991e5155ce1fc30ab1f37e08b7b79eb06598 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Thu, 19 Jan 2017 15:23:48 +0100 Subject: [PATCH 001/113] [ospec:docs] Document the fact that ospec doesn't work when installed globally --- ospec/README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ospec/README.md b/ospec/README.md index 676be219..8b9c2a79 100644 --- a/ospec/README.md +++ b/ospec/README.md @@ -278,12 +278,20 @@ ospec will automatically evaluate all `*.js` files in any folder named `/tests`. $ npm test ``` -#### Installing ospec globally +#### Direct use from the command line -While it's recommended to install ospec locally to maintain reproducible environments, sometimes it may be deemed appropriate to install it globally. To do so, run this command: +Ospec doesn't work when installed globally. Using global scripts is generally a bad idea since you can end up with different, incompatible versions of the same package installed locally and globally. + +To work around this limitation, you can use [`npm-run`](https://www.npmjs.com/package/npm-run) which enables one to run the binaries of locally installed packages. ``` -npm install ospec -g +npm install npm-run -g +``` + +Then, from a project that has ospec installed as a (dev) dependency: + +``` +npm-run ospec ``` --- From 9d5f686bbdacf8af32c677f2c67f6da6168b45fd Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 3 Feb 2017 22:21:14 -0500 Subject: [PATCH 002/113] #1579 prevent infinite loop in non-existent default route --- api/router.js | 5 ++- api/tests/test-router.js | 2 +- mithril.js | 8 ++-- mithril.min.js | 82 ++++++++++++++++++++-------------------- router/router.js | 6 +-- 5 files changed, 51 insertions(+), 52 deletions(-) diff --git a/api/router.js b/api/router.js index 1d267bb8..b4690966 100644 --- a/api/router.js +++ b/api/router.js @@ -14,8 +14,9 @@ module.exports = function($window, redrawService) { var run = function() { if (render != null) redrawService.render(root, render(Vnode(component, attrs.key, attrs))) } - var bail = function() { - routeService.setPath(defaultRoute, null, {replace: true}) + var bail = function(path) { + if (path !== defaultRoute) routeService.setPath(defaultRoute, null, {replace: true}) + else throw new Error("Could not resolve default route " + defaultRoute) } routeService.defineRoutes(routes, function(payload, params, path) { var update = lastUpdate = function(routeResolver, comp) { diff --git a/api/tests/test-router.js b/api/tests/test-router.js index 8fec9042..9b8705da 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -108,7 +108,7 @@ o.spec("route", function() { done() } }) - + o("redraws when render function is executed", function() { var onupdate = o.spy() var oninit = o.spy() diff --git a/mithril.js b/mithril.js index c1ec91f0..3ccb4917 100644 --- a/mithril.js +++ b/mithril.js @@ -1056,7 +1056,6 @@ var coreRouter = function($window) { var path = router.getPath() var params = {} var pathname = parsePath(path, params, params) - var state = $window.history.state if (state != null) { for (var k in state) params[k] = state[k] @@ -1077,12 +1076,10 @@ var coreRouter = function($window) { } reject(path, params) } - if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute) else if (router.prefix.charAt(0) === "#") $window.onhashchange = resolveRoute resolveRoute() } - return router } var _20 = function($window, redrawService0) { @@ -1094,8 +1091,9 @@ var _20 = function($window, redrawService0) { var run1 = function() { if (render1 != null) redrawService0.render(root, render1(Vnode(component, attrs3.key, attrs3))) } - var bail = function() { - routeService.setPath(defaultRoute, null, {replace: true}) + var bail = function(path) { + if (path !== defaultRoute) routeService.setPath(defaultRoute, null, {replace: true}) + else throw new Error("Could not resolve default route " + defaultRoute) } routeService.defineRoutes(routes, function(payload, params, path) { var update = lastUpdate = function(routeResolver, comp) { diff --git a/mithril.min.js b/mithril.min.js index 528305de..eaf10ad2 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,42 +1,42 @@ -new function(){function x(a,c,k,d,h,m){return{tag:a,key:c,attrs:k,children:d,text:h,dom:m,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function B(a){if(null==a||"string"!==typeof a&&"function"!==typeof a.view)throw Error("The selector must be either a string or a component.");if("string"===typeof a&&void 0===G[a]){for(var c,k,d=[],h={};c=N.exec(a);){var m=c[1],l=c[2];""===m&&""!==l?k=l:"#"===m?h.id=l:"."===m?d.push(l):"["===c[3][0]&&((m=c[6])&&(m=m.replace(/\\(["'])/g,"$1").replace(/\\\\/g, -"\\")),"class"===c[4]?d.push(m):h[c[4]]=m||!0)}0b.indexOf("?")?"?":"&";b+=d+c}return b}function l(a){try{return""!==a?JSON.parse(a):null}catch(A){throw Error(a);}} -function p(a){return a.responseText}function r(a,c){if("function"===typeof a)if(Array.isArray(c))for(var b=0;bn.status||304===n.status)c(r(b.type,a));else{var g=Error(n.responseText),e;for(e in a)g[e]= -a[e];d(g)}}catch(f){d(f)}};k&&null!=b.data?n.send(b.data):n.send()});return!0===b.background?A:q(A)},jsonp:function(b,l){var p=k();b=d(b,l);var u=new c(function(c,d){var k=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+q++,l=a.document.createElement("script");a[k]=function(d){l.parentNode.removeChild(l);c(r(b.type,d));delete a[k]};l.onerror=function(){l.parentNode.removeChild(l);d(Error("JSONP request failed"));delete a[k]};null==b.data&&(b.data={});b.url=h(b.url,b.data);b.data[b.callbackKey|| -"callback"]=k;l.src=m(b.url,b.data);a.document.documentElement.appendChild(l)});return!0===b.background?u:p(u)},setCompletionCallback:function(a){u=a}}}(window,w),M=function(a){function c(g,e,a,b,c,d,h){for(;a=t&&A>=n;){var y=e[t],v=a[n];if(y!==v||f)if(null==y)t++;else if(null==v)n++;else if(y.key===v.key)t++,n++,m(g,y,v,b,p(e,t,d),f,h),f&&y.tag===v.tag&&r(g,l(y),d);else if(y=e[q],y!==v||f)if(null==y)q--;else if(null==v)n++;else if(y.key===v.key)m(g,y,v,b,p(e, -q+1,d),f,h),(f||n=t&&A>=n;){y=e[q];v=a[A];if(y!==v||f)if(null==y)q--;else{if(null!=v)if(y.key===v.key)m(g,y,v,b,p(e,q+1,d),f,h),f&&y.tag===v.tag&&r(g,l(y),d),null!=y.dom&&(d=y.dom),q--;else{if(!z){z=e;var y=q,C={},x;for(x=0;xb.indexOf("?")?"?":"&";b+=d+c}return b}function l(b){try{return""!==b?JSON.parse(b):null}catch(z){throw Error(b);}} +function p(b){return b.responseText}function u(b,a){if("function"===typeof b)if(Array.isArray(a))for(var c=0;cq.status||304===q.status)c(u(b.type,a));else{var g=Error(q.responseText),e;for(e in a)g[e]= +a[e];d(g)}}catch(f){d(f)}};k&&null!=b.data?q.send(b.data):q.send()});return!0===b.background?z:r(z)},jsonp:function(b,l){var p=k();b=d(b,l);var v=new c(function(c,d){var k=b.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+r++,l=a.document.createElement("script");a[k]=function(d){l.parentNode.removeChild(l);c(u(b.type,d));delete a[k]};l.onerror=function(){l.parentNode.removeChild(l);d(Error("JSONP request failed"));delete a[k]};null==b.data&&(b.data={});b.url=h(b.url,b.data);b.data[b.callbackKey|| +"callback"]=k;l.src=n(b.url,b.data);a.document.documentElement.appendChild(l)});return!0===b.background?v:p(v)},setCompletionCallback:function(a){v=a}}}(window,m),M=function(a){function c(g,e,a,b,c,d,h){for(;a=t&&z>=q;){var y=e[t],w=a[q];if(y!==w||f)if(null==y)t++;else if(null==w)q++;else if(y.key===w.key)t++,q++,n(g,y,w,b,p(e,t,d),f,h),f&&y.tag===w.tag&&u(g,l(y),d);else if(y=e[r],y!==w||f)if(null==y)r--;else if(null==w)q++;else if(y.key===w.key)n(g,y,w,b,p(e, +r+1,d),f,h),(f||q=t&&z>=q;){y=e[r];w=a[z];if(y!==w||f)if(null==y)r--;else{if(null!=w)if(y.key===w.key)n(g,y,w,b,p(e,r+1,d),f,h),f&&y.tag===w.tag&&u(g,l(y),d),null!=y.dom&&(d=y.dom),r--;else{if(!D){D=e;var y=r,B={},m;for(m=0;m Date: Fri, 3 Feb 2017 22:56:34 -0500 Subject: [PATCH 003/113] add docs about mixed keys --- api/redraw.js | 2 +- docs/keys.md | 27 ++++++++++++++++++++++++++- mithril.js | 1 - 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/api/redraw.js b/api/redraw.js index 3f98b061..e975d286 100644 --- a/api/redraw.js +++ b/api/redraw.js @@ -28,7 +28,7 @@ module.exports = function($window) { renderService.setEventCallback(function(e) { if (e.redraw !== false) redraw() }) - + var callbacks = [] function subscribe(key, callback) { unsubscribe(key) diff --git a/docs/keys.md b/docs/keys.md index 0149418c..2c1e46d7 100644 --- a/docs/keys.md +++ b/docs/keys.md @@ -3,7 +3,6 @@ - [What are keys](#what-are-keys) - [How to use](#how-to-use) - [Debugging key related issues](#debugging-key-related-issues) -- [Avoid anti-patterns](#avoid-anti-patterns) --- @@ -152,3 +151,29 @@ var things = [ ] ``` +#### Avoid mixing keyed and non-keyed vnodes in the same array + +An array of vnodes must have only keyed vnodes or non-keyed vnodes, but not both. If you need to mix them, create a nested array. + +```javascript +// AVOID +m("div", [ + m("div", "a"), + m("div", {key: 1}, "b"), +]) + +// PREFER +m("div", [ + m("div", {key: 0}, "a"), + m("div", {key: 1}, "b"), +]) + + +// PREFER +m("div", [ + m("div", "a"), + [ + m("div", {key: 1}, "b"), + ] +]) +``` diff --git a/mithril.js b/mithril.js index 3ccb4917..d92059ec 100644 --- a/mithril.js +++ b/mithril.js @@ -918,7 +918,6 @@ var _11 = function($window) { renderService.setEventCallback(function(e) { if (e.redraw !== false) redraw() }) - var callbacks = [] function subscribe(key1, callback) { unsubscribe(key1) From e3b0861b8a494e0f121b61e754cd744a87ff6e97 Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 3 Feb 2017 23:06:22 -0500 Subject: [PATCH 004/113] clean doc artifacts --- docs/generate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generate.js b/docs/generate.js index 9d694b87..bcf5465e 100644 --- a/docs/generate.js +++ b/docs/generate.js @@ -25,7 +25,7 @@ function generate(pathname) { generate(pathname + "/" + filename) }) } - else if (!pathname.match(/tutorials|archive/)) { + else if (!pathname.match(/tutorials|archive|guides|methods/)) { if (pathname.match(/\.md$/)) { var outputFilename = pathname.replace(/\.md$/, ".html") var markdown = fs.readFileSync(pathname, "utf-8") From 23a1693c02d242e6eb68dcb3c2d6bbe4acc7b995 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Mon, 6 Feb 2017 14:35:25 -0800 Subject: [PATCH 005/113] docs: add m.route.build/parseQueryString to changelog (#1594) --- docs/change-log.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/change-log.md b/docs/change-log.md index d0de73bc..f68c9f77 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -28,6 +28,7 @@ If you are migrating, consider using the [mithril-codemods](https://www.npmjs.co - [`m.route` and anchor tags](#mroute-and-anchor-tags) - [Reading/writing the current route](#readingwriting-the-current-route) - [Accessing route params](#accessing-route-params) +- [Building/Parsing query strings](#buildingparsing-query-strings) - [Preventing unmounting](#preventing-unmounting) - [Run code on component removal](#run-code-on-component-removal) - [`m.request`](#mrequest) @@ -459,6 +460,28 @@ m.route(document.body, "/booga", { --- +## Building/Parsing query strings + +`v0.2.x` used methods hanging off of `m.route`, `m.route.buildQueryString()` and `m.route.parseQueryString()`. In `v1.x` these have been broken out and attached to the root `m`. + +### `v0.2.x` + +```javascript +var qs = m.route.buildQueryString({ a : 1 }); + +var obj = m.route.parseQueryString("a=1"); +``` + +### `v1.x` + +```javascript +var qs = m.buildQueryString({ a : 1 }); + +var obj = m.parseQueryString("a=1"); +``` + +--- + ## Preventing unmounting It is no longer possible to prevent unmounting via `onunload`'s `e.preventDefault()`. Instead you should explicitly call `m.route.set` when the expected conditions are met. From 5ab2cf4172f6425f080c8ffda66c6093d6077c02 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Mon, 6 Feb 2017 14:46:31 -0800 Subject: [PATCH 006/113] fix: Use loose comparison for non-string values fixes #1593 --- render/render.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/render/render.js b/render/render.js index 4e993f59..449f380b 100644 --- a/render/render.js +++ b/render/render.js @@ -438,11 +438,11 @@ module.exports = function($window) { else if (key === "style") updateStyle(element, old, value) else if (key in element && !isAttribute(key) && ns === undefined && !isCustomElement(vnode)) { //setting input[value] to same value by typing on focused element moves cursor to end in Chrome - if (vnode.tag === "input" && key === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return + if (vnode.tag === "input" && key === "value" && vnode.dom.value == value && vnode.dom === $doc.activeElement) return //setting select[value] to same value while having select open blinks select dropdown in Chrome - if (vnode.tag === "select" && key === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return + if (vnode.tag === "select" && key === "value" && vnode.dom.value == value && vnode.dom === $doc.activeElement) return //setting option[value] to same value while having select open blinks select dropdown in Chrome - if (vnode.tag === "option" && key === "value" && vnode.dom.value === value) return + if (vnode.tag === "option" && key === "value" && vnode.dom.value == value) return element[key] = value } else { From 390e1fe3434ab78b7eed770651db2ae256b646cf Mon Sep 17 00:00:00 2001 From: Gyandeep Singh Date: Tue, 7 Feb 2017 20:55:53 -0600 Subject: [PATCH 007/113] Update: Add elem to parent before adding its children --- render/render.js | 50 +++++++++++++++++++++---------------- render/tests/test-oninit.js | 2 +- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/render/render.js b/render/render.js index 4e993f59..9f99979a 100644 --- a/render/render.js +++ b/render/render.js @@ -14,30 +14,32 @@ module.exports = function($window) { for (var i = start; i < end; i++) { var vnode = vnodes[i] if (vnode != null) { - insertNode(parent, createNode(vnode, hooks, ns), nextSibling) + createNode(parent, vnode, hooks, ns, nextSibling) } } } - function createNode(vnode, hooks, ns) { + function createNode(parent, vnode, hooks, ns, nextSibling) { var tag = vnode.tag if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) if (typeof tag === "string") { switch (tag) { - case "#": return createText(vnode) - case "<": return createHTML(vnode) - case "[": return createFragment(vnode, hooks, ns) - default: return createElement(vnode, hooks, ns) + case "#": return createText(parent, vnode, nextSibling) + case "<": return createHTML(parent, vnode, nextSibling) + case "[": return createFragment(parent, vnode, hooks, ns, nextSibling) + default: return createElement(parent, vnode, hooks, ns, nextSibling) } } - else return createComponent(vnode, hooks, ns) + else return createComponent(parent, vnode, hooks, ns, nextSibling) } - function createText(vnode) { - return vnode.dom = $doc.createTextNode(vnode.children) + function createText(parent, vnode, nextSibling) { + vnode.dom = $doc.createTextNode(vnode.children) + insertNode(parent, vnode.dom, nextSibling) + return vnode.dom } - function createHTML(vnode) { + function createHTML(parent, vnode, nextSibling) { var match = vnode.children.match(/^\s*?<(\w+)/im) || [] - var parent = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match[1]] || "div" - var temp = $doc.createElement(parent) + var parent1 = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match[1]] || "div" + var temp = $doc.createElement(parent1) temp.innerHTML = vnode.children vnode.dom = temp.firstChild @@ -47,9 +49,10 @@ module.exports = function($window) { while (child = temp.firstChild) { fragment.appendChild(child) } + insertNode(parent, fragment, nextSibling) return fragment } - function createFragment(vnode, hooks, ns) { + function createFragment(parent, vnode, hooks, ns, nextSibling) { var fragment = $doc.createDocumentFragment() if (vnode.children != null) { var children = vnode.children @@ -57,9 +60,10 @@ module.exports = function($window) { } vnode.dom = fragment.firstChild vnode.domSize = fragment.childNodes.length + insertNode(parent, fragment, nextSibling) return fragment } - function createElement(vnode, hooks, ns) { + function createElement(parent, vnode, hooks, ns, nextSibling) { var tag = vnode.tag switch (vnode.tag) { case "svg": ns = "http://www.w3.org/2000/svg"; break @@ -78,6 +82,8 @@ module.exports = function($window) { setAttrs(vnode, attrs, ns) } + insertNode(parent, element, nextSibling) + if (vnode.attrs != null && vnode.attrs.contenteditable != null) { setContentEditable(vnode) } @@ -94,7 +100,7 @@ module.exports = function($window) { } return element } - function createComponent(vnode, hooks, ns) { + function createComponent(parent, vnode, hooks, ns, nextSibling) { vnode.state = Object.create(vnode.tag) var view = vnode.tag.view if (view.reentrantLock != null) return $emptyFragment @@ -104,9 +110,10 @@ module.exports = function($window) { view.reentrantLock = null if (vnode.instance != null) { if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as arguments") - var element = createNode(vnode.instance, hooks, ns) + var element = createNode(parent, vnode.instance, hooks, ns, nextSibling) vnode.dom = vnode.instance.dom vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0 + insertNode(parent, element, nextSibling) return element } else { @@ -132,7 +139,7 @@ module.exports = function($window) { if (isUnkeyed) { for (var i = 0; i < old.length; i++) { if (old[i] === vnodes[i]) continue - else if (old[i] == null && vnodes[i] != null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling)) + else if (old[i] == null && vnodes[i] != null) createNode(parent, vnodes[i], hooks, ns, getNextSibling(old, i + 1, nextSibling)) else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes) else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns) } @@ -189,8 +196,7 @@ module.exports = function($window) { if (movable.dom != null) nextSibling = movable.dom } else { - var dom = createNode(v, hooks, undefined) - insertNode(parent, dom, nextSibling) + var dom = createNode(parent, v, hooks, undefined, nextSibling) nextSibling = dom } } @@ -223,7 +229,7 @@ module.exports = function($window) { } else { removeNode(old, null) - insertNode(parent, createNode(vnode, hooks, ns), nextSibling) + createNode(parent, vnode, hooks, ns, nextSibling) } } function updateText(old, vnode) { @@ -235,7 +241,7 @@ module.exports = function($window) { function updateHTML(parent, old, vnode, nextSibling) { if (old.children !== vnode.children) { toFragment(old) - insertNode(parent, createHTML(vnode), nextSibling) + createHTML(parent, vnode, nextSibling) } else vnode.dom = old.dom, vnode.domSize = old.domSize } @@ -284,7 +290,7 @@ module.exports = function($window) { vnode.instance = Vnode.normalize(vnode.tag.view.call(vnode.state, vnode)) updateLifecycle(vnode.tag, vnode, hooks, recycling) if (vnode.instance != null) { - if (old.instance == null) insertNode(parent, createNode(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) vnode.dom = vnode.instance.dom vnode.domSize = vnode.instance.domSize diff --git a/render/tests/test-oninit.js b/render/tests/test-oninit.js index 1f0e7701..3265cd8b 100644 --- a/render/tests/test-oninit.js +++ b/render/tests/test-oninit.js @@ -187,7 +187,7 @@ o.spec("oninit", function() { called = true o(vnode.dom).equals(undefined) - o(root.childNodes.length).equals(0) + o(root.childNodes.length).equals(1) } o(called).equals(true) }) From c7e0b204f8de9b6a9a4fc4a0dd397d1f09ba5c13 Mon Sep 17 00:00:00 2001 From: Yoshiki Shibukawa Date: Wed, 8 Feb 2017 02:02:39 -0500 Subject: [PATCH 008/113] docs: Update route.md (#1600) Fix typo --- docs/route.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/route.md b/docs/route.md index 6e0ff744..b2d357df 100644 --- a/docs/route.md +++ b/docs/route.md @@ -385,7 +385,7 @@ var Layout = { } ``` -In the example above, the layout merely consists of a `
` that contains the children passed to the component, but in a real life scenario it could be as complex as neeeded. +In the example above, the layout merely consists of a `
` that contains the children passed to the component, but in a real life scenario it could be as complex as needed. One way to wrap the layout is to define an anonymous component in the routes map: From 153161b849981c210a9c7c2436c33f940416a9fb Mon Sep 17 00:00:00 2001 From: Barry Els Date: Wed, 8 Feb 2017 21:30:34 +0200 Subject: [PATCH 009/113] docs: Minor grammar and link bait update :) (#1602) --- docs/framework-comparison.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/framework-comparison.md b/docs/framework-comparison.md index 92573f40..4e4bd7f5 100644 --- a/docs/framework-comparison.md +++ b/docs/framework-comparison.md @@ -22,7 +22,7 @@ However, if you're starting something new, do consider giving Mithril a try, if ## Why use Mithril? -In one sentence: because **Mithril is pragmatic**. The 10 minutes [guide](index.md) is a good example: that's how long it takes to learn components, XHR and routing - and that's just about the right amount of knowledge needed to build useful applications. +In one sentence: because **Mithril is pragmatic**. This [10 minute guide](index.md) is a good example: that's how long it takes to learn components, XHR and routing - and that's just about the right amount of knowledge needed to build useful applications. Mithril is all about getting meaningful work done efficiently. Doing file uploads? [The docs show you how](request.md#file-uploads). Authentication? [Documented too](route.md#authentication). Exit animations? [You got it](animation.md). No extra libraries, no magic. From 1f38233a47099517af45414deceb5a03bcd7e272 Mon Sep 17 00:00:00 2001 From: "Luiz Paulo \"Bills" Date: Wed, 8 Feb 2017 21:41:00 -0200 Subject: [PATCH 010/113] docs: better example codes (#1606) --- docs/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index b95e83ff..94c4180c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -54,7 +54,7 @@ Let's create an HTML file to follow along: ```markup - + + + + diff --git a/test-utils/tests/test-components.js b/test-utils/tests/test-components.js new file mode 100644 index 00000000..cc9a091b --- /dev/null +++ b/test-utils/tests/test-components.js @@ -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)) + }) +}) From d7f39a8254fe269042f5bdf84d1286203c044d84 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 00:01:05 +0100 Subject: [PATCH 041/113] Tests: group together tests with/without components --- api/tests/test-mount.js | 8 +- render/tests/test-onbeforeupdate.js | 127 ++++++++++++++-------------- render/tests/test-onremove.js | 65 +++++++------- render/tests/test-updateNodes.js | 67 +++++++-------- tests/test-api.js | 20 ++--- 5 files changed, 145 insertions(+), 142 deletions(-) diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js index 038f470a..dcee259a 100644 --- a/api/tests/test-mount.js +++ b/api/tests/test-mount.js @@ -22,20 +22,20 @@ o.spec("mount", function() { render = coreRenderer($window).render }) - o("throws on invalid `root` DOM node", function() { + o("throws on invalid component", function() { var threw = false try { - mount(null, {view: function() {}}) + mount(root, {}) } catch (e) { threw = true } o(threw).equals(true) }) - o("throws on invalid component", function() { + o("throws on invalid `root` DOM node", function() { var threw = false try { - mount(root, {}) + mount(null, {view: function() {}}) } catch (e) { threw = true } diff --git a/render/tests/test-onbeforeupdate.js b/render/tests/test-onbeforeupdate.js index 8b326b8c..2844e8ee 100644 --- a/render/tests/test-onbeforeupdate.js +++ b/render/tests/test-onbeforeupdate.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -56,6 +57,69 @@ o.spec("onbeforeupdate", function() { o(root.firstChild.nodeValue).equals("a") }) + o("does not prevent update if returning true", function() { + var onbeforeupdate = function() {return true} + var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} + var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} + + render(root, [vnode]) + render(root, [updated]) + + o(root.firstChild.attributes["id"].nodeValue).equals("b") + }) + + o("accepts arguments for comparison", function() { + var count = 0 + var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} + var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} + + 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() { + var count = 0 + var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} + var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} + + render(root, [vnode]) + + function onbeforeupdate(vnode, old) { + count++ + return true + } + + o(count).equals(0) + }) + + o("is called only once on update", function() { + var count = 0 + var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} + var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} + + render(root, [vnode]) + render(root, [updated]) + + function onbeforeupdate(vnode, old) { + count++ + return true + } + + o(count).equals(1) + }) + o("prevents update in component", function() { var component = { onbeforeupdate: function() {return false}, @@ -136,17 +200,6 @@ o.spec("onbeforeupdate", function() { o(root.firstChild.attributes["id"].nodeValue).equals("b") }) - o("does not prevent update if returning true", function() { - var onbeforeupdate = function() {return true} - var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} - var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} - - 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 = { onbeforeupdate: function() {return true}, @@ -163,27 +216,6 @@ o.spec("onbeforeupdate", function() { o(root.firstChild.attributes["id"].nodeValue).equals("b") }) - o("accepts arguments for comparison", function() { - var count = 0 - var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} - var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} - - 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("accepts arguments for comparison in component", function() { var component = { onbeforeupdate: onbeforeupdate, @@ -211,21 +243,6 @@ o.spec("onbeforeupdate", function() { o(root.firstChild.attributes["id"].nodeValue).equals("b") }) - o("is not called on creation", function() { - var count = 0 - var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} - var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} - - render(root, [vnode]) - - function onbeforeupdate(vnode, old) { - count++ - return true - } - - o(count).equals(0) - }) - o("is not called on component creation", function() { var component = { onbeforeupdate: onbeforeupdate, @@ -248,22 +265,6 @@ o.spec("onbeforeupdate", function() { o(count).equals(0) }) - o("is called only once on update", function() { - var count = 0 - var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} - var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} - - render(root, [vnode]) - render(root, [updated]) - - function onbeforeupdate(vnode, old) { - count++ - return true - } - - o(count).equals(1) - }) - o("is called only once on component update", function() { var component = { onbeforeupdate: onbeforeupdate, diff --git a/render/tests/test-onremove.js b/render/tests/test-onremove.js index d3f423fc..8aa2eabd 100644 --- a/render/tests/test-onremove.js +++ b/render/tests/test-onremove.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") var m = require("../../render/hyperscript") @@ -80,6 +81,38 @@ o.spec("onremove", function() { o(remove.this).equals(vnode.state) o(remove.args[0]).equals(vnode) }) + o("does not set onremove as an event handler", function() { + var remove = o.spy() + var vnode = {tag: "div", attrs: {onremove: remove}, children: []} + + render(root, [vnode]) + + o(vnode.dom.onremove).equals(undefined) + o(vnode.dom.attributes["onremove"]).equals(undefined) + }) + o("calls onremove on recycle", function() { + var remove = o.spy() + var vnodes = [{tag: "div", key: 1}] + var temp = [{tag: "div", key: 2, attrs: {onremove: remove}}] + var updated = [{tag: "div", key: 1}] + + render(root, vnodes) + render(root, temp) + render(root, updated) + + o(remove.callCount).equals(1) + }) + o("does not recycle when there's an onremove", function() { + var remove = o.spy() + var vnode = {tag: "div", key: 1, attrs: {onremove: remove}} + var updated = {tag: "div", key: 1, attrs: {onremove: remove}} + + render(root, [vnode]) + render(root, []) + render(root, [updated]) + + o(vnode.dom).notEquals(updated.dom) + }) o("calls onremove on nested component", function() { var spy = o.spy() var comp = { @@ -113,36 +146,4 @@ o.spec("onremove", function() { o(spy.callCount).equals(1) }) - o("does not set onremove as an event handler", function() { - var remove = o.spy() - var vnode = {tag: "div", attrs: {onremove: remove}, children: []} - - render(root, [vnode]) - - o(vnode.dom.onremove).equals(undefined) - o(vnode.dom.attributes["onremove"]).equals(undefined) - }) - o("calls onremove on recycle", function() { - var remove = o.spy() - var vnodes = [{tag: "div", key: 1}] - var temp = [{tag: "div", key: 2, attrs: {onremove: remove}}] - var updated = [{tag: "div", key: 1}] - - render(root, vnodes) - render(root, temp) - render(root, updated) - - o(remove.callCount).equals(1) - }) - o("does not recycle when there's an onremove", function() { - var remove = o.spy() - var vnode = {tag: "div", key: 1, attrs: {onremove: remove}} - var updated = {tag: "div", key: 1, attrs: {onremove: remove}} - - render(root, [vnode]) - render(root, []) - render(root, [updated]) - - o(vnode.dom).notEquals(updated.dom) - }) }) diff --git a/render/tests/test-updateNodes.js b/render/tests/test-updateNodes.js index b0e8c337..c7b7095c 100644 --- a/render/tests/test-updateNodes.js +++ b/render/tests/test-updateNodes.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -838,38 +839,6 @@ o.spec("updateNodes", function() { o(root.childNodes[0].nodeName).equals("A") 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 () { var onupdate = o.spy(); var cached = {tag:"a", attrs:{onupdate: onupdate}} @@ -926,7 +895,7 @@ o.spec("updateNodes", function() { o(update.callCount).equals(2) 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 updated = {tag: "b"} @@ -936,4 +905,36 @@ o.spec("updateNodes", function() { o(vnode.dom).notEquals(updated.dom) }) + 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") + }) }) diff --git a/tests/test-api.js b/tests/test-api.js index 989b00a4..59e3eb23 100644 --- a/tests/test-api.js +++ b/tests/test-api.js @@ -68,6 +68,16 @@ o.spec("api", function() { 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("works", function() { var root = window.document.createElement("div") @@ -157,14 +167,4 @@ o.spec("api", function() { }, 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 - }) - }) }) From a8e5189f0bd75d36ab1e98c62bc953f043a84f40 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 00:14:24 +0100 Subject: [PATCH 042/113] Tests: factory => closure components --- api/tests/test-mount.js | 2 +- api/tests/test-router.js | 2 +- render/tests/test-render.js | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js index dcee259a..5afd9c39 100644 --- a/api/tests/test-mount.js +++ b/api/tests/test-mount.js @@ -60,7 +60,7 @@ o.spec("mount", function() { o(root.firstChild.nodeName).equals("DIV") }) - o("renders into `root` factory (factory component)", function() { + o("renders into `root` (closure component)", function() { mount(root, function(){ return { view : function() { diff --git a/api/tests/test-router.js b/api/tests/test-router.js index c624789b..f43605e1 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -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() function Cmp() {return {view: view}} diff --git a/render/tests/test-render.js b/render/tests/test-render.js index 2f3ebb9a..82e5ddba 100644 --- a/render/tests/test-render.js +++ b/render/tests/test-render.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -118,7 +119,7 @@ o.spec("render", function() { o(oninit.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 onbeforeupdate = o.spy() function A() { @@ -141,7 +142,7 @@ o.spec("render", function() { o(oninit.callCount).equals(1) 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 onbeforeupdate = o.spy() function A() { @@ -164,7 +165,7 @@ o.spec("render", function() { o(oninit.callCount).equals(1) 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() { throw new Error("error") } From f96319e6f64ed690e2a8d2b2630c64d362a64657 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 00:33:01 +0100 Subject: [PATCH 043/113] Tests: add dummy forEach blocks around sections with components --- api/tests/test-mount.js | 451 ++++----- render/tests/test-component.js | 1335 ++++++++++++++------------- render/tests/test-onbeforeremove.js | 68 +- render/tests/test-onbeforeupdate.js | 336 +++---- render/tests/test-onremove.js | 72 +- render/tests/test-updateNodes.js | 66 +- tests/test-api.js | 163 ++-- 7 files changed, 1268 insertions(+), 1223 deletions(-) diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js index 5afd9c39..86ae3835 100644 --- a/api/tests/test-mount.js +++ b/api/tests/test-mount.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var m = require("../../render/hyperscript") @@ -32,237 +33,243 @@ o.spec("mount", function() { o(threw).equals(true) }) - o("throws on invalid `root` DOM node", function() { - var threw = false - try { - mount(null, {view: function() {}}) - } catch (e) { - threw = true - } - o(threw).equals(true) - }) + ;[components[0]].forEach(function(cmp){ + o.spec(cmp.kind, function(){ + var createComponent = cmp.create - o("renders into `root` (POJO component)", function() { - mount(root, { - view : function() { - 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` (closure component)", function() { - mount(root, function(){ - return { - view : function() { - return m("div") + 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(root.firstChild.nodeName).equals("DIV") - }) - - o("mounting null unmounts", function() { - mount(root, { - view : function() { - return m("div") - } - }) - - mount(root, null) - - o(root.childNodes.length).equals(0) - }) - - o("redraws on events", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var onclick = o.spy() - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - mount(root, { - view : function() { - return m("div", { - oninit : oninit, - onupdate : onupdate, - onclick : onclick, - }) - } - }) - - root.firstChild.dispatchEvent(e) - - o(oninit.callCount).equals(1) - o(onupdate.callCount).equals(0) - - o(onclick.callCount).equals(1) - o(onclick.this).equals(root.firstChild) - o(onclick.args[0].type).equals("click") - o(onclick.args[0].target).equals(root.firstChild) - - // Wrapped to give time for the rate-limited redraw to fire - setTimeout(function() { - o(onupdate.callCount).equals(1) - - done() - }, FRAME_BUDGET) - }) - - o("redraws several mount points on events", function(done, timeout) { - timeout(60) - - var onupdate0 = o.spy() - var oninit0 = o.spy() - var onclick0 = o.spy() - var onupdate1 = o.spy() - var oninit1 = o.spy() - var onclick1 = o.spy() - - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - render(root, [ - m("#child0"), - m("#child1") - ]) - - mount(root.childNodes[0], { - view : function() { - return m("div", { - oninit : oninit0, - onupdate : onupdate0, - onclick : onclick0, - }) - } - }) - - o(oninit0.callCount).equals(1) - o(onupdate0.callCount).equals(0) - - mount(root.childNodes[1], { - view : function() { - return m("div", { - oninit : oninit1, - onupdate : onupdate1, - onclick : onclick1, - }) - } - }) - - o(oninit1.callCount).equals(1) - o(onupdate1.callCount).equals(0) - - root.childNodes[0].firstChild.dispatchEvent(e) - o(onclick0.callCount).equals(1) - o(onclick0.this).equals(root.childNodes[0].firstChild) - - setTimeout(function() { - o(onupdate0.callCount).equals(1) - o(onupdate1.callCount).equals(1) - - root.childNodes[1].firstChild.dispatchEvent(e) - o(onclick1.callCount).equals(1) - o(onclick1.this).equals(root.childNodes[1].firstChild) - - setTimeout(function() { - o(onupdate0.callCount).equals(2) - o(onupdate1.callCount).equals(2) - - done() - }, FRAME_BUDGET) - }, FRAME_BUDGET) - - }) - - o("event handlers can skip redraw", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - mount(root, { - view: function() { - return m("div", { - oninit: oninit, - onupdate: onupdate, - onclick: function(e) { - e.redraw = false + o("renders into `root` (POJO component)", function() { + mount(root, { + view : function() { + return m("div") } }) - } - }) - root.firstChild.dispatchEvent(e) + o(root.firstChild.nodeName).equals("DIV") + }) - o(oninit.callCount).equals(1) + o("renders into `root` (class component)", function() { + function Cmp(){} + Cmp.prototype.view = function(){return m("div")} + mount(root, Cmp) - // Wrapped to ensure no redraw fired - setTimeout(function() { - o(onupdate.callCount).equals(0) + o(root.firstChild.nodeName).equals("DIV") + }) - done() - }, FRAME_BUDGET) - }) - - o("redraws when the render function is run", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - - mount(root, { - view : function() { - return m("div", { - oninit: oninit, - onupdate: onupdate + o("renders into `root` (closure component)", function() { + mount(root, function(){ + return { + view : function() { + return m("div") + } + } }) - } + + o(root.firstChild.nodeName).equals("DIV") + }) + + o("mounting null unmounts", function() { + mount(root, { + view : function() { + return m("div") + } + }) + + mount(root, null) + + o(root.childNodes.length).equals(0) + }) + + o("redraws on events", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var onclick = o.spy() + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + mount(root, { + view : function() { + return m("div", { + oninit : oninit, + onupdate : onupdate, + onclick : onclick, + }) + } + }) + + root.firstChild.dispatchEvent(e) + + o(oninit.callCount).equals(1) + o(onupdate.callCount).equals(0) + + o(onclick.callCount).equals(1) + o(onclick.this).equals(root.firstChild) + o(onclick.args[0].type).equals("click") + o(onclick.args[0].target).equals(root.firstChild) + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, FRAME_BUDGET) + }) + + o("redraws several mount points on events", function(done, timeout) { + timeout(60) + + var onupdate0 = o.spy() + var oninit0 = o.spy() + var onclick0 = o.spy() + var onupdate1 = o.spy() + var oninit1 = o.spy() + var onclick1 = o.spy() + + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + render(root, [ + m("#child0"), + m("#child1") + ]) + + mount(root.childNodes[0], { + view : function() { + return m("div", { + oninit : oninit0, + onupdate : onupdate0, + onclick : onclick0, + }) + } + }) + + o(oninit0.callCount).equals(1) + o(onupdate0.callCount).equals(0) + + mount(root.childNodes[1], { + view : function() { + return m("div", { + oninit : oninit1, + onupdate : onupdate1, + onclick : onclick1, + }) + } + }) + + o(oninit1.callCount).equals(1) + o(onupdate1.callCount).equals(0) + + root.childNodes[0].firstChild.dispatchEvent(e) + o(onclick0.callCount).equals(1) + o(onclick0.this).equals(root.childNodes[0].firstChild) + + setTimeout(function() { + o(onupdate0.callCount).equals(1) + o(onupdate1.callCount).equals(1) + + root.childNodes[1].firstChild.dispatchEvent(e) + o(onclick1.callCount).equals(1) + o(onclick1.this).equals(root.childNodes[1].firstChild) + + setTimeout(function() { + o(onupdate0.callCount).equals(2) + o(onupdate1.callCount).equals(2) + + done() + }, FRAME_BUDGET) + }, FRAME_BUDGET) + + }) + + o("event handlers can skip redraw", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + mount(root, { + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate, + onclick: function(e) { + e.redraw = false + } + }) + } + }) + + root.firstChild.dispatchEvent(e) + + o(oninit.callCount).equals(1) + + // Wrapped to ensure no redraw fired + setTimeout(function() { + o(onupdate.callCount).equals(0) + + done() + }, FRAME_BUDGET) + }) + + o("redraws when the render function is run", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + + mount(root, { + view : function() { + return m("div", { + oninit: oninit, + onupdate: onupdate + }) + } + }) + + o(oninit.callCount).equals(1) + o(onupdate.callCount).equals(0) + + redrawService.redraw() + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, FRAME_BUDGET) + }) + + o("throttles", function(done, timeout) { + timeout(200) + + var i = 0 + mount(root, {view: function() {i++}}) + var before = i + + redrawService.redraw() + redrawService.redraw() + redrawService.redraw() + redrawService.redraw() + + var after = i + + setTimeout(function(){ + o(before).equals(1) // mounts synchronously + o(after).equals(1) // throttles rest + o(i).equals(2) + done() + },40) + }) }) - - o(oninit.callCount).equals(1) - o(onupdate.callCount).equals(0) - - redrawService.redraw() - - // Wrapped to give time for the rate-limited redraw to fire - setTimeout(function() { - o(onupdate.callCount).equals(1) - - done() - }, FRAME_BUDGET) }) - - o("throttles", function(done, timeout) { - timeout(200) - - var i = 0 - mount(root, {view: function() {i++}}) - var before = i - - redrawService.redraw() - redrawService.redraw() - redrawService.redraw() - redrawService.redraw() - - var after = i - - setTimeout(function(){ - o(before).equals(1) // mounts synchronously - o(after).equals(1) // throttles rest - o(i).equals(2) - done() - },40) - }) -}) +}) \ No newline at end of file diff --git a/render/tests/test-component.js b/render/tests/test-component.js index ed70fe7d..0f2ceaa3 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -13,689 +14,695 @@ o.spec("component", function() { render = vdom($window).render }) - o.spec("basics", function() { - o("works", function() { - var component = { - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - var node = {tag: component} + ;[components[0]].forEach(function(cmp){ + o.spec(cmp.kind, function(){ + var createComponent = cmp.create - render(root, [node]) + o.spec("basics", function() { + o("works", function() { + var component = { + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + } + var node = {tag: component} - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("receives arguments", function() { - var component = { - view: function(vnode) { - return {tag: "div", attrs: vnode.attrs, text: vnode.text} - } - } - var node = {tag: component, attrs: {id: "a"}, text: "b"} + render(root, [node]) - render(root, [node]) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("receives arguments", function() { + var component = { + view: function(vnode) { + return {tag: "div", attrs: vnode.attrs, text: vnode.text} + } + } + var node = {tag: component, attrs: {id: "a"}, text: "b"} - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("updates", function() { - var component = { - view: function(vnode) { - return {tag: "div", attrs: vnode.attrs, text: vnode.text} - } - } - render(root, [{tag: component, attrs: {id: "a"}, text: "b"}]) - render(root, [{tag: component, attrs: {id: "c"}, text: "d"}]) + render(root, [node]) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("c") - o(root.firstChild.firstChild.nodeValue).equals("d") - }) - o("updates root from null", function() { - var visible = false - var component = { - view: function(vnode) { - return visible ? {tag: "div"} : null - } - } - render(root, [{tag: component}]) - visible = true - render(root, [{tag: component}]) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("updates", function() { + var component = { + view: function(vnode) { + return {tag: "div", attrs: vnode.attrs, text: vnode.text} + } + } + render(root, [{tag: component, attrs: {id: "a"}, text: "b"}]) + render(root, [{tag: component, attrs: {id: "c"}, text: "d"}]) - o(root.firstChild.nodeName).equals("DIV") - }) - o("updates root from primitive", function() { - var visible = false - var component = { - view: function(vnode) { - return visible ? {tag: "div"} : false - } - } - render(root, [{tag: component}]) - visible = true - render(root, [{tag: component}]) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("c") + o(root.firstChild.firstChild.nodeValue).equals("d") + }) + o("updates root from null", function() { + var visible = false + var component = { + view: function(vnode) { + return visible ? {tag: "div"} : null + } + } + render(root, [{tag: component}]) + visible = true + render(root, [{tag: component}]) - o(root.firstChild.nodeName).equals("DIV") - }) - o("updates root to null", function() { - var visible = true - var component = { - view: function(vnode) { - return visible ? {tag: "div"} : null - } - } - render(root, [{tag: component}]) - visible = false - render(root, [{tag: component}]) + o(root.firstChild.nodeName).equals("DIV") + }) + o("updates root from primitive", function() { + var visible = false + var component = { + view: function(vnode) { + return visible ? {tag: "div"} : false + } + } + render(root, [{tag: component}]) + visible = true + render(root, [{tag: component}]) - o(root.childNodes.length).equals(0) - }) - o("updates root to primitive", function() { - var visible = true - var component = { - view: function(vnode) { - return visible ? {tag: "div"} : false - } - } - render(root, [{tag: component}]) - visible = false - render(root, [{tag: component}]) + o(root.firstChild.nodeName).equals("DIV") + }) + o("updates root to null", function() { + var visible = true + var component = { + view: function(vnode) { + return visible ? {tag: "div"} : null + } + } + render(root, [{tag: component}]) + visible = false + render(root, [{tag: component}]) - o(root.firstChild.nodeValue).equals("") - }) - o("updates root from null to null", function() { - var component = { - view: function(vnode) { - return null - } - } - render(root, [{tag: component}]) - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("removes", function() { - var component = { - view: function(vnode) { - return {tag: "div"} - } - } - var div = {tag: "div", key: 2} - render(root, [{tag: component, key: 1}, div]) - render(root, [{tag: "div", key: 2}]) - - o(root.childNodes.length).equals(1) - o(root.firstChild).equals(div.dom) - }) - o("svg works when creating across component boundary", function() { - var component = { - view: function(vnode) { - return {tag: "g"} - } - } - render(root, [{tag: "svg", children: [{tag: component}]}]) - - o(root.firstChild.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg") - }) - o("svg works when updating across component boundary", function() { - var component = { - view: function(vnode) { - 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.spec("return value", function() { - o("can return fragments", function() { - var component = { - view: function(vnode) { - return [ - {tag: "label"}, - {tag: "input"}, - ] - } - } - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(2) - o(root.childNodes[0].nodeName).equals("LABEL") - o(root.childNodes[1].nodeName).equals("INPUT") - }) - o("can return string", function() { - var component = { - view: function(vnode) { - return "a" - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("a") - }) - o("can return falsy string", function() { - var component = { - view: function(vnode) { - return "" - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("") - }) - o("can return number", function() { - var component = { - view: function(vnode) { - return 1 - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("1") - }) - o("can return falsy number", function() { - var component = { - view: function(vnode) { - return 0 - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("0") - }) - o("can return boolean", function() { - var component = { - view: function(vnode) { - return true - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("true") - }) - o("can return falsy boolean", function() { - var component = { - view: function(vnode) { - return false - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("") - }) - o("can return null", function() { - var component = { - view: function(vnode) { - return null - } - } - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("can return undefined", function() { - var component = { - view: function(vnode) { - return undefined - } - } - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("throws a custom error if it returns itself", function() { - // A view that returns its vnode would otherwise trigger an infinite loop - var component = { - view: function(vnode) { - return vnode - } - } - try { - render(root, [{tag: component}]) - } - catch (e) { - o(e instanceof Error).equals(true) - // Call stack exception is a RangeError - o(e instanceof RangeError).equals(false) - } - }) - o("can update when returning fragments", function() { - var component = { - view: function(vnode) { - return [ - {tag: "label"}, - {tag: "input"}, - ] - } - } - render(root, [{tag: component}]) - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(2) - o(root.childNodes[0].nodeName).equals("LABEL") - o(root.childNodes[1].nodeName).equals("INPUT") - }) - o("can update when returning primitive", function() { - var component = { - view: function(vnode) { - return "a" - } - } - render(root, [{tag: component}]) - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("a") - }) - o("can update when returning null", function() { - var component = { - view: function(vnode) { - return null - } - } - render(root, [{tag: component}]) - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("can remove when returning fragments", function() { - var component = { - view: function(vnode) { - return [ - {tag: "label"}, - {tag: "input"}, - ] - } - } - var div = {tag: "div", key: 2} - render(root, [{tag: component, key: 1}, div]) - - render(root, [{tag: "div", key: 2}]) - - o(root.childNodes.length).equals(1) - o(root.firstChild).equals(div.dom) - }) - o("can remove when returning primitive", function() { - var component = { - view: function(vnode) { - return "a" - } - } - var div = {tag: "div", key: 2} - render(root, [{tag: component, key: 1}, div]) - - render(root, [{tag: "div", key: 2}]) - - o(root.childNodes.length).equals(1) - o(root.firstChild).equals(div.dom) - }) - }) - o.spec("lifecycle", function() { - o("calls oninit", function() { - var called = 0 - var component = { - oninit: function(vnode) { - called++ - - o(vnode.tag).equals(component) - o(vnode.dom).equals(undefined) o(root.childNodes.length).equals(0) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - var node = {tag: component} + }) + o("updates root to primitive", function() { + var visible = true + var component = { + view: function(vnode) { + return visible ? {tag: "div"} : false + } + } + render(root, [{tag: component}]) + visible = false + render(root, [{tag: component}]) - render(root, [node]) + o(root.firstChild.nodeValue).equals("") + }) + o("updates root from null to null", function() { + var component = { + view: function(vnode) { + return null + } + } + render(root, [{tag: component}]) + render(root, [{tag: component}]) - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls oninit when returning fragment", function() { - var called = 0 - var component = { - oninit: function(vnode) { - called++ - - o(vnode.tag).equals(component) - o(vnode.dom).equals(undefined) o(root.childNodes.length).equals(0) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - var node = {tag: component} + }) + o("removes", function() { + var component = { + view: function(vnode) { + return {tag: "div"} + } + } + var div = {tag: "div", key: 2} + render(root, [{tag: component, key: 1}, div]) + render(root, [{tag: "div", key: 2}]) - render(root, [node]) + o(root.childNodes.length).equals(1) + o(root.firstChild).equals(div.dom) + }) + o("svg works when creating across component boundary", function() { + var component = { + view: function(vnode) { + return {tag: "g"} + } + } + render(root, [{tag: "svg", children: [{tag: component}]}]) - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls oninit before view", function() { - var viewCalled = false + o(root.firstChild.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg") + }) + o("svg works when updating across component boundary", function() { + var component = { + view: function(vnode) { + return {tag: "g"} + } + } + render(root, [{tag: "svg", children: [{tag: component}]}]) + render(root, [{tag: "svg", children: [{tag: component}]}]) - render(root, { - tag: { - view: function() { - viewCalled = true - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - }, - oninit: function(vnode) { - o(viewCalled).equals(false) - }, - } + o(root.firstChild.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg") + }) + }) + o.spec("return value", function() { + o("can return fragments", function() { + var component = { + view: function(vnode) { + return [ + {tag: "label"}, + {tag: "input"}, + ] + } + } + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(2) + o(root.childNodes[0].nodeName).equals("LABEL") + o(root.childNodes[1].nodeName).equals("INPUT") + }) + o("can return string", function() { + var component = { + view: function(vnode) { + return "a" + } + } + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("a") + }) + o("can return falsy string", function() { + var component = { + view: function(vnode) { + return "" + } + } + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("") + }) + o("can return number", function() { + var component = { + view: function(vnode) { + return 1 + } + } + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("1") + }) + o("can return falsy number", function() { + var component = { + view: function(vnode) { + return 0 + } + } + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("0") + }) + o("can return boolean", function() { + var component = { + view: function(vnode) { + return true + } + } + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("true") + }) + o("can return falsy boolean", function() { + var component = { + view: function(vnode) { + return false + } + } + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("") + }) + o("can return null", function() { + var component = { + view: function(vnode) { + return null + } + } + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(0) + }) + o("can return undefined", function() { + var component = { + view: function(vnode) { + return undefined + } + } + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(0) + }) + o("throws a custom error if it returns itself", function() { + // A view that returns its vnode would otherwise trigger an infinite loop + var component = { + view: function(vnode) { + return vnode + } + } + try { + render(root, [{tag: component}]) + } + catch (e) { + o(e instanceof Error).equals(true) + // Call stack exception is a RangeError + o(e instanceof RangeError).equals(false) + } + }) + o("can update when returning fragments", function() { + var component = { + view: function(vnode) { + return [ + {tag: "label"}, + {tag: "input"}, + ] + } + } + render(root, [{tag: component}]) + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(2) + o(root.childNodes[0].nodeName).equals("LABEL") + o(root.childNodes[1].nodeName).equals("INPUT") + }) + o("can update when returning primitive", function() { + var component = { + view: function(vnode) { + return "a" + } + } + render(root, [{tag: component}]) + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("a") + }) + o("can update when returning null", function() { + var component = { + view: function(vnode) { + return null + } + } + render(root, [{tag: component}]) + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(0) + }) + o("can remove when returning fragments", function() { + var component = { + view: function(vnode) { + return [ + {tag: "label"}, + {tag: "input"}, + ] + } + } + var div = {tag: "div", key: 2} + render(root, [{tag: component, key: 1}, div]) + + render(root, [{tag: "div", key: 2}]) + + o(root.childNodes.length).equals(1) + o(root.firstChild).equals(div.dom) + }) + o("can remove when returning primitive", function() { + var component = { + view: function(vnode) { + return "a" + } + } + var div = {tag: "div", key: 2} + render(root, [{tag: component, key: 1}, div]) + + render(root, [{tag: "div", key: 2}]) + + o(root.childNodes.length).equals(1) + o(root.firstChild).equals(div.dom) + }) + }) + o.spec("lifecycle", function() { + o("calls oninit", function() { + var called = 0 + var component = { + oninit: function(vnode) { + called++ + + o(vnode.tag).equals(component) + o(vnode.dom).equals(undefined) + o(root.childNodes.length).equals(0) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + } + var node = {tag: component} + + render(root, [node]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls oninit when returning fragment", function() { + var called = 0 + var component = { + oninit: function(vnode) { + called++ + + o(vnode.tag).equals(component) + o(vnode.dom).equals(undefined) + o(root.childNodes.length).equals(0) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + } + var node = {tag: component} + + render(root, [node]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls oninit before view", function() { + var viewCalled = false + + render(root, { + tag: { + view: function() { + viewCalled = true + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + }, + oninit: function(vnode) { + o(viewCalled).equals(false) + }, + } + }) + }) + o("does not calls oninit on redraw", function() { + var init = o.spy() + var component = { + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + }, + oninit: init, + } + + function view() { + return {tag: component} + } + + render(root, view()) + render(root, view()) + + o(init.callCount).equals(1) + }) + o("calls oncreate", function() { + var called = 0 + var component = { + oncreate: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + } + var node = {tag: component} + + render(root, [node]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("does not calls oncreate on redraw", function() { + var create = o.spy() + var component = { + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + }, + oncreate: create, + } + + function view() { + return {tag: component} + } + + render(root, view()) + render(root, view()) + + o(create.callCount).equals(1) + }) + o("calls oncreate when returning fragment", function() { + var called = 0 + var component = { + oncreate: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + } + var node = {tag: component} + + render(root, [node]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls onupdate", function() { + var called = 0 + var component = { + onupdate: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + } + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, [{tag: component}]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls onupdate when returning fragment", function() { + var called = 0 + var component = { + onupdate: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + } + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, [{tag: component}]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls onremove", function() { + var called = 0 + var component = { + onremove: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + } + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, []) + + o(called).equals(1) + o(root.childNodes.length).equals(0) + }) + o("calls onremove when returning fragment", function() { + var called = 0 + var component = { + onremove: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + } + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, []) + + o(called).equals(1) + o(root.childNodes.length).equals(0) + }) + o("calls onbeforeremove", function() { + var called = 0 + var component = { + onbeforeremove: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + } + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, []) + + o(called).equals(1) + o(root.childNodes.length).equals(0) + }) + o("calls onbeforeremove when returning fragment", function() { + var called = 0 + var component = { + onbeforeremove: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + } + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, []) + + o(called).equals(1) + o(root.childNodes.length).equals(0) + }) + o("does not recycle when there's an onupdate", function() { + var component = { + onupdate: function() {}, + view: function() { + return {tag: "div"} + } + } + var update = o.spy() + var vnode = {tag: component, key: 1} + var updated = {tag: component, key: 1} + + render(root, [vnode]) + render(root, []) + render(root, [updated]) + + o(vnode.dom).notEquals(updated.dom) + }) + }) + o.spec("state", function() { + o("copies state", function() { + var called = 0 + var data = {a: 1} + var component = { + data: data, + oninit: init, + view: function() { + return "" + } + } + + render(root, [{tag: component}]) + + function init(vnode) { + o(vnode.state.data).deepEquals(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() { + var called = 0 + var body = {a: 1} + var data = [body] + var component = { + data: data, + oninit: init, + view: function() { + return "" + } + } + + render(root, [{tag: component}]) + + function init(vnode) { + o(vnode.state.data).equals(data) + o(vnode.state.data[0]).equals(body) + } + }) }) }) - o("does not calls oninit on redraw", function() { - var init = o.spy() - var component = { - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - }, - oninit: init, - } - - function view() { - return {tag: component} - } - - render(root, view()) - render(root, view()) - - o(init.callCount).equals(1) - }) - o("calls oncreate", function() { - var called = 0 - var component = { - oncreate: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - var node = {tag: component} - - render(root, [node]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("does not calls oncreate on redraw", function() { - var create = o.spy() - var component = { - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - }, - oncreate: create, - } - - function view() { - return {tag: component} - } - - render(root, view()) - render(root, view()) - - o(create.callCount).equals(1) - }) - o("calls oncreate when returning fragment", function() { - var called = 0 - var component = { - oncreate: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - var node = {tag: component} - - render(root, [node]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls onupdate", function() { - var called = 0 - var component = { - onupdate: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, [{tag: component}]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls onupdate when returning fragment", function() { - var called = 0 - var component = { - onupdate: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, [{tag: component}]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls onremove", function() { - var called = 0 - var component = { - onremove: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, []) - - o(called).equals(1) - o(root.childNodes.length).equals(0) - }) - o("calls onremove when returning fragment", function() { - var called = 0 - var component = { - onremove: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, []) - - o(called).equals(1) - o(root.childNodes.length).equals(0) - }) - o("calls onbeforeremove", function() { - var called = 0 - var component = { - onbeforeremove: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, []) - - o(called).equals(1) - o(root.childNodes.length).equals(0) - }) - o("calls onbeforeremove when returning fragment", function() { - var called = 0 - var component = { - onbeforeremove: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, []) - - o(called).equals(1) - o(root.childNodes.length).equals(0) - }) - o("does not recycle when there's an onupdate", function() { - var component = { - onupdate: function() {}, - view: function() { - return {tag: "div"} - } - } - var update = o.spy() - var vnode = {tag: component, key: 1} - var updated = {tag: component, key: 1} - - render(root, [vnode]) - render(root, []) - render(root, [updated]) - - o(vnode.dom).notEquals(updated.dom) - }) }) - o.spec("state", function() { - o("copies state", function() { - var called = 0 - var data = {a: 1} - var component = { - data: data, - oninit: init, - view: function() { - return "" - } - } - - render(root, [{tag: component}]) - - function init(vnode) { - o(vnode.state.data).deepEquals(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() { - var called = 0 - var body = {a: 1} - var data = [body] - var component = { - data: data, - oninit: init, - view: function() { - return "" - } - } - - render(root, [{tag: component}]) - - function init(vnode) { - o(vnode.state.data).equals(data) - o(vnode.state.data[0]).equals(body) - } - }) - }) - o.spec("Alternative ways to specify componenents", function() { + o.spec("Alternative ways to specify components", function() { o("Classes can be used as components", function() { function MyComponent(vnode){ o(vnode.state).equals(null) diff --git a/render/tests/test-onbeforeremove.js b/render/tests/test-onbeforeremove.js index c9af4894..0a3ca8fe 100644 --- a/render/tests/test-onbeforeremove.js +++ b/render/tests/test-onbeforeremove.js @@ -2,6 +2,7 @@ var o = require("../../ospec/ospec") var callAsync = require("../../test-utils/callAsync") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") var Promise = require("../../promise/promise") @@ -169,39 +170,44 @@ o.spec("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 onbeforeremove = function(){return Promise.resolve()} - var component = { - onbeforeremove: onbeforeremove, - onremove: onremove, - view: function() {}, - } - render(root, [{tag: component, attrs: {onbeforeremove: onbeforeremove, onremove: onremove}}]) - render(root, []) - callAsync(function() { - o(onremove.callCount).equals(2) // once for `tag`, once for `attrs` - done() - }) - }) - o("awaits promise resolution before removing the node", function(done) { - var view = o.spy() - var onremove = o.spy() - var onbeforeremove = function(){return new Promise(function(resolve){callAsync(resolve)})} - var component = { - onbeforeremove: onbeforeremove, - onremove: onremove, - view: view, - } - render(root, [{tag: component}]) - render(root, []) + ;[components[0]].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) { + var onremove = o.spy() + var onbeforeremove = function(){return Promise.resolve()} + var component = { + onbeforeremove: onbeforeremove, + onremove: onremove, + view: function() {}, + } + render(root, [{tag: component, attrs: {onbeforeremove: onbeforeremove, onremove: onremove}}]) + render(root, []) + callAsync(function() { + o(onremove.callCount).equals(2) // once for `tag`, once for `attrs` + done() + }) + }) + o("awaits promise resolution before removing the node", function(done) { + var view = o.spy() + var onremove = o.spy() + var onbeforeremove = function(){return new Promise(function(resolve){callAsync(resolve)})} + var component = { + onbeforeremove: onbeforeremove, + onremove: onremove, + view: view, + } + render(root, [{tag: component}]) + render(root, []) - callAsync(function(){ - o(onremove.callCount).equals(0) + callAsync(function(){ + o(onremove.callCount).equals(0) - callAsync(function() { - o(onremove.callCount).equals(1) - done() + callAsync(function() { + o(onremove.callCount).equals(1) + done() + }) + }) }) }) }) diff --git a/render/tests/test-onbeforeupdate.js b/render/tests/test-onbeforeupdate.js index 2844e8ee..d637ab66 100644 --- a/render/tests/test-onbeforeupdate.js +++ b/render/tests/test-onbeforeupdate.js @@ -120,171 +120,177 @@ o.spec("onbeforeupdate", function() { o(count).equals(1) }) - 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"}]} + ;[components[0]].forEach(function(cmp){ + o.spec(cmp.kind, function(){ + var createComponent = cmp.create - render(root, [vnode]) - render(root, [updated]) + 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"}]} - o(root.firstChild.firstChild.nodeValue).equals("a") + 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 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 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 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 component update", 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++ + return true + } + + o(count).equals(1) + }) + }) }) - - 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 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 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 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 component update", 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++ - return true - } - - o(count).equals(1) - }) -}) +}) \ No newline at end of file diff --git a/render/tests/test-onremove.js b/render/tests/test-onremove.js index 8aa2eabd..e6e40f55 100644 --- a/render/tests/test-onremove.js +++ b/render/tests/test-onremove.js @@ -113,37 +113,43 @@ o.spec("onremove", function() { o(vnode.dom).notEquals(updated.dom) }) - 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) + ;[components[0]].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 = { + 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("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) - }) -}) +}) \ No newline at end of file diff --git a/render/tests/test-updateNodes.js b/render/tests/test-updateNodes.js index c7b7095c..2d11533a 100644 --- a/render/tests/test-updateNodes.js +++ b/render/tests/test-updateNodes.js @@ -905,36 +905,42 @@ o.spec("updateNodes", function() { o(vnode.dom).notEquals(updated.dom) }) - 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"}]}] + ;[components[0]].forEach(function(cmp){ + o.spec(cmp.kind, function(){ + var createComponent = cmp.create - render(root, vnodes) - render(root, temp) - render(root, updated) + 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"}]}] - o(root.childNodes.length).equals(2) - o(root.childNodes[0].nodeName).equals("A") - o(root.childNodes[1].nodeName).equals("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("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") - }) -}) +}) \ No newline at end of file diff --git a/tests/test-api.js b/tests/test-api.js index 59e3eb23..6a7d7ef4 100644 --- a/tests/test-api.js +++ b/tests/test-api.js @@ -2,6 +2,7 @@ var o = require("../ospec/ospec") var browserMock = require("../test-utils/browserMock") +var components = require("../test-utils/components") o.spec("api", function() { var m @@ -87,84 +88,90 @@ o.spec("api", function() { o(root.firstChild.nodeName).equals("DIV") }) }) - o.spec("m.mount", function() { - o("works", function() { - var root = window.document.createElement("div") - m.mount(root, {view: function() {return m("div")}}) + ;[components[0]].forEach(function(cmp){ + o.spec(cmp.kind, function(){ + var createComponent = cmp.create - o(root.childNodes.length).equals(1) - o(root.firstChild.nodeName).equals("DIV") + o.spec("m.mount", function() { + o("works", function() { + var root = window.document.createElement("div") + m.mount(root, {view: function() {return m("div")}}) + + o(root.childNodes.length).equals(1) + o(root.firstChild.nodeName).equals("DIV") + }) + }) + o.spec("m.route", function() { + o("works", function(done) { + var root = window.document.createElement("div") + m.route(root, "/a", { + "/a": {view: function() {return m("div")}} + }) + + setTimeout(function() { + o(root.childNodes.length).equals(1) + o(root.firstChild.nodeName).equals("DIV") + + done() + }, FRAME_BUDGET) + }) + o("m.route.prefix", function(done) { + var root = window.document.createElement("div") + m.route.prefix("#") + m.route(root, "/a", { + "/a": {view: function() {return m("div")}} + }) + + setTimeout(function() { + o(root.childNodes.length).equals(1) + o(root.firstChild.nodeName).equals("DIV") + + done() + }, FRAME_BUDGET) + }) + o("m.route.get", function(done) { + var root = window.document.createElement("div") + m.route(root, "/a", { + "/a": {view: function() {return m("div")}} + }) + + setTimeout(function() { + o(m.route.get()).equals("/a") + + done() + }, FRAME_BUDGET) + }) + o("m.route.set", function(done, timeout) { + timeout(100) + var root = window.document.createElement("div") + m.route(root, "/a", { + "/:id": {view: function() {return m("div")}} + }) + + setTimeout(function() { + m.route.set("/b") + setTimeout(function() { + o(m.route.get()).equals("/b") + + done() + }, FRAME_BUDGET) + }, FRAME_BUDGET) + }) + }) + o.spec("m.redraw", function() { + o("works", function(done) { + var count = 0 + var root = window.document.createElement("div") + m.mount(root, {view: function() {count++}}) + setTimeout(function() { + m.redraw() + + o(count).equals(2) + + done() + }, FRAME_BUDGET) + }) + }) }) }) - o.spec("m.route", function() { - o("works", function(done) { - var root = window.document.createElement("div") - m.route(root, "/a", { - "/a": {view: function() {return m("div")}} - }) - - setTimeout(function() { - o(root.childNodes.length).equals(1) - o(root.firstChild.nodeName).equals("DIV") - - done() - }, FRAME_BUDGET) - }) - o("m.route.prefix", function(done) { - var root = window.document.createElement("div") - m.route.prefix("#") - m.route(root, "/a", { - "/a": {view: function() {return m("div")}} - }) - - setTimeout(function() { - o(root.childNodes.length).equals(1) - o(root.firstChild.nodeName).equals("DIV") - - done() - }, FRAME_BUDGET) - }) - o("m.route.get", function(done) { - var root = window.document.createElement("div") - m.route(root, "/a", { - "/a": {view: function() {return m("div")}} - }) - - setTimeout(function() { - o(m.route.get()).equals("/a") - - done() - }, FRAME_BUDGET) - }) - o("m.route.set", function(done, timeout) { - timeout(100) - var root = window.document.createElement("div") - m.route(root, "/a", { - "/:id": {view: function() {return m("div")}} - }) - - setTimeout(function() { - m.route.set("/b") - setTimeout(function() { - o(m.route.get()).equals("/b") - - done() - }, FRAME_BUDGET) - }, FRAME_BUDGET) - }) - }) - o.spec("m.redraw", function() { - o("works", function(done) { - var count = 0 - var root = window.document.createElement("div") - m.mount(root, {view: function() {count++}}) - setTimeout(function() { - m.redraw() - - o(count).equals(2) - - done() - }, FRAME_BUDGET) - }) - }) -}) +}) \ No newline at end of file From 90b67b42f0c6dea17347b9ff25939ca68a0b0e41 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 01:07:51 +0100 Subject: [PATCH 044/113] Tests: render/test-components tweaks --- render/tests/test-component.js | 38 ++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/render/tests/test-component.js b/render/tests/test-component.js index 0f2ceaa3..bd369faa 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -658,7 +658,7 @@ o.spec("component", function() { }) }) o.spec("state", function() { - o("copies state", function() { + o("initializes state", function() { var called = 0 var data = {a: 1} var component = { @@ -672,15 +672,10 @@ o.spec("component", function() { render(root, [{tag: component}]) function init(vnode) { - o(vnode.state.data).deepEquals(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 "copy" is shallow', function() { var called = 0 var body = {a: 1} var data = [body] @@ -702,7 +697,32 @@ o.spec("component", function() { }) }) }) - o.spec("Alternative ways to specify components", function() { + o.spec("Tests specific to certain component kinds", function() { + + o.spec("POJO state", function() { + o("copies state", function() { + var called = 0 + var data = {a: 1} + var component = { + data: data, + oninit: init, + view: function() { + return "" + } + } + + 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("Classes can be used as components", function() { function MyComponent(vnode){ o(vnode.state).equals(null) @@ -769,7 +789,7 @@ o.spec("component", function() { 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 functions can be used as components", function() { var state, context function component(vnode) { o(vnode.state).equals(null) From ac38335453af3f1221701e5e0b366ed65238009a Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 01:18:02 +0100 Subject: [PATCH 045/113] 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 --- api/tests/test-mount.js | 32 ++++++++++++++++---------------- render/tests/test-component.js | 12 ++++++------ render/tests/test-onremove.js | 24 ++++++++++++------------ render/tests/test-updateNodes.js | 6 +++--- tests/test-api.js | 12 ++++++------ 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js index 86ae3835..8791724e 100644 --- a/api/tests/test-mount.js +++ b/api/tests/test-mount.js @@ -40,7 +40,7 @@ o.spec("mount", function() { o("throws on invalid `root` DOM node", function() { var threw = false try { - mount(null, {view: function() {}}) + mount(null, createComponent({view: function() {}})) } catch (e) { threw = true } @@ -48,11 +48,11 @@ o.spec("mount", function() { }) o("renders into `root` (POJO component)", function() { - mount(root, { + mount(root, createComponent({ view : function() { return m("div") } - }) + })) o(root.firstChild.nodeName).equals("DIV") }) @@ -78,11 +78,11 @@ o.spec("mount", function() { }) o("mounting null unmounts", function() { - mount(root, { + mount(root, createComponent({ view : function() { return m("div") } - }) + })) mount(root, null) @@ -97,7 +97,7 @@ o.spec("mount", function() { e.initEvent("click", true, true) - mount(root, { + mount(root, createComponent({ view : function() { return m("div", { oninit : oninit, @@ -105,7 +105,7 @@ o.spec("mount", function() { onclick : onclick, }) } - }) + })) root.firstChild.dispatchEvent(e) @@ -144,7 +144,7 @@ o.spec("mount", function() { m("#child1") ]) - mount(root.childNodes[0], { + mount(root.childNodes[0], createComponent({ view : function() { return m("div", { oninit : oninit0, @@ -152,12 +152,12 @@ o.spec("mount", function() { onclick : onclick0, }) } - }) + })) o(oninit0.callCount).equals(1) o(onupdate0.callCount).equals(0) - mount(root.childNodes[1], { + mount(root.childNodes[1], createComponent({ view : function() { return m("div", { oninit : oninit1, @@ -165,7 +165,7 @@ o.spec("mount", function() { onclick : onclick1, }) } - }) + })) o(oninit1.callCount).equals(1) o(onupdate1.callCount).equals(0) @@ -199,7 +199,7 @@ o.spec("mount", function() { e.initEvent("click", true, true) - mount(root, { + mount(root, createComponent({ view: function() { return m("div", { oninit: oninit, @@ -209,7 +209,7 @@ o.spec("mount", function() { } }) } - }) + })) root.firstChild.dispatchEvent(e) @@ -227,14 +227,14 @@ o.spec("mount", function() { var onupdate = o.spy() var oninit = o.spy() - mount(root, { + mount(root, createComponent({ view : function() { return m("div", { oninit: oninit, onupdate: onupdate }) } - }) + })) o(oninit.callCount).equals(1) o(onupdate.callCount).equals(0) @@ -253,7 +253,7 @@ o.spec("mount", function() { timeout(200) var i = 0 - mount(root, {view: function() {i++}}) + mount(root, createComponent({view: function() {i++}})) var before = i redrawService.redraw() diff --git a/render/tests/test-component.js b/render/tests/test-component.js index bd369faa..516cd2e8 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -397,7 +397,7 @@ o.spec("component", function() { o("calls oninit before view", function() { var viewCalled = false - render(root, { + render(root, createComponent({ tag: { view: function() { viewCalled = true @@ -407,7 +407,7 @@ o.spec("component", function() { o(viewCalled).equals(false) }, } - }) + })) }) o("does not calls oninit on redraw", function() { var init = o.spy() @@ -661,13 +661,13 @@ o.spec("component", function() { o("initializes state", function() { var called = 0 var data = {a: 1} - var component = { + var component = createComponent({ data: data, oninit: init, view: function() { return "" } - } + }) render(root, [{tag: component}]) @@ -679,13 +679,13 @@ o.spec("component", function() { var called = 0 var body = {a: 1} var data = [body] - var component = { + var component = createComponent({ data: data, oninit: init, view: function() { return "" } - } + }) render(root, [{tag: component}]) diff --git a/render/tests/test-onremove.js b/render/tests/test-onremove.js index e6e40f55..4def776e 100644 --- a/render/tests/test-onremove.js +++ b/render/tests/test-onremove.js @@ -119,16 +119,16 @@ o.spec("onremove", function() { o("calls onremove on nested component", function() { var spy = o.spy() - var comp = { + var comp = createComponent({ view: function() {return m(outer)} - } - var outer = { + }) + var outer = createComponent({ view: function() {return m(inner)} - } - var inner = { + }) + var inner = createComponent({ onremove: spy, view: function() {return m("div")} - } + }) render(root, {tag: comp}) render(root, null) @@ -136,15 +136,15 @@ o.spec("onremove", function() { }) o("calls onremove on nested component child", function() { var spy = o.spy() - var comp = { + var comp = createComponent({ view: function() {return m(outer)} - } - var outer = { + }) + var outer = createComponent({ view: function() {return m(inner, m("a", {onremove: spy}))} - } - var inner = { + }) + var inner = createComponent({ view: function(vnode) {return m("div", vnode.children)} - } + }) render(root, {tag: comp}) render(root, null) diff --git a/render/tests/test-updateNodes.js b/render/tests/test-updateNodes.js index 2d11533a..93860e1f 100644 --- a/render/tests/test-updateNodes.js +++ b/render/tests/test-updateNodes.js @@ -910,7 +910,7 @@ o.spec("updateNodes", function() { var createComponent = cmp.create o("fragment child toggles from null when followed by null component then tag", function() { - var component = {view: function() {return null}} + 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"}]}] @@ -925,8 +925,8 @@ o.spec("updateNodes", function() { }) 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 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"}]}] diff --git a/tests/test-api.js b/tests/test-api.js index 6a7d7ef4..e206b8af 100644 --- a/tests/test-api.js +++ b/tests/test-api.js @@ -95,7 +95,7 @@ o.spec("api", function() { o.spec("m.mount", function() { o("works", function() { 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.firstChild.nodeName).equals("DIV") @@ -105,7 +105,7 @@ o.spec("api", function() { o("works", function(done) { var root = window.document.createElement("div") m.route(root, "/a", { - "/a": {view: function() {return m("div")}} + "/a": createComponent({view: function() {return m("div")}}) }) setTimeout(function() { @@ -119,7 +119,7 @@ o.spec("api", function() { var root = window.document.createElement("div") m.route.prefix("#") m.route(root, "/a", { - "/a": {view: function() {return m("div")}} + "/a": createComponent({view: function() {return m("div")}}) }) setTimeout(function() { @@ -132,7 +132,7 @@ o.spec("api", function() { o("m.route.get", function(done) { var root = window.document.createElement("div") m.route(root, "/a", { - "/a": {view: function() {return m("div")}} + "/a": createComponent({view: function() {return m("div")}}) }) setTimeout(function() { @@ -145,7 +145,7 @@ o.spec("api", function() { timeout(100) var root = window.document.createElement("div") m.route(root, "/a", { - "/:id": {view: function() {return m("div")}} + "/:id": createComponent({view: function() {return m("div")}}) }) setTimeout(function() { @@ -162,7 +162,7 @@ o.spec("api", function() { o("works", function(done) { var count = 0 var root = window.document.createElement("div") - m.mount(root, {view: function() {count++}}) + m.mount(root, createComponent({view: function() {count++}})) setTimeout(function() { m.redraw() From 24243dba9498dca2cf3387e478b14401c285573f Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 02:07:48 +0100 Subject: [PATCH 046/113] Tests: wrap the rest of the components definitions --- render/tests/test-component.js | 164 ++++++++++++++-------------- render/tests/test-onbeforeremove.js | 8 +- render/tests/test-onbeforeupdate.js | 36 +++--- 3 files changed, 104 insertions(+), 104 deletions(-) diff --git a/render/tests/test-component.js b/render/tests/test-component.js index 516cd2e8..1ae9ea45 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -20,11 +20,11 @@ o.spec("component", function() { o.spec("basics", function() { o("works", function() { - var component = { + var component = createComponent({ view: function() { return {tag: "div", attrs: {id: "a"}, text: "b"} } - } + }) var node = {tag: component} render(root, [node]) @@ -34,11 +34,11 @@ o.spec("component", function() { o(root.firstChild.firstChild.nodeValue).equals("b") }) o("receives arguments", function() { - var component = { + var component = createComponent({ view: function(vnode) { return {tag: "div", attrs: vnode.attrs, text: vnode.text} } - } + }) var node = {tag: component, attrs: {id: "a"}, text: "b"} render(root, [node]) @@ -48,11 +48,11 @@ o.spec("component", function() { o(root.firstChild.firstChild.nodeValue).equals("b") }) o("updates", function() { - var component = { + var component = createComponent({ view: function(vnode) { return {tag: "div", attrs: vnode.attrs, text: vnode.text} } - } + }) render(root, [{tag: component, attrs: {id: "a"}, text: "b"}]) render(root, [{tag: component, attrs: {id: "c"}, text: "d"}]) @@ -62,11 +62,11 @@ o.spec("component", function() { }) o("updates root from null", function() { var visible = false - var component = { + var component = createComponent({ view: function(vnode) { return visible ? {tag: "div"} : null } - } + }) render(root, [{tag: component}]) visible = true render(root, [{tag: component}]) @@ -75,11 +75,11 @@ o.spec("component", function() { }) o("updates root from primitive", function() { var visible = false - var component = { + var component = createComponent({ view: function(vnode) { return visible ? {tag: "div"} : false } - } + }) render(root, [{tag: component}]) visible = true render(root, [{tag: component}]) @@ -88,11 +88,11 @@ o.spec("component", function() { }) o("updates root to null", function() { var visible = true - var component = { + var component = createComponent({ view: function(vnode) { return visible ? {tag: "div"} : null } - } + }) render(root, [{tag: component}]) visible = false render(root, [{tag: component}]) @@ -101,11 +101,11 @@ o.spec("component", function() { }) o("updates root to primitive", function() { var visible = true - var component = { + var component = createComponent({ view: function(vnode) { return visible ? {tag: "div"} : false } - } + }) render(root, [{tag: component}]) visible = false render(root, [{tag: component}]) @@ -113,22 +113,22 @@ o.spec("component", function() { o(root.firstChild.nodeValue).equals("") }) o("updates root from null to null", function() { - var component = { + var component = createComponent({ view: function(vnode) { return null } - } + }) render(root, [{tag: component}]) render(root, [{tag: component}]) o(root.childNodes.length).equals(0) }) o("removes", function() { - var component = { + var component = createComponent({ view: function(vnode) { return {tag: "div"} } - } + }) var div = {tag: "div", key: 2} render(root, [{tag: component, key: 1}, div]) render(root, [{tag: "div", key: 2}]) @@ -137,21 +137,21 @@ o.spec("component", function() { o(root.firstChild).equals(div.dom) }) o("svg works when creating across component boundary", function() { - var component = { + var component = createComponent({ view: function(vnode) { return {tag: "g"} } - } + }) render(root, [{tag: "svg", children: [{tag: component}]}]) o(root.firstChild.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg") }) o("svg works when updating across component boundary", function() { - var component = { + var component = createComponent({ view: function(vnode) { return {tag: "g"} } - } + }) render(root, [{tag: "svg", children: [{tag: component}]}]) render(root, [{tag: "svg", children: [{tag: component}]}]) @@ -160,14 +160,14 @@ o.spec("component", function() { }) o.spec("return value", function() { o("can return fragments", function() { - var component = { + var component = createComponent({ view: function(vnode) { return [ {tag: "label"}, {tag: "input"}, ] } - } + }) render(root, [{tag: component}]) o(root.childNodes.length).equals(2) @@ -175,98 +175,98 @@ o.spec("component", function() { o(root.childNodes[1].nodeName).equals("INPUT") }) o("can return string", function() { - var component = { + var component = createComponent({ view: function(vnode) { return "a" } - } + }) render(root, [{tag: component}]) o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeValue).equals("a") }) o("can return falsy string", function() { - var component = { + var component = createComponent({ view: function(vnode) { return "" } - } + }) render(root, [{tag: component}]) o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeValue).equals("") }) o("can return number", function() { - var component = { + var component = createComponent({ view: function(vnode) { return 1 } - } + }) render(root, [{tag: component}]) o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeValue).equals("1") }) o("can return falsy number", function() { - var component = { + var component = createComponent({ view: function(vnode) { return 0 } - } + }) render(root, [{tag: component}]) o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeValue).equals("0") }) o("can return boolean", function() { - var component = { + var component = createComponent({ view: function(vnode) { return true } - } + }) render(root, [{tag: component}]) o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeValue).equals("true") }) o("can return falsy boolean", function() { - var component = { + var component = createComponent({ view: function(vnode) { return false } - } + }) render(root, [{tag: component}]) o(root.firstChild.nodeType).equals(3) o(root.firstChild.nodeValue).equals("") }) o("can return null", function() { - var component = { + var component = createComponent({ view: function(vnode) { return null } - } + }) render(root, [{tag: component}]) o(root.childNodes.length).equals(0) }) o("can return undefined", function() { - var component = { + var component = createComponent({ view: function(vnode) { return undefined } - } + }) render(root, [{tag: component}]) o(root.childNodes.length).equals(0) }) o("throws a custom error if it returns itself", function() { // A view that returns its vnode would otherwise trigger an infinite loop - var component = { + var component = createComponent({ view: function(vnode) { return vnode } - } + }) try { render(root, [{tag: component}]) } @@ -277,14 +277,14 @@ o.spec("component", function() { } }) o("can update when returning fragments", function() { - var component = { + var component = createComponent({ view: function(vnode) { return [ {tag: "label"}, {tag: "input"}, ] } - } + }) render(root, [{tag: component}]) render(root, [{tag: component}]) @@ -293,11 +293,11 @@ o.spec("component", function() { o(root.childNodes[1].nodeName).equals("INPUT") }) o("can update when returning primitive", function() { - var component = { + var component = createComponent({ view: function(vnode) { return "a" } - } + }) render(root, [{tag: component}]) render(root, [{tag: component}]) @@ -305,25 +305,25 @@ o.spec("component", function() { o(root.firstChild.nodeValue).equals("a") }) o("can update when returning null", function() { - var component = { + var component = createComponent({ view: function(vnode) { return null } - } + }) render(root, [{tag: component}]) render(root, [{tag: component}]) o(root.childNodes.length).equals(0) }) o("can remove when returning fragments", function() { - var component = { + var component = createComponent({ view: function(vnode) { return [ {tag: "label"}, {tag: "input"}, ] } - } + }) var div = {tag: "div", key: 2} render(root, [{tag: component, key: 1}, div]) @@ -333,11 +333,11 @@ o.spec("component", function() { o(root.firstChild).equals(div.dom) }) o("can remove when returning primitive", function() { - var component = { + var component = createComponent({ view: function(vnode) { return "a" } - } + }) var div = {tag: "div", key: 2} render(root, [{tag: component, key: 1}, div]) @@ -350,7 +350,7 @@ o.spec("component", function() { o.spec("lifecycle", function() { o("calls oninit", function() { var called = 0 - var component = { + var component = createComponent({ oninit: function(vnode) { called++ @@ -361,7 +361,7 @@ o.spec("component", function() { view: function() { return {tag: "div", attrs: {id: "a"}, text: "b"} } - } + }) var node = {tag: component} render(root, [node]) @@ -373,7 +373,7 @@ o.spec("component", function() { }) o("calls oninit when returning fragment", function() { var called = 0 - var component = { + var component = createComponent({ oninit: function(vnode) { called++ @@ -384,7 +384,7 @@ o.spec("component", function() { view: function() { return [{tag: "div", attrs: {id: "a"}, text: "b"}] } - } + }) var node = {tag: component} render(root, [node]) @@ -411,12 +411,12 @@ o.spec("component", function() { }) o("does not calls oninit on redraw", function() { var init = o.spy() - var component = { + var component = createComponent({ view: function() { return {tag: "div", attrs: {id: "a"}, text: "b"} }, oninit: init, - } + }) function view() { return {tag: component} @@ -429,7 +429,7 @@ o.spec("component", function() { }) o("calls oncreate", function() { var called = 0 - var component = { + var component = createComponent({ oncreate: function(vnode) { called++ @@ -440,7 +440,7 @@ o.spec("component", function() { view: function() { return {tag: "div", attrs: {id: "a"}, text: "b"} } - } + }) var node = {tag: component} render(root, [node]) @@ -452,12 +452,12 @@ o.spec("component", function() { }) o("does not calls oncreate on redraw", function() { var create = o.spy() - var component = { + var component = createComponent({ view: function() { return {tag: "div", attrs: {id: "a"}, text: "b"} }, oncreate: create, - } + }) function view() { return {tag: component} @@ -470,7 +470,7 @@ o.spec("component", function() { }) o("calls oncreate when returning fragment", function() { var called = 0 - var component = { + var component = createComponent({ oncreate: function(vnode) { called++ @@ -481,7 +481,7 @@ o.spec("component", function() { view: function() { return [{tag: "div", attrs: {id: "a"}, text: "b"}] } - } + }) var node = {tag: component} render(root, [node]) @@ -493,7 +493,7 @@ o.spec("component", function() { }) o("calls onupdate", function() { var called = 0 - var component = { + var component = createComponent({ onupdate: function(vnode) { called++ @@ -504,7 +504,7 @@ o.spec("component", function() { view: function() { return {tag: "div", attrs: {id: "a"}, text: "b"} } - } + }) render(root, [{tag: component}]) @@ -519,7 +519,7 @@ o.spec("component", function() { }) o("calls onupdate when returning fragment", function() { var called = 0 - var component = { + var component = createComponent({ onupdate: function(vnode) { called++ @@ -530,7 +530,7 @@ o.spec("component", function() { view: function() { return [{tag: "div", attrs: {id: "a"}, text: "b"}] } - } + }) render(root, [{tag: component}]) @@ -545,7 +545,7 @@ o.spec("component", function() { }) o("calls onremove", function() { var called = 0 - var component = { + var component = createComponent({ onremove: function(vnode) { called++ @@ -556,7 +556,7 @@ o.spec("component", function() { view: function() { return {tag: "div", attrs: {id: "a"}, text: "b"} } - } + }) render(root, [{tag: component}]) @@ -569,7 +569,7 @@ o.spec("component", function() { }) o("calls onremove when returning fragment", function() { var called = 0 - var component = { + var component = createComponent({ onremove: function(vnode) { called++ @@ -580,7 +580,7 @@ o.spec("component", function() { view: function() { return [{tag: "div", attrs: {id: "a"}, text: "b"}] } - } + }) render(root, [{tag: component}]) @@ -593,7 +593,7 @@ o.spec("component", function() { }) o("calls onbeforeremove", function() { var called = 0 - var component = { + var component = createComponent({ onbeforeremove: function(vnode) { called++ @@ -604,7 +604,7 @@ o.spec("component", function() { view: function() { return {tag: "div", attrs: {id: "a"}, text: "b"} } - } + }) render(root, [{tag: component}]) @@ -617,7 +617,7 @@ o.spec("component", function() { }) o("calls onbeforeremove when returning fragment", function() { var called = 0 - var component = { + var component = createComponent({ onbeforeremove: function(vnode) { called++ @@ -628,7 +628,7 @@ o.spec("component", function() { view: function() { return [{tag: "div", attrs: {id: "a"}, text: "b"}] } - } + }) render(root, [{tag: component}]) @@ -640,12 +640,12 @@ o.spec("component", function() { o(root.childNodes.length).equals(0) }) o("does not recycle when there's an onupdate", function() { - var component = { + var component = createComponent({ onupdate: function() {}, view: function() { return {tag: "div"} } - } + }) var update = o.spy() var vnode = {tag: component, key: 1} var updated = {tag: component, key: 1} @@ -661,13 +661,13 @@ o.spec("component", function() { o("initializes state", function() { var called = 0 var data = {a: 1} - var component = createComponent({ + var component = createComponent(createComponent({ data: data, oninit: init, view: function() { return "" } - }) + })) render(root, [{tag: component}]) @@ -679,13 +679,13 @@ o.spec("component", function() { var called = 0 var body = {a: 1} var data = [body] - var component = createComponent({ + var component = createComponent(createComponent({ data: data, oninit: init, view: function() { return "" } - }) + })) render(root, [{tag: component}]) diff --git a/render/tests/test-onbeforeremove.js b/render/tests/test-onbeforeremove.js index 0a3ca8fe..050d1758 100644 --- a/render/tests/test-onbeforeremove.js +++ b/render/tests/test-onbeforeremove.js @@ -176,11 +176,11 @@ o.spec("onbeforeremove", function() { o("finalizes the remove phase asynchronously when promise is returned synchronously from both attrs- and tag.onbeforeremove", function(done) { var onremove = o.spy() var onbeforeremove = function(){return Promise.resolve()} - var component = { + var component = createComponent({ onbeforeremove: onbeforeremove, onremove: onremove, view: function() {}, - } + }) render(root, [{tag: component, attrs: {onbeforeremove: onbeforeremove, onremove: onremove}}]) render(root, []) callAsync(function() { @@ -192,11 +192,11 @@ o.spec("onbeforeremove", function() { var view = o.spy() var onremove = o.spy() var onbeforeremove = function(){return new Promise(function(resolve){callAsync(resolve)})} - var component = { + var component = createComponent({ onbeforeremove: onbeforeremove, onremove: onremove, view: view, - } + }) render(root, [{tag: component}]) render(root, []) diff --git a/render/tests/test-onbeforeupdate.js b/render/tests/test-onbeforeupdate.js index d637ab66..626b9ddc 100644 --- a/render/tests/test-onbeforeupdate.js +++ b/render/tests/test-onbeforeupdate.js @@ -125,12 +125,12 @@ o.spec("onbeforeupdate", function() { var createComponent = cmp.create o("prevents update in component", function() { - var component = { + 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"}]} @@ -141,12 +141,12 @@ o.spec("onbeforeupdate", function() { }) o("prevents update if returning false in component and false in vnode", function() { - var component = { + 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}}} @@ -157,12 +157,12 @@ o.spec("onbeforeupdate", function() { }) o("does not prevent update if returning true in component and true in vnode", function() { - var component = { + 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}}} @@ -173,12 +173,12 @@ o.spec("onbeforeupdate", function() { }) o("does not prevent update if returning false in component but true in vnode", function() { - var component = { + 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}}} @@ -189,12 +189,12 @@ o.spec("onbeforeupdate", function() { }) o("does not prevent update if returning true in component but false in vnode", function() { - var component = { + 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}}} @@ -205,12 +205,12 @@ o.spec("onbeforeupdate", function() { }) o("does not prevent update if returning true from component", function() { - var component = { + 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"}} @@ -221,12 +221,12 @@ o.spec("onbeforeupdate", function() { }) o("accepts arguments for comparison in component", function() { - var component = { + var component = createComponent({ 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"}} @@ -248,12 +248,12 @@ o.spec("onbeforeupdate", function() { }) o("is not called on component creation", function() { - var component = { + var component = createComponent({ onbeforeupdate: onbeforeupdate, view: function(vnode) { return {tag: "div", attrs: vnode.attrs} }, - } + }) var count = 0 var vnode = {tag: "div", attrs: {id: "a"}} @@ -270,12 +270,12 @@ o.spec("onbeforeupdate", function() { }) o("is called only once on component update", function() { - var component = { + var component = createComponent({ onbeforeupdate: onbeforeupdate, view: function(vnode) { return {tag: "div", attrs: vnode.attrs} }, - } + }) var count = 0 var vnode = {tag: component, attrs: {id: "a"}} From fca89f987f29f67cba55b5cbc31657f7a35a9119 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 02:10:22 +0100 Subject: [PATCH 047/113] Tests: enable the three kind of components in all related files but api/tests/test-route.js --- api/tests/test-mount.js | 24 ++---------------------- render/tests/test-component.js | 2 +- render/tests/test-onbeforeremove.js | 2 +- render/tests/test-onbeforeupdate.js | 2 +- render/tests/test-onremove.js | 2 +- render/tests/test-updateNodes.js | 2 +- tests/test-api.js | 2 +- 7 files changed, 8 insertions(+), 28 deletions(-) diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js index 8791724e..4ba466c7 100644 --- a/api/tests/test-mount.js +++ b/api/tests/test-mount.js @@ -33,7 +33,7 @@ o.spec("mount", function() { o(threw).equals(true) }) - ;[components[0]].forEach(function(cmp){ + components.forEach(function(cmp){ o.spec(cmp.kind, function(){ var createComponent = cmp.create @@ -47,7 +47,7 @@ o.spec("mount", function() { o(threw).equals(true) }) - o("renders into `root` (POJO component)", function() { + o("renders into `root`", function() { mount(root, createComponent({ view : function() { return m("div") @@ -57,26 +57,6 @@ o.spec("mount", function() { 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` (closure component)", function() { - mount(root, function(){ - return { - view : function() { - return m("div") - } - } - }) - - o(root.firstChild.nodeName).equals("DIV") - }) - o("mounting null unmounts", function() { mount(root, createComponent({ view : function() { diff --git a/render/tests/test-component.js b/render/tests/test-component.js index 1ae9ea45..64cd6fcb 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -14,7 +14,7 @@ o.spec("component", function() { render = vdom($window).render }) - ;[components[0]].forEach(function(cmp){ + components.forEach(function(cmp){ o.spec(cmp.kind, function(){ var createComponent = cmp.create diff --git a/render/tests/test-onbeforeremove.js b/render/tests/test-onbeforeremove.js index 050d1758..4395f90a 100644 --- a/render/tests/test-onbeforeremove.js +++ b/render/tests/test-onbeforeremove.js @@ -170,7 +170,7 @@ o.spec("onbeforeremove", function() { done() }) }) - ;[components[0]].forEach(function(cmp){ + 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) { diff --git a/render/tests/test-onbeforeupdate.js b/render/tests/test-onbeforeupdate.js index 626b9ddc..cf16dd1c 100644 --- a/render/tests/test-onbeforeupdate.js +++ b/render/tests/test-onbeforeupdate.js @@ -120,7 +120,7 @@ o.spec("onbeforeupdate", function() { o(count).equals(1) }) - ;[components[0]].forEach(function(cmp){ + components.forEach(function(cmp){ o.spec(cmp.kind, function(){ var createComponent = cmp.create diff --git a/render/tests/test-onremove.js b/render/tests/test-onremove.js index 4def776e..a7f88a6b 100644 --- a/render/tests/test-onremove.js +++ b/render/tests/test-onremove.js @@ -113,7 +113,7 @@ o.spec("onremove", function() { o(vnode.dom).notEquals(updated.dom) }) - ;[components[0]].forEach(function(cmp){ + components.forEach(function(cmp){ o.spec(cmp.kind, function(){ var createComponent = cmp.create diff --git a/render/tests/test-updateNodes.js b/render/tests/test-updateNodes.js index 93860e1f..c58a29b4 100644 --- a/render/tests/test-updateNodes.js +++ b/render/tests/test-updateNodes.js @@ -905,7 +905,7 @@ o.spec("updateNodes", function() { o(vnode.dom).notEquals(updated.dom) }) - ;[components[0]].forEach(function(cmp){ + components.forEach(function(cmp){ o.spec(cmp.kind, function(){ var createComponent = cmp.create diff --git a/tests/test-api.js b/tests/test-api.js index e206b8af..ec57a0f3 100644 --- a/tests/test-api.js +++ b/tests/test-api.js @@ -88,7 +88,7 @@ o.spec("api", function() { o(root.firstChild.nodeName).equals("DIV") }) }) - ;[components[0]].forEach(function(cmp){ + components.forEach(function(cmp){ o.spec(cmp.kind, function(){ var createComponent = cmp.create From b86805ada35f15e03533e7f710175df94e566fb5 Mon Sep 17 00:00:00 2001 From: Yoshiki Shibukawa Date: Sun, 19 Feb 2017 22:00:30 +0900 Subject: [PATCH 048/113] when method is GET and TRACE, useBody becomes always false --- request/request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/request/request.js b/request/request.js index b153b0e9..489d853f 100644 --- a/request/request.js +++ b/request/request.js @@ -43,7 +43,7 @@ module.exports = function($window, Promise) { if (args.method == null) args.method = "GET" args.method = args.method.toUpperCase() - var useBody = typeof args.useBody === "boolean" ? args.useBody : args.method !== "GET" && args.method !== "TRACE" + var useBody = (args.method === "GET" || args.method === "TRACE") ? false : (typeof args.useBody === "boolean" ? args.useBody : true) if (typeof args.serialize !== "function") args.serialize = typeof FormData !== "undefined" && args.data instanceof FormData ? function(value) {return value} : JSON.stringify if (typeof args.deserialize !== "function") args.deserialize = deserialize From 818c13dba99f6640bfefd73027a441fce9757646 Mon Sep 17 00:00:00 2001 From: Leo Date: Sun, 19 Feb 2017 08:27:31 -0500 Subject: [PATCH 049/113] fix example --- docs/simple-application.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/simple-application.md b/docs/simple-application.md index 1e7a57da..0fdab745 100644 --- a/docs/simple-application.md +++ b/docs/simple-application.md @@ -324,7 +324,7 @@ module.exports = { m("input.input[type=text][placeholder=First name]"), m("label.label", "Last name"), m("input.input[placeholder=Last name]"), - m("button.button[type=submit]", "Save"), + m("button.button[type=button]", "Save"), ]) } } @@ -420,7 +420,7 @@ module.exports = { m("input.input[type=text][placeholder=First name]", {value: User.current.firstName}), m("label.label", "Last name"), m("input.input[placeholder=Last name]", {value: User.current.lastName}), - m("button.button[type=submit]", "Save"), + m("button.button[type=button]", "Save"), ]) } } @@ -477,7 +477,7 @@ module.exports = { oninput: m.withAttr("value", function(value) {User.current.lastName = value}), value: User.current.lastName }), - m("button.button[type=submit]", "Save"), + m("button.button[type=button]", "Save"), ]) } } From 65941d1416c063bf3b64506d42b7cf1d14d009a7 Mon Sep 17 00:00:00 2001 From: Leo Date: Sun, 19 Feb 2017 08:29:35 -0500 Subject: [PATCH 050/113] document classes --- docs/change-log.md | 7 +++++++ docs/components.md | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/docs/change-log.md b/docs/change-log.md index 014f6c23..5a9b75a3 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -9,10 +9,17 @@ ### v1.0.2 +#### News + +- support for ES6 class components +- updated typescript definitions + #### Bug fixes - fix IE11 input[type] error - [#1610](https://github.com/lhorie/mithril.js/issues/1610) - apply [#1609](https://github.com/lhorie/mithril.js/issues/1609) to unkeyed children case +- fix abort detection [#1612](https://github.com/lhorie/mithril.js/issues/1612) +- fix input value focus issue when value is loosely equal to old value [#1593](https://github.com/lhorie/mithril.js/issues/1593) --- diff --git a/docs/components.md b/docs/components.md index 6134ccc1..db5c0b0f 100644 --- a/docs/components.md +++ b/docs/components.md @@ -3,6 +3,7 @@ - [Structure](#structure) - [Lifecycle methods](#lifecycle-methods) - [State](#state) +- [ES6 classes](#es6-classes) - [Avoid-anti-patterns](#avoid-anti-patterns) ### Structure @@ -170,6 +171,44 @@ Be aware that when using ES5 functions, the value of `this` in nested anonymous --- +### ES6 classes + +Components can also be written using ES6 class syntax: + +```javascript +class ES6ClassComponent { + view() { + return m("div", "Hello from an ES6 class") + } +} +``` + +They can be consumed in the same way regular components can. + +```javascript +// EXAMPLE: via m.render +m.render(document.body, m(ES6ClassComponent)) + +// EXAMPLE: via m.mount +m.mount(document.body, ES6ClassComponent) + +// EXAMPLE: via m.route +m.route(document.body, "/", { + "/": ES6ClassComponent +}) + +// EXAMPLE: component composition +class AnotherES6ClassComponent { + view() { + return m("main", [ + m(ES6ClassComponent) + ]) + } +} +``` + +--- + ### Avoid anti-patterns Although Mithril is flexible, some code patterns are discouraged: From 1a4bcc44e6eb2fd908de07c2cb1c59c7add0368c Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Mon, 20 Feb 2017 18:32:20 -0500 Subject: [PATCH 051/113] Keep the bundles out of the diffs Makes reviewing a little easier. --- .gitattributes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 21256661..6de5c647 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ -* text=auto \ No newline at end of file +* text=auto +/mithril.js binary +/mithril.min.js binary From 2196f5c9094f1d74d22b0dcfa876c9cc5290310b Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Tue, 21 Feb 2017 09:16:08 +0100 Subject: [PATCH 052/113] Add test-utils/components.js to index.html where needed --- api/tests/index.html | 1 + render/tests/index.html | 1 + 2 files changed, 2 insertions(+) diff --git a/api/tests/index.html b/api/tests/index.html index fd2557d5..37d313f4 100644 --- a/api/tests/index.html +++ b/api/tests/index.html @@ -13,6 +13,7 @@ + diff --git a/render/tests/index.html b/render/tests/index.html index 480b8b7c..b978ae6f 100644 --- a/render/tests/index.html +++ b/render/tests/index.html @@ -8,6 +8,7 @@ + From 1dd5fe310127557ad19c679f9115a05981e0518e Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 22:53:46 +0100 Subject: [PATCH 053/113] Tests: Some more render/tests/test-component.js refactoring --- render/tests/test-component.js | 307 ++++++++++++++++++--------------- 1 file changed, 171 insertions(+), 136 deletions(-) diff --git a/render/tests/test-component.js b/render/tests/test-component.js index 64cd6fcb..6398eaa0 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -656,6 +656,130 @@ 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){ + // #1638, the assertion passes for `oninit` because both values are wrong. + if (hook !== 'oncreate') 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) { + // #1638 + if (hook !== "oninit" && hook !== 'oncreate') 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.spec("state", function() { o("initializes state", function() { @@ -675,7 +799,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 called = 0 var body = {a: 1} var data = [body] @@ -698,11 +822,10 @@ o.spec("component", function() { }) }) o.spec("Tests specific to certain component kinds", function() { - - o.spec("POJO state", function() { - o("copies state", function() { + o.spec("state", function() { + o("POJO", function() { var called = 0 - var data = {a: 1} + var data = {} var component = { data: data, oninit: init, @@ -721,142 +844,54 @@ 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) + // #1638 + // 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 + var context - var context + render(root, [{tag: component, attrs: {oninit: oninit}}]) + render(root, [{tag: component, attrs: {oninit: oninit}}]) + render(root, []) - 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) + // #1638 + // 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) }) }) }) From fc038f9d859e5e458fb50938f7ef446337d5d620 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 22:57:31 +0100 Subject: [PATCH 054/113] Tests: enable tests for #1638 --- render/tests/test-component.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/render/tests/test-component.js b/render/tests/test-component.js index 6398eaa0..212905d2 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -747,8 +747,7 @@ o.spec("component", function() { ] hooks.forEach(function(hook) { attrs[hook] = o.spy(function(vnode){ - // #1638, the assertion passes for `oninit` because both values are wrong. - if (hook !== 'oncreate') o(this).equals(vnode.state)(hook) + o(this).equals(vnode.state)(hook) }) methods[hook] = o.spy(function(vnode){ o(this).equals(vnode.state) @@ -762,8 +761,7 @@ o.spec("component", function() { render(root, []) hooks.forEach(function(hook) { - // #1638 - if (hook !== "oninit" && hook !== 'oncreate') o(attrs[hook].this).equals(methods.view.this)(hook) + o(attrs[hook].this).equals(methods.view.this)(hook) o(methods[hook].this).equals(methods.view.this)(hook) }) @@ -848,8 +846,7 @@ o.spec("component", function() { var oninit = o.spy() var component = o.spy(function(vnode){ o(vnode.state).equals(null) - // #1638 - // o(oninit.callCount).equals(0) + o(oninit.callCount).equals(0) }) var view = o.spy(function(){ o(this instanceof component).equals(true) @@ -877,8 +874,7 @@ o.spec("component", function() { }) var component = o.spy(function(vnode) { o(vnode.state).equals(null) - // #1638 - // o(oninit.callCount).equals(0) + o(oninit.callCount).equals(0) return state = { view: view } From 7668ddd120c00f4152cdaf5591043e5ce6bff70e Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sun, 19 Feb 2017 23:00:34 +0100 Subject: [PATCH 055/113] fix #1638 --- render/render.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/render/render.js b/render/render.js index ea015ebd..eb31229b 100644 --- a/render/render.js +++ b/render/render.js @@ -20,8 +20,8 @@ module.exports = function($window) { } function createNode(parent, vnode, hooks, ns, nextSibling) { var tag = vnode.tag - if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) if (typeof tag === "string") { + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) switch (tag) { case "#": return createText(parent, vnode, nextSibling) case "<": return createHTML(parent, vnode, nextSibling) @@ -116,6 +116,7 @@ module.exports = function($window) { sentinel.$$reentrantLock$$ = true } + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) initLifecycle(vnode.state, vnode, hooks) vnode.instance = Vnode.normalize(vnode.state.view(vnode)) sentinel.$$reentrantLock$$ = null From e496f7bfa6e222fa07fea84effa1c55ddf127069 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Mon, 20 Feb 2017 16:46:34 +0100 Subject: [PATCH 056/113] Test: ensure that recycled components get a fresh state --- render/tests/test-component.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/render/tests/test-component.js b/render/tests/test-component.js index 212905d2..0ff4e07e 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -776,7 +776,28 @@ o.spec("component", function() { 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() { From 6a7144fc89b63944110ca4ce3169adcbbfee34a0 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Mon, 20 Feb 2017 20:29:18 +0100 Subject: [PATCH 057/113] Tests: add a test for onbeforeupdate and recycled nodes --- render/tests/test-onbeforeupdate.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/render/tests/test-onbeforeupdate.js b/render/tests/test-onbeforeupdate.js index cf16dd1c..93643409 100644 --- a/render/tests/test-onbeforeupdate.js +++ b/render/tests/test-onbeforeupdate.js @@ -120,6 +120,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 From f6e627a8916ac923eb2778a92c07031025ed493f Mon Sep 17 00:00:00 2001 From: Gampol T Date: Thu, 23 Feb 2017 17:45:25 +0700 Subject: [PATCH 058/113] fix state.update not destroy when edit text blank if (state.editing.title === "") should call state.destroy() instead of destroy() --- examples/todomvc/todomvc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/todomvc/todomvc.js b/examples/todomvc/todomvc.js index 1f1f257a..59099cf5 100644 --- a/examples/todomvc/todomvc.js +++ b/examples/todomvc/todomvc.js @@ -38,7 +38,7 @@ var state = { update: function(title) { if (state.editing != null) { state.editing.title = title.trim() - if (state.editing.title === "") destroy(state.editing) + if (state.editing.title === "") state.destroy(state.editing) state.editing = null } }, From 450570d4c9f2bc6da646d357d4fa59bd934f347f Mon Sep 17 00:00:00 2001 From: Gampol T Date: Thu, 23 Feb 2017 17:48:13 +0700 Subject: [PATCH 059/113] fix ui.save not detect escape key change event from onkeypress to onkeyup because onkeypress cannot detect escape key press if (e.keyCode === 27) --- examples/todomvc/todomvc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/todomvc/todomvc.js b/examples/todomvc/todomvc.js index 59099cf5..83d2a822 100644 --- a/examples/todomvc/todomvc.js +++ b/examples/todomvc/todomvc.js @@ -104,7 +104,7 @@ var Todos = { m("label", {ondblclick: function() {state.dispatch("edit", [todo])}}, todo.title), m("button.destroy", {onclick: function() {state.dispatch("destroy", [todo])}}), ]), - m("input.edit", {onupdate: function(vnode) {ui.focus(vnode, todo)}, onkeypress: ui.save, onblur: ui.save}) + m("input.edit", {onupdate: function(vnode) {ui.focus(vnode, todo)}, onkeyup: ui.save, onblur: ui.save}) ]) }), ]), From c501f0fd810f3882f1968dab9ea65d4b5448d8af Mon Sep 17 00:00:00 2001 From: Gandalf-the-Bot Date: Thu, 23 Feb 2017 23:40:18 +0000 Subject: [PATCH 060/113] Bundled output for commit d29c16e0a68d5d26959473daa8ab690f72abce98 [skip ci] --- mithril.js | 2 +- mithril.min.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mithril.js b/mithril.js index c2eb76e9..ff4bb92c 100644 --- a/mithril.js +++ b/mithril.js @@ -238,7 +238,7 @@ var _8 = function($window, Promise) { var promise0 = new Promise(function(resolve, reject) { if (args.method == null) args.method = "GET" args.method = args.method.toUpperCase() - var useBody = typeof args.useBody === "boolean" ? args.useBody : args.method !== "GET" && args.method !== "TRACE" + var useBody = (args.method === "GET" || args.method === "TRACE") ? false : (typeof args.useBody === "boolean" ? args.useBody : true) if (typeof args.serialize !== "function") args.serialize = typeof FormData !== "undefined" && args.data instanceof FormData ? function(value) {return value} : JSON.stringify if (typeof args.deserialize !== "function") args.deserialize = deserialize if (typeof args.extract !== "function") args.extract = extract diff --git a/mithril.min.js b/mithril.min.js index 7dbf8beb..8ef08ded 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -8,8 +8,8 @@ typeof a)h(c);else try{g(a(c))}catch(A){l&&l(A)}});"function"===typeof f.retry&& 0;kc.indexOf("?")?"?":"&";c+=f+d}return c}function k(c){try{return""!==c?JSON.parse(c):null}catch(A){throw Error(c);}} -function m(c){return c.responseText}function n(c,a){if("function"===typeof c)if(Array.isArray(a))for(var d=0;dp.status||304===p.status)d(n(c.type,a));else{var q=Error(p.responseText),b;for(b in a)q[b]= a[b];f(q)}}catch(e){f(e)}};h&&null!=c.data?p.send(c.data):p.send()});return!0===c.background?A:t(A)},jsonp:function(c,k){var m=h();c=f(c,k);var x=new d(function(d,f){var h=c.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+t++,k=a.document.createElement("script");a[h]=function(f){k.parentNode.removeChild(k);d(n(c.type,f));delete a[h]};k.onerror=function(){k.parentNode.removeChild(k);f(Error("JSONP request failed"));delete a[h]};null==c.data&&(c.data={});c.url=g(c.url,c.data);c.data[c.callbackKey|| "callback"]=h;k.src=l(c.url,c.data);a.document.documentElement.appendChild(k)});return!0===c.background?x:m(x)},setCompletionCallback:function(a){x=a}}}(window,v),N=function(a){function d(a,b,e,c,d,f,g){for(;e Date: Thu, 23 Feb 2017 18:45:47 -0500 Subject: [PATCH 061/113] update linter --- package.json | 2 +- request/request.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 026dfe20..ad89e82a 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "postversion": "git push --follow-tags" }, "devDependencies": { - "eslint": "^2.10.2", + "eslint": "^3.16.1", "istanbul": "^0.4.3", "marked": "^0.3.6" }, diff --git a/request/request.js b/request/request.js index 9088b3b0..76e218c9 100644 --- a/request/request.js +++ b/request/request.js @@ -2,7 +2,7 @@ var buildQueryString = require("../querystring/build") -var FILE_PROTOCOL_REGEX = new RegExp('^file://', 'i') +var FILE_PROTOCOL_REGEX = new RegExp("^file://", "i") module.exports = function($window, Promise) { var callbackCount = 0 From 3786373b587a7374e9df961d83a26baad06ef0d5 Mon Sep 17 00:00:00 2001 From: Gandalf-the-Bot Date: Thu, 23 Feb 2017 23:46:47 +0000 Subject: [PATCH 062/113] Bundled output for commit ed5d9a2711ac6f50be907377b7ad1850c60f92c1 [skip ci] --- mithril.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mithril.js b/mithril.js index 7f9ae2f1..5d3c46e5 100644 --- a/mithril.js +++ b/mithril.js @@ -203,7 +203,7 @@ var buildQueryString = function(object) { else args.push(encodeURIComponent(key0) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : "")) } } -var FILE_PROTOCOL_REGEX = new RegExp('^file://', 'i') +var FILE_PROTOCOL_REGEX = new RegExp("^file://", "i") var _8 = function($window, Promise) { var callbackCount = 0 var oncompletion From 3e7649ef06faa7dbdb7ca69a61dada993e8b70ee Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Fri, 24 Feb 2017 09:26:26 +0100 Subject: [PATCH 063/113] Fix recycled components initialization fix #1641 --- render/render.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/render/render.js b/render/render.js index eb31229b..19ca6ebe 100644 --- a/render/render.js +++ b/render/render.js @@ -100,7 +100,7 @@ module.exports = function($window) { } return element } - function createComponent(parent, vnode, hooks, ns, nextSibling) { + function initComponent(vnode, hooks) { var sentinel if (typeof vnode.tag === "function") { vnode.state = null @@ -120,7 +120,9 @@ module.exports = function($window) { initLifecycle(vnode.state, vnode, hooks) vnode.instance = Vnode.normalize(vnode.state.view(vnode)) sentinel.$$reentrantLock$$ = null - + } + function createComponent(parent, vnode, hooks, ns, nextSibling) { + initComponent(vnode, hooks) if (vnode.instance != null) { 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) @@ -233,11 +235,12 @@ module.exports = function($window) { if (oldTag === tag) { vnode.state = old.state vnode.events = old.events - if (shouldUpdate(vnode, old)) return - if (vnode.attrs != null) { - updateLifecycle(vnode.attrs, vnode, hooks, recycling) - } + if (!recycling && shouldNotUpdate(vnode, old)) return if (typeof oldTag === "string") { + if (vnode.attrs != null) { + if (recycling) initLifecycle(vnode.attrs, vnode, hooks) + else updateLifecycle(vnode.attrs, vnode, hooks) + } switch (oldTag) { case "#": updateText(old, vnode); break case "<": updateHTML(parent, old, vnode, nextSibling); break @@ -307,8 +310,13 @@ module.exports = function($window) { } } function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) { - vnode.instance = Vnode.normalize(vnode.state.view(vnode)) - updateLifecycle(vnode.state, vnode, hooks, recycling) + if (recycling) { + initComponent(vnode, hooks) + } else { + vnode.instance = Vnode.normalize(vnode.state.view(vnode)) + if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks) + updateLifecycle(vnode.state, vnode, hooks) + } if (vnode.instance != null) { if (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling) else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns) @@ -562,11 +570,10 @@ module.exports = function($window) { if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode) if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode)) } - function updateLifecycle(source, vnode, hooks, recycling) { - if (recycling) initLifecycle(source, vnode, hooks) - else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) + function updateLifecycle(source, vnode, hooks) { + if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) } - function shouldUpdate(vnode, old) { + function shouldNotUpdate(vnode, old) { var forceVnodeUpdate, forceComponentUpdate 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) From 71dcde6adf05c090c50a8ea1af47e37e4d147cd9 Mon Sep 17 00:00:00 2001 From: Barney Carroll Date: Fri, 24 Feb 2017 10:54:07 +0000 Subject: [PATCH 064/113] Warn about data models w key props passed as attrs Hint to avoid problems such as those found here [February 23, 2017 6:28 PM](https://gitter.im/lhorie/mithril.js?at=58af29c47ceae5376a2d470e) --- docs/keys.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/keys.md b/docs/keys.md index 2c1e46d7..cfca7cdb 100644 --- a/docs/keys.md +++ b/docs/keys.md @@ -177,3 +177,29 @@ m("div", [ ] ]) ``` + +#### Avoid passing model data directly to components if the model uses `key` as a data property + +The `key` property may appear in your data model in a way that conflicts with Mithril's key logic. For example, a component may represent an entity whose `key` property is expected to change over time. This can lead to components receiving the wrong data, re-initialise, or change positions unexpectedly. If your data model uses the `key` property, make sure to wrap the data such that Mithril doesn't misinterpret it as a rendering instruction: + +```javascript +// Data model +var users = [ + {id: 1, name: "John", key: 'a'}, + {id: 2, name: "Mary", key: 'b'}, +] + +// Later on... +users[0].key = 'c' + +// AVOID +users.map(function(user){ + // The component for John will be destroyed and recreated + return m(UserComponent, user) +}) + +// PREFER +users.map(function(user){ + // Key is specifically extracted: data model is given its own property + return m(UserComponent, {key: user.id, model: user}) +}) From 97c60f9046cc46cc81bd4d5b4f991a7b2ca6faf1 Mon Sep 17 00:00:00 2001 From: Barney Carroll Date: Fri, 24 Feb 2017 13:35:42 +0000 Subject: [PATCH 065/113] Close code blocks! --- docs/keys.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/keys.md b/docs/keys.md index cfca7cdb..d6b16f8c 100644 --- a/docs/keys.md +++ b/docs/keys.md @@ -203,3 +203,4 @@ users.map(function(user){ // Key is specifically extracted: data model is given its own property return m(UserComponent, {key: user.id, model: user}) }) +``` From 60e8f307f19a0106722b3d7c05e14ae7b3fab456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Yves=20G=C3=A9rardy?= Date: Sat, 25 Feb 2017 21:53:51 +0100 Subject: [PATCH 066/113] 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 --- api/tests/index.html | 1 + api/tests/test-mount.js | 443 ++++--- api/tests/test-router.js | 2 +- render/render.js | 34 +- render/tests/index.html | 1 + render/tests/test-component.js | 1693 ++++++++++++++------------- render/tests/test-onbeforeremove.js | 68 +- render/tests/test-onbeforeupdate.js | 352 +++--- render/tests/test-onremove.js | 75 +- render/tests/test-render.js | 7 +- render/tests/test-updateNodes.js | 75 +- test-utils/components.js | 27 + test-utils/tests/index.html | 2 + test-utils/tests/test-components.js | 54 + tests/test-api.js | 187 +-- 15 files changed, 1615 insertions(+), 1406 deletions(-) create mode 100644 test-utils/components.js create mode 100644 test-utils/tests/test-components.js diff --git a/api/tests/index.html b/api/tests/index.html index fd2557d5..37d313f4 100644 --- a/api/tests/index.html +++ b/api/tests/index.html @@ -13,6 +13,7 @@ + diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js index 038f470a..4ba466c7 100644 --- a/api/tests/test-mount.js +++ b/api/tests/test-mount.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var m = require("../../render/hyperscript") @@ -22,16 +23,6 @@ o.spec("mount", function() { 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() { var threw = false try { @@ -42,227 +33,223 @@ o.spec("mount", function() { o(threw).equals(true) }) - o("renders into `root` (POJO component)", function() { - mount(root, { - view : function() { - return m("div") - } - }) + components.forEach(function(cmp){ + o.spec(cmp.kind, function(){ + var createComponent = cmp.create - 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("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(root.firstChild.nodeName).equals("DIV") - }) - - o("mounting null unmounts", function() { - mount(root, { - view : function() { - return m("div") - } - }) - - mount(root, null) - - o(root.childNodes.length).equals(0) - }) - - o("redraws on events", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var onclick = o.spy() - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - mount(root, { - view : function() { - return m("div", { - oninit : oninit, - onupdate : onupdate, - onclick : onclick, - }) - } - }) - - root.firstChild.dispatchEvent(e) - - o(oninit.callCount).equals(1) - o(onupdate.callCount).equals(0) - - o(onclick.callCount).equals(1) - o(onclick.this).equals(root.firstChild) - o(onclick.args[0].type).equals("click") - o(onclick.args[0].target).equals(root.firstChild) - - // Wrapped to give time for the rate-limited redraw to fire - setTimeout(function() { - o(onupdate.callCount).equals(1) - - done() - }, FRAME_BUDGET) - }) - - o("redraws several mount points on events", function(done, timeout) { - timeout(60) - - var onupdate0 = o.spy() - var oninit0 = o.spy() - var onclick0 = o.spy() - var onupdate1 = o.spy() - var oninit1 = o.spy() - var onclick1 = o.spy() - - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - render(root, [ - m("#child0"), - m("#child1") - ]) - - mount(root.childNodes[0], { - view : function() { - return m("div", { - oninit : oninit0, - onupdate : onupdate0, - onclick : onclick0, - }) - } - }) - - o(oninit0.callCount).equals(1) - o(onupdate0.callCount).equals(0) - - mount(root.childNodes[1], { - view : function() { - return m("div", { - oninit : oninit1, - onupdate : onupdate1, - onclick : onclick1, - }) - } - }) - - o(oninit1.callCount).equals(1) - o(onupdate1.callCount).equals(0) - - root.childNodes[0].firstChild.dispatchEvent(e) - o(onclick0.callCount).equals(1) - o(onclick0.this).equals(root.childNodes[0].firstChild) - - setTimeout(function() { - o(onupdate0.callCount).equals(1) - o(onupdate1.callCount).equals(1) - - root.childNodes[1].firstChild.dispatchEvent(e) - o(onclick1.callCount).equals(1) - o(onclick1.this).equals(root.childNodes[1].firstChild) - - setTimeout(function() { - o(onupdate0.callCount).equals(2) - o(onupdate1.callCount).equals(2) - - done() - }, FRAME_BUDGET) - }, FRAME_BUDGET) - - }) - - o("event handlers can skip redraw", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - mount(root, { - view: function() { - return m("div", { - oninit: oninit, - onupdate: onupdate, - onclick: function(e) { - e.redraw = false + o("renders into `root`", function() { + mount(root, createComponent({ + view : function() { + return m("div") } - }) - } + })) + + o(root.firstChild.nodeName).equals("DIV") + }) + + o("mounting null unmounts", function() { + mount(root, createComponent({ + view : function() { + return m("div") + } + })) + + mount(root, null) + + o(root.childNodes.length).equals(0) + }) + + o("redraws on events", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var onclick = o.spy() + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + mount(root, createComponent({ + view : function() { + return m("div", { + oninit : oninit, + onupdate : onupdate, + onclick : onclick, + }) + } + })) + + root.firstChild.dispatchEvent(e) + + o(oninit.callCount).equals(1) + o(onupdate.callCount).equals(0) + + o(onclick.callCount).equals(1) + o(onclick.this).equals(root.firstChild) + o(onclick.args[0].type).equals("click") + o(onclick.args[0].target).equals(root.firstChild) + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, FRAME_BUDGET) + }) + + o("redraws several mount points on events", function(done, timeout) { + timeout(60) + + var onupdate0 = o.spy() + var oninit0 = o.spy() + var onclick0 = o.spy() + var onupdate1 = o.spy() + var oninit1 = o.spy() + var onclick1 = o.spy() + + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + render(root, [ + m("#child0"), + m("#child1") + ]) + + mount(root.childNodes[0], createComponent({ + view : function() { + return m("div", { + oninit : oninit0, + onupdate : onupdate0, + onclick : onclick0, + }) + } + })) + + o(oninit0.callCount).equals(1) + o(onupdate0.callCount).equals(0) + + mount(root.childNodes[1], createComponent({ + view : function() { + return m("div", { + oninit : oninit1, + onupdate : onupdate1, + onclick : onclick1, + }) + } + })) + + o(oninit1.callCount).equals(1) + o(onupdate1.callCount).equals(0) + + root.childNodes[0].firstChild.dispatchEvent(e) + o(onclick0.callCount).equals(1) + o(onclick0.this).equals(root.childNodes[0].firstChild) + + setTimeout(function() { + o(onupdate0.callCount).equals(1) + o(onupdate1.callCount).equals(1) + + root.childNodes[1].firstChild.dispatchEvent(e) + o(onclick1.callCount).equals(1) + o(onclick1.this).equals(root.childNodes[1].firstChild) + + setTimeout(function() { + o(onupdate0.callCount).equals(2) + o(onupdate1.callCount).equals(2) + + done() + }, FRAME_BUDGET) + }, FRAME_BUDGET) + + }) + + o("event handlers can skip redraw", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + mount(root, createComponent({ + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate, + onclick: function(e) { + e.redraw = false + } + }) + } + })) + + root.firstChild.dispatchEvent(e) + + o(oninit.callCount).equals(1) + + // Wrapped to ensure no redraw fired + setTimeout(function() { + o(onupdate.callCount).equals(0) + + done() + }, FRAME_BUDGET) + }) + + o("redraws when the render function is run", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + + mount(root, createComponent({ + view : function() { + return m("div", { + oninit: oninit, + onupdate: onupdate + }) + } + })) + + o(oninit.callCount).equals(1) + o(onupdate.callCount).equals(0) + + redrawService.redraw() + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, FRAME_BUDGET) + }) + + o("throttles", function(done, timeout) { + timeout(200) + + var i = 0 + mount(root, createComponent({view: function() {i++}})) + var before = i + + redrawService.redraw() + redrawService.redraw() + redrawService.redraw() + redrawService.redraw() + + var after = i + + setTimeout(function(){ + o(before).equals(1) // mounts synchronously + o(after).equals(1) // throttles rest + o(i).equals(2) + done() + },40) + }) }) - - root.firstChild.dispatchEvent(e) - - o(oninit.callCount).equals(1) - - // Wrapped to ensure no redraw fired - setTimeout(function() { - o(onupdate.callCount).equals(0) - - done() - }, FRAME_BUDGET) }) - - o("redraws when the render function is run", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - - mount(root, { - view : function() { - return m("div", { - oninit: oninit, - onupdate: onupdate - }) - } - }) - - o(oninit.callCount).equals(1) - o(onupdate.callCount).equals(0) - - redrawService.redraw() - - // Wrapped to give time for the rate-limited redraw to fire - setTimeout(function() { - o(onupdate.callCount).equals(1) - - done() - }, FRAME_BUDGET) - }) - - o("throttles", function(done, timeout) { - timeout(200) - - var i = 0 - mount(root, {view: function() {i++}}) - var before = i - - redrawService.redraw() - redrawService.redraw() - redrawService.redraw() - redrawService.redraw() - - var after = i - - setTimeout(function(){ - o(before).equals(1) // mounts synchronously - o(after).equals(1) // throttles rest - o(i).equals(2) - done() - },40) - }) -}) +}) \ No newline at end of file diff --git a/api/tests/test-router.js b/api/tests/test-router.js index c624789b..f43605e1 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -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() function Cmp() {return {view: view}} diff --git a/render/render.js b/render/render.js index 483c2637..f5d35085 100644 --- a/render/render.js +++ b/render/render.js @@ -20,8 +20,8 @@ module.exports = function($window) { } function createNode(parent, vnode, hooks, ns, nextSibling) { var tag = vnode.tag - if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) if (typeof tag === "string") { + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) switch (tag) { case "#": return createText(parent, vnode, nextSibling) case "<": return createHTML(parent, vnode, nextSibling) @@ -100,7 +100,7 @@ module.exports = function($window) { } return element } - function createComponent(parent, vnode, hooks, ns, nextSibling) { + function initComponent(vnode, hooks) { var sentinel if (typeof vnode.tag === "function") { vnode.state = null @@ -116,10 +116,13 @@ module.exports = function($window) { sentinel.$$reentrantLock$$ = true } + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) initLifecycle(vnode.state, vnode, hooks) vnode.instance = Vnode.normalize(vnode.state.view(vnode)) sentinel.$$reentrantLock$$ = null - + } + function createComponent(parent, vnode, hooks, ns, nextSibling) { + initComponent(vnode, hooks) if (vnode.instance != null) { 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) @@ -232,11 +235,12 @@ module.exports = function($window) { if (oldTag === tag) { vnode.state = old.state vnode.events = old.events - if (shouldUpdate(vnode, old)) return - if (vnode.attrs != null) { - updateLifecycle(vnode.attrs, vnode, hooks, recycling) - } + if (!recycling && shouldNotUpdate(vnode, old)) return if (typeof oldTag === "string") { + if (vnode.attrs != null) { + if (recycling) initLifecycle(vnode.attrs, vnode, hooks) + else updateLifecycle(vnode.attrs, vnode, hooks) + } switch (oldTag) { case "#": updateText(old, vnode); 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) { - vnode.instance = Vnode.normalize(vnode.state.view(vnode)) - updateLifecycle(vnode.state, vnode, hooks, recycling) + if (recycling) { + initComponent(vnode, hooks) + } else { + vnode.instance = Vnode.normalize(vnode.state.view(vnode)) + if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks) + updateLifecycle(vnode.state, vnode, hooks) + } if (vnode.instance != null) { if (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling) 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.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode)) } - function updateLifecycle(source, vnode, hooks, recycling) { - if (recycling) initLifecycle(source, vnode, hooks) - else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) + function updateLifecycle(source, vnode, hooks) { + if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) } - function shouldUpdate(vnode, old) { + function shouldNotUpdate(vnode, old) { var forceVnodeUpdate, forceComponentUpdate 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) diff --git a/render/tests/index.html b/render/tests/index.html index 480b8b7c..b978ae6f 100644 --- a/render/tests/index.html +++ b/render/tests/index.html @@ -8,6 +8,7 @@ + diff --git a/render/tests/test-component.js b/render/tests/test-component.js index ed70fe7d..0ff4e07e 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -13,823 +14,901 @@ o.spec("component", function() { render = vdom($window).render }) - o.spec("basics", function() { - o("works", function() { - var component = { - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - var node = {tag: component} - - render(root, [node]) - - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("receives arguments", function() { - var component = { - view: function(vnode) { - return {tag: "div", attrs: vnode.attrs, text: vnode.text} - } - } - var node = {tag: component, attrs: {id: "a"}, text: "b"} - - render(root, [node]) - - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("updates", function() { - var component = { - view: function(vnode) { - return {tag: "div", attrs: vnode.attrs, text: vnode.text} - } - } - render(root, [{tag: component, attrs: {id: "a"}, text: "b"}]) - render(root, [{tag: component, attrs: {id: "c"}, text: "d"}]) - - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("c") - o(root.firstChild.firstChild.nodeValue).equals("d") - }) - o("updates root from null", function() { - var visible = false - var component = { - view: function(vnode) { - return visible ? {tag: "div"} : null - } - } - render(root, [{tag: component}]) - visible = true - render(root, [{tag: component}]) - - o(root.firstChild.nodeName).equals("DIV") - }) - o("updates root from primitive", function() { - var visible = false - var component = { - view: function(vnode) { - return visible ? {tag: "div"} : false - } - } - render(root, [{tag: component}]) - visible = true - render(root, [{tag: component}]) - - o(root.firstChild.nodeName).equals("DIV") - }) - o("updates root to null", function() { - var visible = true - var component = { - view: function(vnode) { - return visible ? {tag: "div"} : null - } - } - render(root, [{tag: component}]) - visible = false - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("updates root to primitive", function() { - var visible = true - var component = { - view: function(vnode) { - return visible ? {tag: "div"} : false - } - } - render(root, [{tag: component}]) - visible = false - render(root, [{tag: component}]) - - o(root.firstChild.nodeValue).equals("") - }) - o("updates root from null to null", function() { - var component = { - view: function(vnode) { - return null - } - } - render(root, [{tag: component}]) - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("removes", function() { - var component = { - view: function(vnode) { - return {tag: "div"} - } - } - var div = {tag: "div", key: 2} - render(root, [{tag: component, key: 1}, div]) - render(root, [{tag: "div", key: 2}]) - - o(root.childNodes.length).equals(1) - o(root.firstChild).equals(div.dom) - }) - o("svg works when creating across component boundary", function() { - var component = { - view: function(vnode) { - return {tag: "g"} - } - } - render(root, [{tag: "svg", children: [{tag: component}]}]) - - o(root.firstChild.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg") - }) - o("svg works when updating across component boundary", function() { - var component = { - view: function(vnode) { - 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.spec("return value", function() { - o("can return fragments", function() { - var component = { - view: function(vnode) { - return [ - {tag: "label"}, - {tag: "input"}, - ] - } - } - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(2) - o(root.childNodes[0].nodeName).equals("LABEL") - o(root.childNodes[1].nodeName).equals("INPUT") - }) - o("can return string", function() { - var component = { - view: function(vnode) { - return "a" - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("a") - }) - o("can return falsy string", function() { - var component = { - view: function(vnode) { - return "" - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("") - }) - o("can return number", function() { - var component = { - view: function(vnode) { - return 1 - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("1") - }) - o("can return falsy number", function() { - var component = { - view: function(vnode) { - return 0 - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("0") - }) - o("can return boolean", function() { - var component = { - view: function(vnode) { - return true - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("true") - }) - o("can return falsy boolean", function() { - var component = { - view: function(vnode) { - return false - } - } - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("") - }) - o("can return null", function() { - var component = { - view: function(vnode) { - return null - } - } - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("can return undefined", function() { - var component = { - view: function(vnode) { - return undefined - } - } - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("throws a custom error if it returns itself", function() { - // A view that returns its vnode would otherwise trigger an infinite loop - var component = { - view: function(vnode) { - return vnode - } - } - try { - render(root, [{tag: component}]) - } - catch (e) { - o(e instanceof Error).equals(true) - // Call stack exception is a RangeError - o(e instanceof RangeError).equals(false) - } - }) - o("can update when returning fragments", function() { - var component = { - view: function(vnode) { - return [ - {tag: "label"}, - {tag: "input"}, - ] - } - } - render(root, [{tag: component}]) - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(2) - o(root.childNodes[0].nodeName).equals("LABEL") - o(root.childNodes[1].nodeName).equals("INPUT") - }) - o("can update when returning primitive", function() { - var component = { - view: function(vnode) { - return "a" - } - } - render(root, [{tag: component}]) - render(root, [{tag: component}]) - - o(root.firstChild.nodeType).equals(3) - o(root.firstChild.nodeValue).equals("a") - }) - o("can update when returning null", function() { - var component = { - view: function(vnode) { - return null - } - } - render(root, [{tag: component}]) - render(root, [{tag: component}]) - - o(root.childNodes.length).equals(0) - }) - o("can remove when returning fragments", function() { - var component = { - view: function(vnode) { - return [ - {tag: "label"}, - {tag: "input"}, - ] - } - } - var div = {tag: "div", key: 2} - render(root, [{tag: component, key: 1}, div]) - - render(root, [{tag: "div", key: 2}]) - - o(root.childNodes.length).equals(1) - o(root.firstChild).equals(div.dom) - }) - o("can remove when returning primitive", function() { - var component = { - view: function(vnode) { - return "a" - } - } - var div = {tag: "div", key: 2} - render(root, [{tag: component, key: 1}, div]) - - render(root, [{tag: "div", key: 2}]) - - o(root.childNodes.length).equals(1) - o(root.firstChild).equals(div.dom) - }) - }) - o.spec("lifecycle", function() { - o("calls oninit", function() { - var called = 0 - var component = { - oninit: function(vnode) { - called++ - - o(vnode.tag).equals(component) - o(vnode.dom).equals(undefined) - o(root.childNodes.length).equals(0) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - var node = {tag: component} - - render(root, [node]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls oninit when returning fragment", function() { - var called = 0 - var component = { - oninit: function(vnode) { - called++ - - o(vnode.tag).equals(component) - o(vnode.dom).equals(undefined) - o(root.childNodes.length).equals(0) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - var node = {tag: component} - - render(root, [node]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls oninit before view", function() { - var viewCalled = false - - render(root, { - tag: { - view: function() { - viewCalled = true - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - }, - oninit: function(vnode) { - o(viewCalled).equals(false) - }, - } - }) - }) - o("does not calls oninit on redraw", function() { - var init = o.spy() - var component = { - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - }, - oninit: init, - } - - function view() { - return {tag: component} - } - - render(root, view()) - render(root, view()) - - o(init.callCount).equals(1) - }) - o("calls oncreate", function() { - var called = 0 - var component = { - oncreate: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - var node = {tag: component} - - render(root, [node]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("does not calls oncreate on redraw", function() { - var create = o.spy() - var component = { - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - }, - oncreate: create, - } - - function view() { - return {tag: component} - } - - render(root, view()) - render(root, view()) - - o(create.callCount).equals(1) - }) - o("calls oncreate when returning fragment", function() { - var called = 0 - var component = { - oncreate: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - var node = {tag: component} - - render(root, [node]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls onupdate", function() { - var called = 0 - var component = { - onupdate: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, [{tag: component}]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls onupdate when returning fragment", function() { - var called = 0 - var component = { - onupdate: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, [{tag: component}]) - - o(called).equals(1) - o(root.firstChild.nodeName).equals("DIV") - o(root.firstChild.attributes["id"].nodeValue).equals("a") - o(root.firstChild.firstChild.nodeValue).equals("b") - }) - o("calls onremove", function() { - var called = 0 - var component = { - onremove: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, []) - - o(called).equals(1) - o(root.childNodes.length).equals(0) - }) - o("calls onremove when returning fragment", function() { - var called = 0 - var component = { - onremove: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, []) - - o(called).equals(1) - o(root.childNodes.length).equals(0) - }) - o("calls onbeforeremove", function() { - var called = 0 - var component = { - onbeforeremove: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return {tag: "div", attrs: {id: "a"}, text: "b"} - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, []) - - o(called).equals(1) - o(root.childNodes.length).equals(0) - }) - o("calls onbeforeremove when returning fragment", function() { - var called = 0 - var component = { - onbeforeremove: function(vnode) { - called++ - - o(vnode.dom).notEquals(undefined) - o(vnode.dom).equals(root.firstChild) - o(root.childNodes.length).equals(1) - }, - view: function() { - return [{tag: "div", attrs: {id: "a"}, text: "b"}] - } - } - - render(root, [{tag: component}]) - - o(called).equals(0) - - render(root, []) - - o(called).equals(1) - o(root.childNodes.length).equals(0) - }) - o("does not recycle when there's an onupdate", function() { - var component = { - onupdate: function() {}, - view: function() { - return {tag: "div"} - } - } - var update = o.spy() - var vnode = {tag: component, key: 1} - var updated = {tag: component, key: 1} - - render(root, [vnode]) - render(root, []) - render(root, [updated]) - - o(vnode.dom).notEquals(updated.dom) - }) - }) - o.spec("state", function() { - o("copies state", function() { - var called = 0 - var data = {a: 1} - var component = { - data: data, - oninit: init, - view: function() { - return "" - } - } - - render(root, [{tag: component}]) - - function init(vnode) { - o(vnode.state.data).deepEquals(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() { - var called = 0 - var body = {a: 1} - var data = [body] - var component = { - data: data, - oninit: init, - view: function() { - return "" - } - } - - render(root, [{tag: component}]) - - function init(vnode) { - o(vnode.state.data).equals(data) - o(vnode.state.data[0]).equals(body) - } - }) - }) - o.spec("Alternative ways to specify componenents", function() { - o("Classes can be used as components", function() { - function MyComponent(vnode){ - o(vnode.state).equals(null) - } - var proto = MyComponent.prototype - - var context - - proto.oninit = o.spy(function(vnode) { - o(this).equals(vnode.state) - 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, []) - - 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("Factory 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 "" + components.forEach(function(cmp){ + o.spec(cmp.kind, function(){ + var createComponent = cmp.create + + o.spec("basics", function() { + o("works", function() { + var component = createComponent({ + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } }) + var node = {tag: component} + + render(root, [node]) + + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("receives arguments", function() { + var component = createComponent({ + view: function(vnode) { + return {tag: "div", attrs: vnode.attrs, text: vnode.text} + } + }) + var node = {tag: component, attrs: {id: "a"}, text: "b"} + + render(root, [node]) + + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("updates", function() { + var component = createComponent({ + view: function(vnode) { + return {tag: "div", attrs: vnode.attrs, text: vnode.text} + } + }) + render(root, [{tag: component, attrs: {id: "a"}, text: "b"}]) + render(root, [{tag: component, attrs: {id: "c"}, text: "d"}]) + + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("c") + o(root.firstChild.firstChild.nodeValue).equals("d") + }) + o("updates root from null", function() { + var visible = false + var component = createComponent({ + view: function(vnode) { + return visible ? {tag: "div"} : null + } + }) + render(root, [{tag: component}]) + visible = true + render(root, [{tag: component}]) + + o(root.firstChild.nodeName).equals("DIV") + }) + o("updates root from primitive", function() { + var visible = false + var component = createComponent({ + view: function(vnode) { + return visible ? {tag: "div"} : false + } + }) + render(root, [{tag: component}]) + visible = true + render(root, [{tag: component}]) + + o(root.firstChild.nodeName).equals("DIV") + }) + o("updates root to null", function() { + var visible = true + var component = createComponent({ + view: function(vnode) { + return visible ? {tag: "div"} : null + } + }) + render(root, [{tag: component}]) + visible = false + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(0) + }) + o("updates root to primitive", function() { + var visible = true + var component = createComponent({ + view: function(vnode) { + return visible ? {tag: "div"} : false + } + }) + render(root, [{tag: component}]) + visible = false + render(root, [{tag: component}]) + + o(root.firstChild.nodeValue).equals("") + }) + o("updates root from null to null", function() { + var component = createComponent({ + view: function(vnode) { + return null + } + }) + render(root, [{tag: component}]) + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(0) + }) + o("removes", function() { + var component = createComponent({ + view: function(vnode) { + return {tag: "div"} + } + }) + var div = {tag: "div", key: 2} + render(root, [{tag: component, key: 1}, div]) + render(root, [{tag: "div", key: 2}]) + + o(root.childNodes.length).equals(1) + o(root.firstChild).equals(div.dom) + }) + o("svg works when creating across component boundary", function() { + var component = createComponent({ + view: function(vnode) { + return {tag: "g"} + } + }) + render(root, [{tag: "svg", children: [{tag: component}]}]) + + o(root.firstChild.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg") + }) + o("svg works when updating across component boundary", function() { + var component = createComponent({ + view: function(vnode) { + 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.spec("return value", function() { + o("can return fragments", function() { + var component = createComponent({ + view: function(vnode) { + return [ + {tag: "label"}, + {tag: "input"}, + ] + } + }) + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(2) + o(root.childNodes[0].nodeName).equals("LABEL") + o(root.childNodes[1].nodeName).equals("INPUT") + }) + o("can return string", function() { + var component = createComponent({ + view: function(vnode) { + return "a" + } + }) + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("a") + }) + o("can return falsy string", function() { + var component = createComponent({ + view: function(vnode) { + return "" + } + }) + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("") + }) + o("can return number", function() { + var component = createComponent({ + view: function(vnode) { + return 1 + } + }) + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("1") + }) + o("can return falsy number", function() { + var component = createComponent({ + view: function(vnode) { + return 0 + } + }) + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("0") + }) + o("can return boolean", function() { + var component = createComponent({ + view: function(vnode) { + return true + } + }) + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("true") + }) + o("can return falsy boolean", function() { + var component = createComponent({ + view: function(vnode) { + return false + } + }) + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("") + }) + o("can return null", function() { + var component = createComponent({ + view: function(vnode) { + return null + } + }) + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(0) + }) + o("can return undefined", function() { + var component = createComponent({ + view: function(vnode) { + return undefined + } + }) + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(0) + }) + o("throws a custom error if it returns itself", function() { + // A view that returns its vnode would otherwise trigger an infinite loop + var component = createComponent({ + view: function(vnode) { + return vnode + } + }) + try { + render(root, [{tag: component}]) + } + catch (e) { + o(e instanceof Error).equals(true) + // Call stack exception is a RangeError + o(e instanceof RangeError).equals(false) + } + }) + o("can update when returning fragments", function() { + var component = createComponent({ + view: function(vnode) { + return [ + {tag: "label"}, + {tag: "input"}, + ] + } + }) + render(root, [{tag: component}]) + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(2) + o(root.childNodes[0].nodeName).equals("LABEL") + o(root.childNodes[1].nodeName).equals("INPUT") + }) + o("can update when returning primitive", function() { + var component = createComponent({ + view: function(vnode) { + return "a" + } + }) + render(root, [{tag: component}]) + render(root, [{tag: component}]) + + o(root.firstChild.nodeType).equals(3) + o(root.firstChild.nodeValue).equals("a") + }) + o("can update when returning null", function() { + var component = createComponent({ + view: function(vnode) { + return null + } + }) + render(root, [{tag: component}]) + render(root, [{tag: component}]) + + o(root.childNodes.length).equals(0) + }) + o("can remove when returning fragments", function() { + var component = createComponent({ + view: function(vnode) { + return [ + {tag: "label"}, + {tag: "input"}, + ] + } + }) + var div = {tag: "div", key: 2} + render(root, [{tag: component, key: 1}, div]) + + render(root, [{tag: "div", key: 2}]) + + o(root.childNodes.length).equals(1) + o(root.firstChild).equals(div.dom) + }) + o("can remove when returning primitive", function() { + var component = createComponent({ + view: function(vnode) { + return "a" + } + }) + var div = {tag: "div", key: 2} + render(root, [{tag: component, key: 1}, div]) + + render(root, [{tag: "div", key: 2}]) + + o(root.childNodes.length).equals(1) + o(root.firstChild).equals(div.dom) + }) + }) + o.spec("lifecycle", function() { + o("calls oninit", function() { + var called = 0 + var component = createComponent({ + oninit: function(vnode) { + called++ + + o(vnode.tag).equals(component) + o(vnode.dom).equals(undefined) + o(root.childNodes.length).equals(0) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + }) + var node = {tag: component} + + render(root, [node]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls oninit when returning fragment", function() { + var called = 0 + var component = createComponent({ + oninit: function(vnode) { + called++ + + o(vnode.tag).equals(component) + o(vnode.dom).equals(undefined) + o(root.childNodes.length).equals(0) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + }) + var node = {tag: component} + + render(root, [node]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls oninit before view", function() { + var viewCalled = false + + render(root, createComponent({ + tag: { + view: function() { + viewCalled = true + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + }, + oninit: function(vnode) { + o(viewCalled).equals(false) + }, + } + })) + }) + o("does not calls oninit on redraw", function() { + var init = o.spy() + var component = createComponent({ + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + }, + oninit: init, + }) + + function view() { + return {tag: component} + } + + render(root, view()) + render(root, view()) + + o(init.callCount).equals(1) + }) + o("calls oncreate", function() { + var called = 0 + var component = createComponent({ + oncreate: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + }) + var node = {tag: component} + + render(root, [node]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("does not calls oncreate on redraw", function() { + var create = o.spy() + var component = createComponent({ + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + }, + oncreate: create, + }) + + function view() { + return {tag: component} + } + + render(root, view()) + render(root, view()) + + o(create.callCount).equals(1) + }) + o("calls oncreate when returning fragment", function() { + var called = 0 + var component = createComponent({ + oncreate: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + }) + var node = {tag: component} + + render(root, [node]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls onupdate", function() { + var called = 0 + var component = createComponent({ + onupdate: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + }) + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, [{tag: component}]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls onupdate when returning fragment", function() { + var called = 0 + var component = createComponent({ + onupdate: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + }) + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, [{tag: component}]) + + o(called).equals(1) + o(root.firstChild.nodeName).equals("DIV") + o(root.firstChild.attributes["id"].nodeValue).equals("a") + o(root.firstChild.firstChild.nodeValue).equals("b") + }) + o("calls onremove", function() { + var called = 0 + var component = createComponent({ + onremove: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + }) + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, []) + + o(called).equals(1) + o(root.childNodes.length).equals(0) + }) + o("calls onremove when returning fragment", function() { + var called = 0 + var component = createComponent({ + onremove: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + }) + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, []) + + o(called).equals(1) + o(root.childNodes.length).equals(0) + }) + o("calls onbeforeremove", function() { + var called = 0 + var component = createComponent({ + onbeforeremove: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return {tag: "div", attrs: {id: "a"}, text: "b"} + } + }) + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, []) + + o(called).equals(1) + o(root.childNodes.length).equals(0) + }) + o("calls onbeforeremove when returning fragment", function() { + var called = 0 + var component = createComponent({ + onbeforeremove: function(vnode) { + called++ + + o(vnode.dom).notEquals(undefined) + o(vnode.dom).equals(root.firstChild) + o(root.childNodes.length).equals(1) + }, + view: function() { + return [{tag: "div", attrs: {id: "a"}, text: "b"}] + } + }) + + render(root, [{tag: component}]) + + o(called).equals(0) + + render(root, []) + + o(called).equals(1) + o(root.childNodes.length).equals(0) + }) + o("does not recycle when there's an onupdate", function() { + var component = createComponent({ + onupdate: function() {}, + view: function() { + return {tag: "div"} + } + }) + var update = o.spy() + var vnode = {tag: component, key: 1} + var updated = {tag: component, key: 1} + + render(root, [vnode]) + render(root, []) + render(root, [updated]) + + 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() { + var called = 0 + var data = {a: 1} + var component = createComponent(createComponent({ + data: data, + oninit: init, + view: function() { + return "" + } + })) + + render(root, [{tag: component}]) + + function init(vnode) { + o(vnode.state.data).equals(data) + } + }) + o('state proxies to the component object/prototype', function() { + var called = 0 + var body = {a: 1} + var data = [body] + var component = createComponent(createComponent({ + data: data, + oninit: init, + view: function() { + return "" + } + })) + + render(root, [{tag: component}]) + + function init(vnode) { + o(vnode.state.data).equals(data) + o(vnode.state.data[0]).equals(body) + } + }) + }) + }) + }) + o.spec("Tests specific to certain component kinds", function() { + o.spec("state", function() { + o("POJO", function() { + var called = 0 + var data = {} + var component = { + data: data, + oninit: init, + view: function() { + return "" + } } - } - render(root, [{tag: component}]) + render(root, [{tag: component}]) - o(state).equals(context) + function init(vnode) { + o(vnode.state.data).equals(data) - 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) + //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 - render(root, [{tag: component}]) + var context - 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(component.callCount).equals(1) + o(oninit.callCount).equals(2) + o(view.callCount).equals(2) + }) + 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 + } + }) - 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) + render(root, [{tag: component, attrs: {oninit: oninit}}]) + render(root, [{tag: component, attrs: {oninit: oninit}}]) + render(root, []) - 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) + o(component.callCount).equals(1) + o(oninit.callCount).equals(1) + o(view.callCount).equals(2) + }) }) }) }) diff --git a/render/tests/test-onbeforeremove.js b/render/tests/test-onbeforeremove.js index c9af4894..4395f90a 100644 --- a/render/tests/test-onbeforeremove.js +++ b/render/tests/test-onbeforeremove.js @@ -2,6 +2,7 @@ var o = require("../../ospec/ospec") var callAsync = require("../../test-utils/callAsync") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") var Promise = require("../../promise/promise") @@ -169,39 +170,44 @@ o.spec("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 onbeforeremove = function(){return Promise.resolve()} - var component = { - onbeforeremove: onbeforeremove, - onremove: onremove, - view: function() {}, - } - render(root, [{tag: component, attrs: {onbeforeremove: onbeforeremove, onremove: onremove}}]) - render(root, []) - callAsync(function() { - o(onremove.callCount).equals(2) // once for `tag`, once for `attrs` - done() - }) - }) - o("awaits promise resolution before removing the node", function(done) { - var view = o.spy() - var onremove = o.spy() - var onbeforeremove = function(){return new Promise(function(resolve){callAsync(resolve)})} - var component = { - onbeforeremove: onbeforeremove, - onremove: onremove, - view: view, - } - render(root, [{tag: component}]) - render(root, []) + 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) { + var onremove = o.spy() + var onbeforeremove = function(){return Promise.resolve()} + var component = createComponent({ + onbeforeremove: onbeforeremove, + onremove: onremove, + view: function() {}, + }) + render(root, [{tag: component, attrs: {onbeforeremove: onbeforeremove, onremove: onremove}}]) + render(root, []) + callAsync(function() { + o(onremove.callCount).equals(2) // once for `tag`, once for `attrs` + done() + }) + }) + o("awaits promise resolution before removing the node", function(done) { + var view = o.spy() + var onremove = o.spy() + var onbeforeremove = function(){return new Promise(function(resolve){callAsync(resolve)})} + var component = createComponent({ + onbeforeremove: onbeforeremove, + onremove: onremove, + view: view, + }) + render(root, [{tag: component}]) + render(root, []) - callAsync(function(){ - o(onremove.callCount).equals(0) + callAsync(function(){ + o(onremove.callCount).equals(0) - callAsync(function() { - o(onremove.callCount).equals(1) - done() + callAsync(function() { + o(onremove.callCount).equals(1) + done() + }) + }) }) }) }) diff --git a/render/tests/test-onbeforeupdate.js b/render/tests/test-onbeforeupdate.js index 8b326b8c..93643409 100644 --- a/render/tests/test-onbeforeupdate.js +++ b/render/tests/test-onbeforeupdate.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -56,86 +57,6 @@ o.spec("onbeforeupdate", function() { 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() { var onbeforeupdate = function() {return true} 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("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() { var count = 0 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("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() { var count = 0 var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} @@ -226,28 +104,6 @@ o.spec("onbeforeupdate", function() { 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() { var count = 0 var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} @@ -264,26 +120,192 @@ o.spec("onbeforeupdate", function() { o(count).equals(1) }) - o("is called only once on component update", function() { - var component = { - onbeforeupdate: onbeforeupdate, - view: function(vnode) { - return {tag: "div", attrs: vnode.attrs} - }, - } + 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}}] - var count = 0 - var vnode = {tag: component, attrs: {id: "a"}} - var updated = {tag: component, attrs: {id: "b"}} + render(root, vnodes) + render(root, temp) + render(root, updated) - render(root, [vnode]) - render(root, [updated]) - - function onbeforeupdate(vnode, old) { - count++ - return true - } - - o(count).equals(1) + 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, + 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 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 vnode = {tag: component, attrs: {id: "a"}} + var updated = {tag: component, attrs: {id: "b"}} + + render(root, [vnode]) + render(root, [updated]) + + function onbeforeupdate(vnode, old) { + count++ + return true + } + + o(count).equals(1) + }) + }) + }) +}) \ No newline at end of file diff --git a/render/tests/test-onremove.js b/render/tests/test-onremove.js index d3f423fc..a7f88a6b 100644 --- a/render/tests/test-onremove.js +++ b/render/tests/test-onremove.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") var m = require("../../render/hyperscript") @@ -80,39 +81,6 @@ o.spec("onremove", function() { o(remove.this).equals(vnode.state) 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() { var remove = o.spy() var vnode = {tag: "div", attrs: {onremove: remove}, children: []} @@ -145,4 +113,43 @@ o.spec("onremove", function() { 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) + }) + }) + }) +}) \ No newline at end of file diff --git a/render/tests/test-render.js b/render/tests/test-render.js index 2f3ebb9a..82e5ddba 100644 --- a/render/tests/test-render.js +++ b/render/tests/test-render.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -118,7 +119,7 @@ o.spec("render", function() { o(oninit.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 onbeforeupdate = o.spy() function A() { @@ -141,7 +142,7 @@ o.spec("render", function() { o(oninit.callCount).equals(1) 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 onbeforeupdate = o.spy() function A() { @@ -164,7 +165,7 @@ o.spec("render", function() { o(oninit.callCount).equals(1) 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() { throw new Error("error") } diff --git a/render/tests/test-updateNodes.js b/render/tests/test-updateNodes.js index b0e8c337..c58a29b4 100644 --- a/render/tests/test-updateNodes.js +++ b/render/tests/test-updateNodes.js @@ -1,6 +1,7 @@ "use strict" var o = require("../../ospec/ospec") +var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -838,38 +839,6 @@ o.spec("updateNodes", function() { o(root.childNodes[0].nodeName).equals("A") 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 () { var onupdate = o.spy(); var cached = {tag:"a", attrs:{onupdate: onupdate}} @@ -926,7 +895,7 @@ o.spec("updateNodes", function() { o(update.callCount).equals(2) 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 updated = {tag: "b"} @@ -936,4 +905,42 @@ o.spec("updateNodes", function() { 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") + }) + }) + }) +}) \ No newline at end of file diff --git a/test-utils/components.js b/test-utils/components.js new file mode 100644 index 00000000..c25ad5da --- /dev/null +++ b/test-utils/components.js @@ -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 + } + } + } +] diff --git a/test-utils/tests/index.html b/test-utils/tests/index.html index e24fa2f8..51b04d73 100644 --- a/test-utils/tests/index.html +++ b/test-utils/tests/index.html @@ -14,12 +14,14 @@ + + diff --git a/test-utils/tests/test-components.js b/test-utils/tests/test-components.js new file mode 100644 index 00000000..cc9a091b --- /dev/null +++ b/test-utils/tests/test-components.js @@ -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)) + }) +}) diff --git a/tests/test-api.js b/tests/test-api.js index 989b00a4..ec57a0f3 100644 --- a/tests/test-api.js +++ b/tests/test-api.js @@ -2,6 +2,7 @@ var o = require("../ospec/ospec") var browserMock = require("../test-utils/browserMock") +var components = require("../test-utils/components") o.spec("api", function() { var m @@ -68,95 +69,6 @@ o.spec("api", function() { o(query).equals("a=1&b=2") }) }) - o.spec("m.render", function() { - o("works", function() { - var root = window.document.createElement("div") - m.render(root, m("div")) - - o(root.childNodes.length).equals(1) - o(root.firstChild.nodeName).equals("DIV") - }) - }) - o.spec("m.mount", function() { - o("works", function() { - var root = window.document.createElement("div") - m.mount(root, {view: function() {return m("div")}}) - - o(root.childNodes.length).equals(1) - o(root.firstChild.nodeName).equals("DIV") - }) - }) - o.spec("m.route", function() { - o("works", function(done) { - var root = window.document.createElement("div") - m.route(root, "/a", { - "/a": {view: function() {return m("div")}} - }) - - setTimeout(function() { - o(root.childNodes.length).equals(1) - o(root.firstChild.nodeName).equals("DIV") - - done() - }, FRAME_BUDGET) - }) - o("m.route.prefix", function(done) { - var root = window.document.createElement("div") - m.route.prefix("#") - m.route(root, "/a", { - "/a": {view: function() {return m("div")}} - }) - - setTimeout(function() { - o(root.childNodes.length).equals(1) - o(root.firstChild.nodeName).equals("DIV") - - done() - }, FRAME_BUDGET) - }) - o("m.route.get", function(done) { - var root = window.document.createElement("div") - m.route(root, "/a", { - "/a": {view: function() {return m("div")}} - }) - - setTimeout(function() { - o(m.route.get()).equals("/a") - - done() - }, FRAME_BUDGET) - }) - o("m.route.set", function(done, timeout) { - timeout(100) - var root = window.document.createElement("div") - m.route(root, "/a", { - "/:id": {view: function() {return m("div")}} - }) - - setTimeout(function() { - m.route.set("/b") - setTimeout(function() { - o(m.route.get()).equals("/b") - - done() - }, FRAME_BUDGET) - }, FRAME_BUDGET) - }) - }) - o.spec("m.redraw", function() { - o("works", function(done) { - var count = 0 - var root = window.document.createElement("div") - m.mount(root, {view: function() {count++}}) - setTimeout(function() { - m.redraw() - - o(count).equals(2) - - done() - }, FRAME_BUDGET) - }) - }) o.spec("m.request", function() { o("works", function() { o(typeof m.request).equals("function") // TODO improve @@ -167,4 +79,99 @@ o.spec("api", function() { o(typeof m.jsonp).equals("function") // TODO improve }) }) -}) + o.spec("m.render", function() { + o("works", function() { + var root = window.document.createElement("div") + m.render(root, m("div")) + + o(root.childNodes.length).equals(1) + 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("works", function() { + var root = window.document.createElement("div") + m.mount(root, createComponent({view: function() {return m("div")}})) + + o(root.childNodes.length).equals(1) + o(root.firstChild.nodeName).equals("DIV") + }) + }) + o.spec("m.route", function() { + o("works", function(done) { + var root = window.document.createElement("div") + m.route(root, "/a", { + "/a": createComponent({view: function() {return m("div")}}) + }) + + setTimeout(function() { + o(root.childNodes.length).equals(1) + o(root.firstChild.nodeName).equals("DIV") + + done() + }, FRAME_BUDGET) + }) + o("m.route.prefix", function(done) { + var root = window.document.createElement("div") + m.route.prefix("#") + m.route(root, "/a", { + "/a": createComponent({view: function() {return m("div")}}) + }) + + setTimeout(function() { + o(root.childNodes.length).equals(1) + o(root.firstChild.nodeName).equals("DIV") + + done() + }, FRAME_BUDGET) + }) + o("m.route.get", function(done) { + var root = window.document.createElement("div") + m.route(root, "/a", { + "/a": createComponent({view: function() {return m("div")}}) + }) + + setTimeout(function() { + o(m.route.get()).equals("/a") + + done() + }, FRAME_BUDGET) + }) + o("m.route.set", function(done, timeout) { + timeout(100) + var root = window.document.createElement("div") + m.route(root, "/a", { + "/:id": createComponent({view: function() {return m("div")}}) + }) + + setTimeout(function() { + m.route.set("/b") + setTimeout(function() { + o(m.route.get()).equals("/b") + + done() + }, FRAME_BUDGET) + }, FRAME_BUDGET) + }) + }) + o.spec("m.redraw", function() { + o("works", function(done) { + var count = 0 + var root = window.document.createElement("div") + m.mount(root, createComponent({view: function() {count++}})) + setTimeout(function() { + m.redraw() + + o(count).equals(2) + + done() + }, FRAME_BUDGET) + }) + }) + }) + }) +}) \ No newline at end of file From 64792cbb11fb774a26ecf26c2dafdcfe708089a8 Mon Sep 17 00:00:00 2001 From: Gandalf-the-Bot Date: Sat, 25 Feb 2017 20:55:13 +0000 Subject: [PATCH 067/113] Bundled output for commit 60e8f307f19a0106722b3d7c05e14ae7b3fab456 [skip ci] --- mithril.js | 33 ++++++++++++------- mithril.min.js | 86 +++++++++++++++++++++++++------------------------- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/mithril.js b/mithril.js index 5d3c46e5..b7f11d41 100644 --- a/mithril.js +++ b/mithril.js @@ -368,8 +368,8 @@ var coreRenderer = function($window) { } function createNode(parent, vnode, hooks, ns, nextSibling) { var tag = vnode.tag - if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) if (typeof tag === "string") { + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) switch (tag) { case "#": return createText(parent, vnode, nextSibling) case "<": return createHTML(parent, vnode, nextSibling) @@ -442,7 +442,7 @@ var coreRenderer = function($window) { } return element } - function createComponent(parent, vnode, hooks, ns, nextSibling) { + function initComponent(vnode, hooks) { var sentinel if (typeof vnode.tag === "function") { vnode.state = null @@ -457,9 +457,13 @@ var coreRenderer = function($window) { if (sentinel.$$reentrantLock$$ != null) return $emptyFragment sentinel.$$reentrantLock$$ = true } + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) initLifecycle(vnode.state, vnode, hooks) vnode.instance = Vnode.normalize(vnode.state.view(vnode)) sentinel.$$reentrantLock$$ = null + } + function createComponent(parent, vnode, hooks, ns, nextSibling) { + initComponent(vnode, hooks) if (vnode.instance != null) { 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) @@ -570,11 +574,12 @@ var coreRenderer = function($window) { if (oldTag === tag) { vnode.state = old.state vnode.events = old.events - if (shouldUpdate(vnode, old)) return - if (vnode.attrs != null) { - updateLifecycle(vnode.attrs, vnode, hooks, recycling) - } + if (!recycling && shouldNotUpdate(vnode, old)) return if (typeof oldTag === "string") { + if (vnode.attrs != null) { + if (recycling) initLifecycle(vnode.attrs, vnode, hooks) + else updateLifecycle(vnode.attrs, vnode, hooks) + } switch (oldTag) { case "#": updateText(old, vnode); break case "<": updateHTML(parent, old, vnode, nextSibling); break @@ -644,8 +649,13 @@ var coreRenderer = function($window) { } } function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) { - vnode.instance = Vnode.normalize(vnode.state.view(vnode)) - updateLifecycle(vnode.state, vnode, hooks, recycling) + if (recycling) { + initComponent(vnode, hooks) + } else { + vnode.instance = Vnode.normalize(vnode.state.view(vnode)) + if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks) + updateLifecycle(vnode.state, vnode, hooks) + } if (vnode.instance != null) { if (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling) else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns) @@ -897,11 +907,10 @@ var coreRenderer = function($window) { if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode) if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode)) } - function updateLifecycle(source, vnode, hooks, recycling) { - if (recycling) initLifecycle(source, vnode, hooks) - else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) + function updateLifecycle(source, vnode, hooks) { + if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) } - function shouldUpdate(vnode, old) { + function shouldNotUpdate(vnode, old) { var forceVnodeUpdate, forceComponentUpdate 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) diff --git a/mithril.min.js b/mithril.min.js index 732ad9c3..ec786ea9 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,43 +1,43 @@ -new function(){function w(a,d,h,f,g,l){return{tag:a,key:d,attrs:h,children:f,text:g,dom:l,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function A(a){if(null==a||"string"!==typeof a&&"function"!==typeof a&&"function"!==typeof a.view)throw Error("The selector must be either a string or a component.");if("string"===typeof a&&void 0===H[a]){for(var d,h,f=[],g={};d=O.exec(a);){var l=d[1],k=d[2];""===l&&""!==k?h=k:"#"===l?g.id=k:"."===l?f.push(k):"["===d[3][0]&&((l=d[6])&&(l=l.replace(/\\(["'])/g, -"$1").replace(/\\\\/g,"\\")),"class"===d[4]?f.push(l):g[d[4]]=l||!0)}0c.indexOf("?")?"?":"&";c+=f+d}return c}function k(c){try{return""!==c?JSON.parse(c):null}catch(t){throw Error(c); -}}function q(c){return c.responseText}function p(c,a){if("function"===typeof c)if(Array.isArray(a))for(var d=0;dn.status||304===n.status||R.test(c.url))d(p(c.type, -b));else{var m=Error(n.responseText),a;for(a in b)m[a]=b[a];f(m)}}catch(C){f(C)}};h&&null!=c.data?n.send(c.data):n.send()});return!0===c.background?t:r(t)},jsonp:function(c,k){var q=h();c=f(c,k);var u=new d(function(d,f){var h=c.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+r++,k=a.document.createElement("script");a[h]=function(f){k.parentNode.removeChild(k);d(p(c.type,f));delete a[h]};k.onerror=function(){k.parentNode.removeChild(k);f(Error("JSONP request failed"));delete a[h]};null== -c.data&&(c.data={});c.url=g(c.url,c.data);c.data[c.callbackKey||"callback"]=h;k.src=l(c.url,c.data);a.document.documentElement.appendChild(k)});return!0===c.background?u:q(u)},setCompletionCallback:function(a){u=a}}}(window,v),N=function(a){function d(a,b,c,d,f,g,k){for(;c=m&&t>=e;){var y=b[m],z=c[e];if(y!==z||f)if(null==y)m++;else if(null==z)e++;else if(y.key===z.key){var C=null!=x&&m>=b.length-x.length||null==x&&f;m++;e++;l(a,y,z,g,q(b,m,B),C,n);f&&y.tag===z.tag&&p(a,k(y),B)}else if(y=b[r],y!==z||f)if(null==y)r--;else if(null==z)e++;else if(y.key===z.key)C=null!=x&&r>=b.length-x.length||null==x&&f, -l(a,y,z,g,q(b,r+1,B),C,n),(f||e=m&&t>=e;){y=b[r];z=c[t];if(y!==z||f)if(null==y)r--;else{if(null!=z)if(y.key===z.key)C=null!=x&&r>=b.length-x.length||null==x&&f,l(a,y,z,g,q(b,r+1,B),C,n),f&&y.tag===z.tag&&p(a,k(y),B),null!=y.dom&&(B=y.dom),r--;else{if(!E){E=b;var y=r,C={},v;for(v=0;va.indexOf("?")?"?":"&";a+=e+d}return a}function h(a){try{return""!==a?JSON.parse(a):null}catch(w){throw Error(a); +}}function r(a){return a.responseText}function n(a,b){if("function"===typeof a)if(Array.isArray(b))for(var d=0;dm.status||304===m.status||R.test(a.url))d(n(a.type, +f));else{var c=Error(m.responseText),q;for(q in f)c[q]=f[q];e(c)}}catch(p){e(p)}};l&&null!=a.data?m.send(a.data):m.send()});return!0===a.background?y:v(y)},jsonp:function(a,h){var v=l();a=e(a,h);var r=new d(function(d,e){var l=a.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,h=b.document.createElement("script");b[l]=function(e){h.parentNode.removeChild(h);d(n(a.type,e));delete b[l]};h.onerror=function(){h.parentNode.removeChild(h);e(Error("JSONP request failed"));delete b[l]};null== +a.data&&(a.data={});a.url=g(a.url,a.data);a.data[a.callbackKey||"callback"]=l;h.src=k(a.url,a.data);b.document.documentElement.appendChild(h)});return!0===a.background?r:v(r)},setCompletionCallback:function(b){v=b}}}(window,A),N=function(b){function d(f,c,q,b,a,d,e){for(;q=q&&y>=p;){var z=c[q],t=b[p];if(z!==t||e)if(null==z)q++;else if(null==t)p++;else if(z.key===t.key){var u=null!=D&&q>=c.length-D.length||null==D&&e;q++;p++;h(f,z,t,g,n(c,q,k),u,v);e&&z.tag===t.tag&&m(f,r(z),k)}else if(z=c[w],z!==t||e)if(null==z)w--;else if(null==t)p++;else if(z.key===t.key)u= +null!=D&&w>=c.length-D.length||null==D&&e,h(f,z,t,g,n(c,w+1,k),u,v),(e||p=q&&y>=p;){z=c[w];t=b[y];if(z!==t||e)if(null==z)w--;else{if(null!=t)if(z.key===t.key)u=null!=D&&w>=c.length-D.length||null==D&&e,h(f,z,t,g,n(c,w+1,k),u,v),e&&z.tag===t.tag&&m(f,r(z),k),null!=z.dom&&(k=z.dom),w--;else{if(!G){G=c;var z=w,u={},x;for(x=0;x Date: Sat, 25 Feb 2017 18:34:32 -0500 Subject: [PATCH 068/113] Add stream/scan, scanMerge types --- mithril.d.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/mithril.d.ts b/mithril.d.ts index 37179e16..8864d60a 100644 --- a/mithril.d.ts +++ b/mithril.d.ts @@ -238,6 +238,18 @@ declare namespace Mithril { /** A special value that can be returned to stream callbacks to halt execution of downstreams. */ HALT: any; } + + interface StreamScan { + /** Creates a new stream with the results of calling the function on every incoming stream with and accumulator and the incoming value. */ + (fn: (acc: U, value: T) => U, acc: U, stream: Stream): Stream; + } + + interface StreamScanMerge { + /** Takes an array of pairs of streams and scan functions and merges all those streams using the given functions into a single stream. */ + (pairs: [Stream, (acc: U, value: T) => U][], acc: U): Stream; + /** Takes an array of pairs of streams and scan functions and merges all those streams using the given functions into a single stream. */ + (pairs: [Stream, (acc: U, value: any) => U][], acc: U): Stream; + } } declare module 'mithril' { @@ -284,3 +296,13 @@ declare module 'mithril/stream' { const s: Mithril.StreamFactory; export = s; } + +declare module 'mithril/stream/scan' { + const s: Mithril.StreamScan; + export = s; +} + +declare module 'mithril/stream/scanMerge' { + const sm: Mithril.StreamScanMerge; + export = sm; +} From 57d3de1e5c378678c901639711a22d121e2ec766 Mon Sep 17 00:00:00 2001 From: Barney Carroll Date: Sun, 26 Feb 2017 17:29:43 +0000 Subject: [PATCH 069/113] Docs: component props aren't copied to instances --- docs/components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/components.md b/docs/components.md index db5c0b0f..c67ea89b 100644 --- a/docs/components.md +++ b/docs/components.md @@ -109,7 +109,7 @@ The state of a component can be accessed three ways: as a blueprint at initializ #### At initialization -Any property attached to the component object is copied for every instance of the component. This allows simple state initialization. +The component object is the prototype of each component instance, so any property defined on the component object will be accessible as a property of `vnode.state`. This allows simple state initialization. In the example below, `data` is a property of the `ComponentWithInitialState` component's state object. From b34c3eaf82e625a52f7303bba0f338ed080594e0 Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Thu, 2 Mar 2017 06:34:01 -0500 Subject: [PATCH 070/113] Optimize memory for selector cache --- render/hyperscript.js | 129 ++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 49 deletions(-) diff --git a/render/hyperscript.js b/render/hyperscript.js index 1630afce..24151431 100644 --- a/render/hyperscript.js +++ b/render/hyperscript.js @@ -4,66 +4,97 @@ var Vnode = require("../render/vnode") var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g var selectorCache = {} +var hasOwn = {}.hasOwnProperty + +function compileSelector(selector) { + var match, tag = "div", classes = [], attrs = {} + while (match = selectorParser.exec(selector)) { + var type = match[1], value = match[2] + if (type === "" && value !== "") tag = value + else if (type === "#") attrs.id = value + else if (type === ".") classes.push(value) + else if (match[3][0] === "[") { + var attrValue = match[6] + if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\") + if (match[4] === "class") classes.push(attrValue) + else attrs[match[4]] = attrValue || true + } + } + if (classes.length > 0) attrs.className = classes.join(" ") + return selectorCache[selector] = {tag: tag, attrs: attrs} +} + +function execSelector(state, attrs, children) { + var hasAttrs = false, childList, text + var className = attrs.className || attrs.class + + for (var key in state.attrs) { + if (hasOwn.call(state.attrs, key)) { + attrs[key] = state.attrs[key] + } + } + + if (className != null) { + if (attrs.class != null) { + attrs.class = undefined + attrs.className = className + } + + if (state.attrs.className != null) { + attrs.className = state.attrs.className + " " + className + } + } + + for (var key in attrs) { + if (hasOwn.call(attrs, key) && key !== "key") { + hasAttrs = true + break + } + } + + if (Array.isArray(children) && children.length === 1 && children[0] != null && children[0].tag === "#") { + text = children[0].children + } else { + childList = children + } + + return Vnode(state.tag, attrs.key, hasAttrs ? attrs : undefined, childList, text) +} + function hyperscript(selector) { + // Because sloppy mode sucks + var attrs = arguments[1], start = 2, children + if (selector == null || typeof selector !== "string" && typeof selector !== "function" && typeof selector.view !== "function") { throw Error("The selector must be either a string or a component."); } - if (typeof selector === "string" && selectorCache[selector] === undefined) { - var match, tag, classes = [], attributes = {} - while (match = selectorParser.exec(selector)) { - var type = match[1], value = match[2] - if (type === "" && value !== "") tag = value - else if (type === "#") attributes.id = value - else if (type === ".") classes.push(value) - else if (match[3][0] === "[") { - var attrValue = match[6] - if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\") - if (match[4] === "class") classes.push(attrValue) - else attributes[match[4]] = attrValue || true - } - } - if (classes.length > 0) attributes.className = classes.join(" ") - selectorCache[selector] = function(attrs, children) { - var hasAttrs = false, childList, text - var className = attrs.className || attrs.class - for (var key in attributes) attrs[key] = attributes[key] - if (className !== undefined) { - if (attrs.class !== undefined) { - attrs.class = undefined - attrs.className = className - } - if (attributes.className !== undefined) attrs.className = attributes.className + " " + className - } - for (var key in attrs) { - if (key !== "key") { - hasAttrs = true - break - } - } - if (Array.isArray(children) && children.length == 1 && children[0] != null && children[0].tag === "#") text = children[0].children - else childList = children + if (typeof selector === "string") { + var cached = selectorCache[selector] || compileSelector(selector) + } - return Vnode(tag || "div", attrs.key, hasAttrs ? attrs : undefined, childList, text, undefined) - } + if (!attrs) { + attrs = {} + } else if (typeof attrs !== "object" || attrs.tag != null || Array.isArray(attrs)) { + attrs = {} + start = 1 } - var attrs, children, childrenIndex - if (arguments[1] == null || typeof arguments[1] === "object" && arguments[1].tag === undefined && !Array.isArray(arguments[1])) { - attrs = arguments[1] - childrenIndex = 2 - } - else childrenIndex = 1 - if (arguments.length === childrenIndex + 1) { - children = Array.isArray(arguments[childrenIndex]) ? arguments[childrenIndex] : [arguments[childrenIndex]] - } - else { + + if (arguments.length === start + 1) { + children = arguments[start] + if (!Array.isArray(children)) children = [children] + } else { children = [] - for (var i = childrenIndex; i < arguments.length; i++) children.push(arguments[i]) + while (start < arguments.length) children.push(arguments[start++]) } - if (typeof selector === "string") return selectorCache[selector](attrs || {}, Vnode.normalizeChildren(children)) + var normalized = Vnode.normalizeChildren(children) - return Vnode(selector, attrs && attrs.key, attrs || {}, Vnode.normalizeChildren(children), undefined, undefined) + if (typeof selector === "string") { + return execSelector(cached, attrs, normalized) + } else { + return Vnode(selector, attrs.key, attrs, normalized) + } } module.exports = hyperscript From 38db32e8b7c4f1837d4f696620651fce93d76654 Mon Sep 17 00:00:00 2001 From: Gandalf-the-Bot Date: Thu, 2 Mar 2017 19:29:12 +0000 Subject: [PATCH 071/113] Bundled output for commit 9d6a5e51eb4cebb4de1a06a32f1ef3b52f265ebc [skip ci] --- mithril.js | 33 ++++++++++++------- mithril.min.js | 86 +++++++++++++++++++++++++------------------------- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/mithril.js b/mithril.js index 5d3c46e5..b7f11d41 100644 --- a/mithril.js +++ b/mithril.js @@ -368,8 +368,8 @@ var coreRenderer = function($window) { } function createNode(parent, vnode, hooks, ns, nextSibling) { var tag = vnode.tag - if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) if (typeof tag === "string") { + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) switch (tag) { case "#": return createText(parent, vnode, nextSibling) case "<": return createHTML(parent, vnode, nextSibling) @@ -442,7 +442,7 @@ var coreRenderer = function($window) { } return element } - function createComponent(parent, vnode, hooks, ns, nextSibling) { + function initComponent(vnode, hooks) { var sentinel if (typeof vnode.tag === "function") { vnode.state = null @@ -457,9 +457,13 @@ var coreRenderer = function($window) { if (sentinel.$$reentrantLock$$ != null) return $emptyFragment sentinel.$$reentrantLock$$ = true } + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) initLifecycle(vnode.state, vnode, hooks) vnode.instance = Vnode.normalize(vnode.state.view(vnode)) sentinel.$$reentrantLock$$ = null + } + function createComponent(parent, vnode, hooks, ns, nextSibling) { + initComponent(vnode, hooks) if (vnode.instance != null) { 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) @@ -570,11 +574,12 @@ var coreRenderer = function($window) { if (oldTag === tag) { vnode.state = old.state vnode.events = old.events - if (shouldUpdate(vnode, old)) return - if (vnode.attrs != null) { - updateLifecycle(vnode.attrs, vnode, hooks, recycling) - } + if (!recycling && shouldNotUpdate(vnode, old)) return if (typeof oldTag === "string") { + if (vnode.attrs != null) { + if (recycling) initLifecycle(vnode.attrs, vnode, hooks) + else updateLifecycle(vnode.attrs, vnode, hooks) + } switch (oldTag) { case "#": updateText(old, vnode); break case "<": updateHTML(parent, old, vnode, nextSibling); break @@ -644,8 +649,13 @@ var coreRenderer = function($window) { } } function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) { - vnode.instance = Vnode.normalize(vnode.state.view(vnode)) - updateLifecycle(vnode.state, vnode, hooks, recycling) + if (recycling) { + initComponent(vnode, hooks) + } else { + vnode.instance = Vnode.normalize(vnode.state.view(vnode)) + if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks) + updateLifecycle(vnode.state, vnode, hooks) + } if (vnode.instance != null) { if (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling) else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns) @@ -897,11 +907,10 @@ var coreRenderer = function($window) { if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode) if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode)) } - function updateLifecycle(source, vnode, hooks, recycling) { - if (recycling) initLifecycle(source, vnode, hooks) - else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) + function updateLifecycle(source, vnode, hooks) { + if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) } - function shouldUpdate(vnode, old) { + function shouldNotUpdate(vnode, old) { var forceVnodeUpdate, forceComponentUpdate 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) diff --git a/mithril.min.js b/mithril.min.js index 732ad9c3..ec786ea9 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -1,43 +1,43 @@ -new function(){function w(a,d,h,f,g,l){return{tag:a,key:d,attrs:h,children:f,text:g,dom:l,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function A(a){if(null==a||"string"!==typeof a&&"function"!==typeof a&&"function"!==typeof a.view)throw Error("The selector must be either a string or a component.");if("string"===typeof a&&void 0===H[a]){for(var d,h,f=[],g={};d=O.exec(a);){var l=d[1],k=d[2];""===l&&""!==k?h=k:"#"===l?g.id=k:"."===l?f.push(k):"["===d[3][0]&&((l=d[6])&&(l=l.replace(/\\(["'])/g, -"$1").replace(/\\\\/g,"\\")),"class"===d[4]?f.push(l):g[d[4]]=l||!0)}0c.indexOf("?")?"?":"&";c+=f+d}return c}function k(c){try{return""!==c?JSON.parse(c):null}catch(t){throw Error(c); -}}function q(c){return c.responseText}function p(c,a){if("function"===typeof c)if(Array.isArray(a))for(var d=0;dn.status||304===n.status||R.test(c.url))d(p(c.type, -b));else{var m=Error(n.responseText),a;for(a in b)m[a]=b[a];f(m)}}catch(C){f(C)}};h&&null!=c.data?n.send(c.data):n.send()});return!0===c.background?t:r(t)},jsonp:function(c,k){var q=h();c=f(c,k);var u=new d(function(d,f){var h=c.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+r++,k=a.document.createElement("script");a[h]=function(f){k.parentNode.removeChild(k);d(p(c.type,f));delete a[h]};k.onerror=function(){k.parentNode.removeChild(k);f(Error("JSONP request failed"));delete a[h]};null== -c.data&&(c.data={});c.url=g(c.url,c.data);c.data[c.callbackKey||"callback"]=h;k.src=l(c.url,c.data);a.document.documentElement.appendChild(k)});return!0===c.background?u:q(u)},setCompletionCallback:function(a){u=a}}}(window,v),N=function(a){function d(a,b,c,d,f,g,k){for(;c=m&&t>=e;){var y=b[m],z=c[e];if(y!==z||f)if(null==y)m++;else if(null==z)e++;else if(y.key===z.key){var C=null!=x&&m>=b.length-x.length||null==x&&f;m++;e++;l(a,y,z,g,q(b,m,B),C,n);f&&y.tag===z.tag&&p(a,k(y),B)}else if(y=b[r],y!==z||f)if(null==y)r--;else if(null==z)e++;else if(y.key===z.key)C=null!=x&&r>=b.length-x.length||null==x&&f, -l(a,y,z,g,q(b,r+1,B),C,n),(f||e=m&&t>=e;){y=b[r];z=c[t];if(y!==z||f)if(null==y)r--;else{if(null!=z)if(y.key===z.key)C=null!=x&&r>=b.length-x.length||null==x&&f,l(a,y,z,g,q(b,r+1,B),C,n),f&&y.tag===z.tag&&p(a,k(y),B),null!=y.dom&&(B=y.dom),r--;else{if(!E){E=b;var y=r,C={},v;for(v=0;va.indexOf("?")?"?":"&";a+=e+d}return a}function h(a){try{return""!==a?JSON.parse(a):null}catch(w){throw Error(a); +}}function r(a){return a.responseText}function n(a,b){if("function"===typeof a)if(Array.isArray(b))for(var d=0;dm.status||304===m.status||R.test(a.url))d(n(a.type, +f));else{var c=Error(m.responseText),q;for(q in f)c[q]=f[q];e(c)}}catch(p){e(p)}};l&&null!=a.data?m.send(a.data):m.send()});return!0===a.background?y:v(y)},jsonp:function(a,h){var v=l();a=e(a,h);var r=new d(function(d,e){var l=a.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,h=b.document.createElement("script");b[l]=function(e){h.parentNode.removeChild(h);d(n(a.type,e));delete b[l]};h.onerror=function(){h.parentNode.removeChild(h);e(Error("JSONP request failed"));delete b[l]};null== +a.data&&(a.data={});a.url=g(a.url,a.data);a.data[a.callbackKey||"callback"]=l;h.src=k(a.url,a.data);b.document.documentElement.appendChild(h)});return!0===a.background?r:v(r)},setCompletionCallback:function(b){v=b}}}(window,A),N=function(b){function d(f,c,q,b,a,d,e){for(;q=q&&y>=p;){var z=c[q],t=b[p];if(z!==t||e)if(null==z)q++;else if(null==t)p++;else if(z.key===t.key){var u=null!=D&&q>=c.length-D.length||null==D&&e;q++;p++;h(f,z,t,g,n(c,q,k),u,v);e&&z.tag===t.tag&&m(f,r(z),k)}else if(z=c[w],z!==t||e)if(null==z)w--;else if(null==t)p++;else if(z.key===t.key)u= +null!=D&&w>=c.length-D.length||null==D&&e,h(f,z,t,g,n(c,w+1,k),u,v),(e||p=q&&y>=p;){z=c[w];t=b[y];if(z!==t||e)if(null==z)w--;else{if(null!=t)if(z.key===t.key)u=null!=D&&w>=c.length-D.length||null==D&&e,h(f,z,t,g,n(c,w+1,k),u,v),e&&z.tag===t.tag&&m(f,r(z),k),null!=z.dom&&(k=z.dom),w--;else{if(!G){G=c;var z=w,u={},x;for(x=0;x Date: Thu, 2 Mar 2017 07:26:52 -0500 Subject: [PATCH 072/113] "use strict" and other linty fixes --- .eslintignore | 15 +- .eslintrc.js | 6 +- .gitignore | 1 + api/tests/test-mount.js | 18 +- api/tests/test-router.js | 66 ++--- browser.js | 2 + bundler/bin/bundle | 1 + bundler/tests/test-bundler.js | 340 +++++++++++++------------ docs/generate.js | 20 +- docs/lint.js | 11 +- hyperscript.js | 2 + mount.js | 4 +- ospec/bin/ospec | 5 +- ospec/ospec.js | 5 +- ospec/tests/test-ospec.js | 8 +- promise/tests/test-promise.js | 32 +-- redraw.js | 4 +- render.js | 4 +- render/tests/test-attributes.js | 32 +-- render/tests/test-component.js | 50 ++-- render/tests/test-createElement.js | 1 + render/tests/test-hyperscript.js | 2 + render/tests/test-onbeforeremove.js | 5 +- render/tests/test-onbeforeupdate.js | 14 +- render/tests/test-oncreate.js | 1 - render/tests/test-oninit.js | 1 - render/tests/test-onupdate.js | 1 - render/tests/test-render.js | 7 +- render/tests/test-updateElement.js | 8 +- render/tests/test-updateNodes.js | 19 +- render/vnode.js | 2 + request.js | 2 + request/tests/test-jsonp.js | 7 +- request/tests/test-request.js | 63 +++-- route.js | 4 +- router/tests/test-getPath.js | 2 +- stream.js | 4 +- stream/tests/test-scan.js | 3 +- stream/tests/test-stream.js | 23 +- test-utils/components.js | 14 +- test-utils/domMock.js | 26 +- test-utils/parseURL.js | 2 +- test-utils/pushStateMock.js | 4 +- test-utils/tests/test-browserMock.js | 4 +- test-utils/tests/test-components.js | 8 +- test-utils/tests/test-domMock.js | 31 ++- test-utils/tests/test-pushStateMock.js | 32 +-- test-utils/tests/test-xhrMock.js | 12 +- test-utils/xhrMock.js | 9 +- tests/test-api.js | 4 +- 50 files changed, 463 insertions(+), 478 deletions(-) diff --git a/.eslintignore b/.eslintignore index 1b9fe34c..08396583 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,11 +1,6 @@ -coverage .vscode -examples -docs -node_modules -tests -test-utils -ospec -mithril.js -mithril.min.js -archive +/node_modules +/examples +/docs/lib +/mithril.js +/mithril.min.js diff --git a/.eslintrc.js b/.eslintrc.js index 25278201..35fbbef6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -188,7 +188,7 @@ module.exports = { "quotes": [ "error", "double", - "avoid-escape" + {"avoidEscape": true} ], "radix": [ "error", @@ -209,7 +209,7 @@ module.exports = { "space-infix-ops": "off", "space-unary-ops": "error", "spaced-comment": "off", - "strict": "off", + "strict": ["error", "global"], "template-curly-spacing": "error", "valid-jsdoc": "off", "vars-on-top": "off", @@ -218,4 +218,4 @@ module.exports = { "yield-star-spacing": "error", "yoda": "off" } -}; \ No newline at end of file +}; diff --git a/.gitignore b/.gitignore index 8af32a76..285af458 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ jsconfig.json npm-debug.log .vscode .DS_Store +.eslintcache diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js index 4ba466c7..db2bee04 100644 --- a/api/tests/test-mount.js +++ b/api/tests/test-mount.js @@ -71,8 +71,8 @@ o.spec("mount", function() { o("redraws on events", function(done) { var onupdate = o.spy() - var oninit = o.spy() - var onclick = o.spy() + var oninit = o.spy() + var onclick = o.spy() var e = $window.document.createEvent("MouseEvents") e.initEvent("click", true, true) @@ -107,13 +107,13 @@ o.spec("mount", function() { o("redraws several mount points on events", function(done, timeout) { timeout(60) - + var onupdate0 = o.spy() - var oninit0 = o.spy() - var onclick0 = o.spy() + var oninit0 = o.spy() + var onclick0 = o.spy() var onupdate1 = o.spy() - var oninit1 = o.spy() - var onclick1 = o.spy() + var oninit1 = o.spy() + var onclick1 = o.spy() var e = $window.document.createEvent("MouseEvents") @@ -228,7 +228,7 @@ o.spec("mount", function() { done() }, FRAME_BUDGET) }) - + o("throttles", function(done, timeout) { timeout(200) @@ -252,4 +252,4 @@ o.spec("mount", function() { }) }) }) -}) \ No newline at end of file +}) diff --git a/api/tests/test-router.js b/api/tests/test-router.js index f43605e1..3fcfbae7 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -6,7 +6,6 @@ var browserMock = require("../../test-utils/browserMock") var m = require("../../render/hyperscript") var callAsync = require("../../test-utils/callAsync") -var coreRenderer = require("../../render/render") var apiRedraw = require("../../api/redraw") var apiRouter = require("../../api/router") var Promise = require("../../promise/promise") @@ -31,7 +30,7 @@ o.spec("route", function() { o("throws on invalid `root` DOM node", function() { var threw = false try { - route(null, '/', {'/':{view: function() {}}}) + route(null, "/", {"/":{view: function() {}}}) } catch (e) { threw = true } @@ -141,7 +140,7 @@ o.spec("route", function() { done() } }) - + o("redraws when render function is executed", function() { var onupdate = o.spy() var oninit = o.spy() @@ -206,7 +205,6 @@ o.spec("route", function() { o("event handlers can skip redraw", function(done) { var onupdate = o.spy() var oninit = o.spy() - var onclick = o.spy() var e = $window.document.createEvent("MouseEvents") e.initEvent("click", true, true) @@ -354,11 +352,6 @@ o.spec("route", function() { o("accepts RouteResolver with onmatch that returns Promise", function(done) { var matchCount = 0 var renderCount = 0 - var Component = { - view: function() { - return m("span") - } - } var resolver = { onmatch: function(args, requestedPath) { @@ -395,11 +388,6 @@ o.spec("route", function() { o("accepts RouteResolver with onmatch that returns Promise", function(done) { var matchCount = 0 var renderCount = 0 - var Component = { - view: function() { - return m("span") - } - } var resolver = { onmatch: function(args, requestedPath) { @@ -437,14 +425,9 @@ o.spec("route", function() { var matchCount = 0 var renderCount = 0 var spy = o.spy() - var Component = { - view: function() { - return m("span") - } - } var resolver = { - onmatch: function(args, requestedPath) { + onmatch: function() { matchCount++ return Promise.reject(new Error("error")) }, @@ -499,7 +482,7 @@ o.spec("route", function() { }) }) - o("changing `vnode.key` in `render` resets the component", function(done, timeout){ + o("changing `vnode.key` in `render` resets the component", function(done){ var oninit = o.spy() var Component = { oninit: oninit, @@ -548,22 +531,15 @@ o.spec("route", function() { }) o("RouteResolver `render` does not have component semantics", function(done) { - var renderCount = 0 - var A = { - view: function() { - return m("div") - } - } - $window.location.href = prefix + "/a" route(root, "/a", { "/a" : { - render: function(vnode) { + render: function() { return m("div") }, }, "/b" : { - render: function(vnode) { + render: function() { return m("div") }, }, @@ -632,7 +608,7 @@ o.spec("route", function() { onmatch: function() { matchCount++ }, - render: function(vnode) { + render: function() { renderCount++ return {tag: Component} }, @@ -726,7 +702,7 @@ o.spec("route", function() { render: render }, "/b" : { - render: function(vnode){ + render: function(){ redirected = true } } @@ -838,7 +814,7 @@ o.spec("route", function() { }) callAsync(function() { - route.set('/b') + route.set("/b") callAsync(function() { callAsync(function() { callAsync(function() { @@ -865,7 +841,7 @@ o.spec("route", function() { render: render }, "/b" : { - onmatch: function(vnode){ + onmatch: function(){ redirected = true return {view: function() {}} } @@ -895,7 +871,7 @@ o.spec("route", function() { render: render }, "/b" : { - render: function(vnode){ + render: function(){ redirected = true } } @@ -924,7 +900,7 @@ o.spec("route", function() { render: render }, "/b" : { - view: function(vnode){ + view: function(){ redirected = true } } @@ -1032,7 +1008,7 @@ o.spec("route", function() { var render = o.spy(function() {return m("div")}) $window.location.href = prefix + "/" - route(root, '/', { + route(root, "/", { "/": { onmatch: onmatch, render: render @@ -1081,23 +1057,23 @@ o.spec("route", function() { o("routing with RouteResolver works more than once", function(done) { $window.location.href = prefix + "/a" - route(root, '/a', { - '/a': { + route(root, "/a", { + "/a": { render: function() { return m("a", "a") } }, - '/b': { + "/b": { render: function() { return m("b", "b") } } }) - route.set('/b') + route.set("/b") callAsync(function() { - route.set('/a') + route.set("/a") callAsync(function() { o(root.firstChild.nodeName).equals("A") @@ -1122,7 +1098,7 @@ o.spec("route", function() { }) }) }, - render: function(vnode) { + render: function() { rendered = true resolved = "a" } @@ -1180,7 +1156,7 @@ o.spec("route", function() { route.set("/b") }) }, - render: function(vnode) { + render: function() { rendered = true resolved = "a" } @@ -1210,7 +1186,7 @@ o.spec("route", function() { var i = 0 $window.location.href = prefix + "/" route(root, "/", { - "/": {view: function(v) {i++}} + "/": {view: function() {i++}} }) var before = i diff --git a/browser.js b/browser.js index fed323f1..0debb4ea 100644 --- a/browser.js +++ b/browser.js @@ -1,3 +1,5 @@ +"use strict" + var m = require("./index") if (typeof module !== "undefined") module["exports"] = m else window.m = m diff --git a/bundler/bin/bundle b/bundler/bin/bundle index e0c4279a..81c65c90 100644 --- a/bundler/bin/bundle +++ b/bundler/bin/bundle @@ -1,3 +1,4 @@ #!/usr/bin/env node +"use strict" require("../cli") diff --git a/bundler/tests/test-bundler.js b/bundler/tests/test-bundler.js index 3055bf7c..40175e19 100644 --- a/bundler/tests/test-bundler.js +++ b/bundler/tests/test-bundler.js @@ -1,3 +1,5 @@ +"use strict" + var o = require("../../ospec/ospec") var bundle = require("../bundle") @@ -5,10 +7,10 @@ var fs = require("fs") var ns = "bundler/tests/" function read(filepath) { - try {return fs.readFileSync(ns + filepath, "utf8")} catch (e) {} + try {return fs.readFileSync(ns + filepath, "utf8")} catch (e) {/* ignore */} } function write(filepath, data) { - try {var exists = fs.statSync(ns + filepath).isFile()} catch (e) {} + try {var exists = fs.statSync(ns + filepath).isFile()} catch (e) {/* ignore */} if (exists) throw new Error("Don't call `write('" + filepath + "')`. Cannot overwrite file") fs.writeFileSync(ns + filepath, data, "utf8") } @@ -18,255 +20,255 @@ function remove(filepath) { o.spec("bundler", function() { o("relative imports works", function() { - write("a.js", `var b = require("./b")`) - write("b.js", `module.exports = 1`) + write("a.js", 'var b = require("./b")') + write("b.js", "module.exports = 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b = 1\n}`) - + + o(read("out.js")).equals("new function() {\nvar b = 1\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("relative imports works with semicolons", function() { - write("a.js", `var b = require("./b");`) - write("b.js", `module.exports = 1;`) + write("a.js", 'var b = require("./b");') + write("b.js", "module.exports = 1;") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b = 1;\n}`) - + + o(read("out.js")).equals("new function() {\nvar b = 1;\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("relative imports works with let", function() { - write("a.js", `let b = require("./b")`) - write("b.js", `module.exports = 1`) + write("a.js", 'let b = require("./b")') + write("b.js", "module.exports = 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nlet b = 1\n}`) - + + o(read("out.js")).equals("new function() {\nlet b = 1\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("relative imports works with const", function() { write("a.js", 'const b = require("./b")') - write("b.js", `module.exports = 1`) + write("b.js", "module.exports = 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nconst b = 1\n}`) - + + o(read("out.js")).equals("new function() {\nconst b = 1\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("relative imports works with assignment", function() { - write("a.js", `var a = {}\na.b = require("./b")`) - write("b.js", `module.exports = 1`) + write("a.js", 'var a = {}\na.b = require("./b")') + write("b.js", "module.exports = 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar a = {}\na.b = 1\n}`) - + + o(read("out.js")).equals("new function() {\nvar a = {}\na.b = 1\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("relative imports works with reassignment", function() { - write("a.js", `var b = {}\nb = require("./b")`) - write("b.js", `module.exports = 1`) + write("a.js", 'var b = {}\nb = require("./b")') + write("b.js", "module.exports = 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b = {}\nb = 1\n}`) - + + o(read("out.js")).equals("new function() {\nvar b = {}\nb = 1\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("relative imports removes extra use strict", function() { - write("a.js", `"use strict"\nvar b = require("./b")`) - write("b.js", `"use strict"\nmodule.exports = 1`) + write("a.js", '"use strict"\nvar b = require("./b")') + write("b.js", '"use strict"\nmodule.exports = 1') bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\n"use strict"\nvar b = 1\n}`) - + + o(read("out.js")).equals('new function() {\n"use strict"\nvar b = 1\n}') + remove("a.js") remove("b.js") remove("out.js") }) o("relative imports removes extra use strict using single quotes", function() { - write("a.js", `'use strict'\nvar b = require("./b")`) - write("b.js", `'use strict'\nmodule.exports = 1`) + write("a.js", "'use strict'\nvar b = require(\"./b\")") + write("b.js", "'use strict'\nmodule.exports = 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\n'use strict'\nvar b = 1\n}`) - + + o(read("out.js")).equals("new function() {\n'use strict'\nvar b = 1\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("relative imports removes extra use strict using mixed quotes", function() { - write("a.js", `"use strict"\nvar b = require("./b")`) - write("b.js", `'use strict'\nmodule.exports = 1`) + write("a.js", '"use strict"\nvar b = require("./b")') + write("b.js", "'use strict'\nmodule.exports = 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\n"use strict"\nvar b = 1\n}`) - + + o(read("out.js")).equals('new function() {\n"use strict"\nvar b = 1\n}') + remove("a.js") remove("b.js") remove("out.js") }) o("works w/ window", function() { - write("a.js", `window.a = 1\nvar b = require("./b")`) - write("b.js", `module.exports = function() {return a}`) + write("a.js", 'window.a = 1\nvar b = require("./b")') + write("b.js", "module.exports = function() {return a}") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nwindow.a = 1\nvar b = function() {return a}\n}`) - + + o(read("out.js")).equals("new function() {\nwindow.a = 1\nvar b = function() {return a}\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("works without assignment", function() { - write("a.js", `require("./b")`) - write("b.js", `1 + 1`) + write("a.js", 'require("./b")') + write("b.js", "1 + 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\n1 + 1\n}`) - + + o(read("out.js")).equals("new function() {\n1 + 1\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("works if used fluently", function() { - write("a.js", `var b = require("./b").toString()`) - write("b.js", `module.exports = []`) + write("a.js", 'var b = require("./b").toString()') + write("b.js", "module.exports = []") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar _0 = []\nvar b = _0.toString()\n}`) - + + o(read("out.js")).equals("new function() {\nvar _0 = []\nvar b = _0.toString()\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("works if used fluently w/ multiline", function() { - write("a.js", `var b = require("./b")\n\t.toString()`) - write("b.js", `module.exports = []`) + write("a.js", 'var b = require("./b")\n\t.toString()') + write("b.js", "module.exports = []") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar _0 = []\nvar b = _0\n\t.toString()\n}`) - + + o(read("out.js")).equals("new function() {\nvar _0 = []\nvar b = _0\n\t.toString()\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("works if used w/ curry", function() { - write("a.js", `var b = require("./b")()`) - write("b.js", `module.exports = function() {}`) + write("a.js", 'var b = require("./b")()') + write("b.js", "module.exports = function() {}") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar _0 = function() {}\nvar b = _0()\n}`) - + + o(read("out.js")).equals("new function() {\nvar _0 = function() {}\nvar b = _0()\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("works if used w/ curry w/ multiline", function() { - write("a.js", `var b = require("./b")\n()`) - write("b.js", `module.exports = function() {}`) + write("a.js", 'var b = require("./b")\n()') + write("b.js", "module.exports = function() {}") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar _0 = function() {}\nvar b = _0\n()\n}`) - + + o(read("out.js")).equals("new function() {\nvar _0 = function() {}\nvar b = _0\n()\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("works if used fluently in one place and not in another", function() { - write("a.js", `var b = require("./b").toString()\nvar c = require("./c")`) - write("b.js", `module.exports = []`) - write("c.js", `var b = require("./b")\nmodule.exports = function() {return b}`) + write("a.js", 'var b = require("./b").toString()\nvar c = require("./c")') + write("b.js", "module.exports = []") + write("c.js", 'var b = require("./b")\nmodule.exports = function() {return b}') bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar _0 = []\nvar b = _0.toString()\nvar b0 = _0\nvar c = function() {return b0}\n}`) - + + o(read("out.js")).equals("new function() {\nvar _0 = []\nvar b = _0.toString()\nvar b0 = _0\nvar c = function() {return b0}\n}") + remove("a.js") remove("b.js") remove("c.js") remove("out.js") }) o("works if used in sequence", function() { - write("a.js", `var b = require("./b"), c = require("./c")`) - write("b.js", `module.exports = 1`) - write("c.js", `var x\nmodule.exports = 2`) + write("a.js", 'var b = require("./b"), c = require("./c")') + write("b.js", "module.exports = 1") + write("c.js", "var x\nmodule.exports = 2") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b = 1\nvar x\nvar c = 2\n}`) - + + o(read("out.js")).equals("new function() {\nvar b = 1\nvar x\nvar c = 2\n}") + remove("a.js") remove("b.js") remove("c.js") remove("out.js") }) o("works if assigned to property", function() { - write("a.js", `var x = {}\nx.b = require("./b")\nx.c = require("./c")`) - write("b.js", `var bb = 1\nmodule.exports = bb`) - write("c.js", `var cc = 2\nmodule.exports = cc`) + write("a.js", 'var x = {}\nx.b = require("./b")\nx.c = require("./c")') + write("b.js", "var bb = 1\nmodule.exports = bb") + write("c.js", "var cc = 2\nmodule.exports = cc") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar x = {}\nvar bb = 1\nx.b = bb\nvar cc = 2\nx.c = cc\n}`) - + + o(read("out.js")).equals("new function() {\nvar x = {}\nvar bb = 1\nx.b = bb\nvar cc = 2\nx.c = cc\n}") + remove("a.js") remove("b.js") remove("c.js") remove("out.js") }) o("works if assigned to property using bracket notation", function() { - write("a.js", `var x = {}\nx["b"] = require("./b")\nx["c"] = require("./c")`) - write("b.js", `var bb = 1\nmodule.exports = bb`) - write("c.js", `var cc = 2\nmodule.exports = cc`) + write("a.js", 'var x = {}\nx["b"] = require("./b")\nx["c"] = require("./c")') + write("b.js", "var bb = 1\nmodule.exports = bb") + write("c.js", "var cc = 2\nmodule.exports = cc") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar x = {}\nvar bb = 1\nx["b"] = bb\nvar cc = 2\nx["c"] = cc\n}`) - + + o(read("out.js")).equals('new function() {\nvar x = {}\nvar bb = 1\nx["b"] = bb\nvar cc = 2\nx["c"] = cc\n}') + remove("a.js") remove("b.js") remove("c.js") remove("out.js") }) o("works if collision", function() { - write("a.js", `var b = require("./b")`) - write("b.js", `var b = 1\nmodule.exports = 2`) + write("a.js", 'var b = require("./b")') + write("b.js", "var b = 1\nmodule.exports = 2") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b0 = 1\nvar b = 2\n}`) - + + o(read("out.js")).equals("new function() {\nvar b0 = 1\nvar b = 2\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("works if multiple aliases", function() { - write("a.js", `var b = require("./b")\n`) - write("b.js", `var b = require("./c")\nb.x = 1\nmodule.exports = b`) - write("c.js", `var b = {}\nmodule.exports = b`) + write("a.js", 'var b = require("./b")\n') + write("b.js", 'var b = require("./c")\nb.x = 1\nmodule.exports = b') + write("c.js", "var b = {}\nmodule.exports = b") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b = {}\nb.x = 1\n}`) - + + o(read("out.js")).equals("new function() {\nvar b = {}\nb.x = 1\n}") + remove("a.js") remove("b.js") remove("c.js") remove("out.js") }) o("works if multiple collision", function() { - write("a.js", `var b = require("./b")\nvar c = require("./c")\nvar d = require("./d")`) - write("b.js", `var a = 1\nmodule.exports = a`) - write("c.js", `var a = 2\nmodule.exports = a`) - write("d.js", `var a = 3\nmodule.exports = a`) + write("a.js", 'var b = require("./b")\nvar c = require("./c")\nvar d = require("./d")') + write("b.js", "var a = 1\nmodule.exports = a") + write("c.js", "var a = 2\nmodule.exports = a") + write("d.js", "var a = 3\nmodule.exports = a") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar a = 1\nvar b = a\nvar a0 = 2\nvar c = a0\nvar a1 = 3\nvar d = a1\n}`) - + + o(read("out.js")).equals("new function() {\nvar a = 1\nvar b = a\nvar a0 = 2\nvar c = a0\nvar a1 = 3\nvar d = a1\n}") + remove("a.js") remove("b.js") remove("c.js") @@ -274,38 +276,38 @@ o.spec("bundler", function() { remove("out.js") }) o("works if included multiple times", function() { - write("a.js", `module.exports = 123`) - write("b.js", `var a = require("./a").toString()\nmodule.exports = a`) - write("c.js", `var a = require("./a").toString()\nvar b = require("./b")`) + write("a.js", "module.exports = 123") + write("b.js", 'var a = require("./a").toString()\nmodule.exports = a') + write("c.js", 'var a = require("./a").toString()\nvar b = require("./b")') bundle(ns + "c.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar _0 = 123\nvar a = _0.toString()\nvar a0 = _0.toString()\nvar b = a0\n}`) - + + o(read("out.js")).equals("new function() {\nvar _0 = 123\nvar a = _0.toString()\nvar a0 = _0.toString()\nvar b = a0\n}") + remove("a.js") remove("b.js") remove("c.js") }) o("works if included multiple times reverse", function() { - write("a.js", `module.exports = 123`) - write("b.js", `var a = require("./a").toString()\nmodule.exports = a`) - write("c.js", `var b = require("./b")\nvar a = require("./a").toString()`) + write("a.js", "module.exports = 123") + write("b.js", 'var a = require("./a").toString()\nmodule.exports = a') + write("c.js", 'var b = require("./b")\nvar a = require("./a").toString()') bundle(ns + "c.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar _0 = 123\nvar a0 = _0.toString()\nvar b = a0\nvar a = _0.toString()\n}`) - + + o(read("out.js")).equals("new function() {\nvar _0 = 123\nvar a0 = _0.toString()\nvar b = a0\nvar a = _0.toString()\n}") + remove("a.js") remove("b.js") remove("c.js") }) o("reuses binding if possible", function() { - write("a.js", `var b = require("./b")\nvar c = require("./c")`) - write("b.js", `var d = require("./d")\nmodule.exports = function() {return d + 1}`) - write("c.js", `var d = require("./d")\nmodule.exports = function() {return d + 2}`) - write("d.js", `module.exports = 1`) + write("a.js", 'var b = require("./b")\nvar c = require("./c")') + write("b.js", 'var d = require("./d")\nmodule.exports = function() {return d + 1}') + write("c.js", 'var d = require("./d")\nmodule.exports = function() {return d + 2}') + write("d.js", "module.exports = 1") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar d = 1\nvar b = function() {return d + 1}\nvar c = function() {return d + 2}\n}`) - + + o(read("out.js")).equals("new function() {\nvar d = 1\nvar b = function() {return d + 1}\nvar c = function() {return d + 2}\n}") + remove("a.js") remove("b.js") remove("c.js") @@ -313,71 +315,71 @@ o.spec("bundler", function() { remove("out.js") }) o("disambiguates conflicts if imported collides with itself", function() { - write("a.js", `var b = require("./b")`) - write("b.js", `var b = 1\nmodule.exports = function() {return b}`) + write("a.js", 'var b = require("./b")') + write("b.js", "var b = 1\nmodule.exports = function() {return b}") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b0 = 1\nvar b = function() {return b0}\n}`) - + + o(read("out.js")).equals("new function() {\nvar b0 = 1\nvar b = function() {return b0}\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("disambiguates conflicts if imported collides with something else", function() { - write("a.js", `var a = 1\nvar b = require("./b")`) - write("b.js", `var a = 2\nmodule.exports = function() {return a}`) + write("a.js", 'var a = 1\nvar b = require("./b")') + write("b.js", "var a = 2\nmodule.exports = function() {return a}") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar a = 1\nvar a0 = 2\nvar b = function() {return a0}\n}`) - + + o(read("out.js")).equals("new function() {\nvar a = 1\nvar a0 = 2\nvar b = function() {return a0}\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("disambiguates conflicts if imported collides with function declaration", function() { - write("a.js", `function a() {}\nvar b = require("./b")`) - write("b.js", `var a = 2\nmodule.exports = function() {return a}`) + write("a.js", 'function a() {}\nvar b = require("./b")') + write("b.js", "var a = 2\nmodule.exports = function() {return a}") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nfunction a() {}\nvar a0 = 2\nvar b = function() {return a0}\n}`) - + + o(read("out.js")).equals("new function() {\nfunction a() {}\nvar a0 = 2\nvar b = function() {return a0}\n}") + remove("a.js") remove("b.js") remove("out.js") }) o("disambiguates conflicts if imported collides with another module's private", function() { - write("a.js", `var b = require("./b")\nvar c = require("./c")`) - write("b.js", `var a = 1\nmodule.exports = function() {return a}`) - write("c.js", `var a = 2\nmodule.exports = function() {return a}`) + write("a.js", 'var b = require("./b")\nvar c = require("./c")') + write("b.js", "var a = 1\nmodule.exports = function() {return a}") + write("c.js", "var a = 2\nmodule.exports = function() {return a}") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar a = 1\nvar b = function() {return a}\nvar a0 = 2\nvar c = function() {return a0}\n}`) - + + o(read("out.js")).equals("new function() {\nvar a = 1\nvar b = function() {return a}\nvar a0 = 2\nvar c = function() {return a0}\n}") + remove("a.js") remove("b.js") remove("c.js") remove("out.js") }) o("does not mess up strings", function() { - write("a.js", `var b = require("./b")`) - write("b.js", `var b = "b b b \\\" b"\nmodule.exports = function() {return b}`) + write("a.js", 'var b = require("./b")') + write("b.js", 'var b = "b b b \\" b"\nmodule.exports = function() {return b}') bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b0 = "b b b \\\" b"\nvar b = function() {return b0}\n}`) - + + o(read("out.js")).equals('new function() {\nvar b0 = "b b b \\" b"\nvar b = function() {return b0}\n}') + remove("a.js") remove("b.js") remove("out.js") }) o("does not mess up properties", function() { - write("a.js", `var b = require("./b")`) - write("b.js", `var b = {b: 1}\nmodule.exports = function() {return b.b}`) + write("a.js", 'var b = require("./b")') + write("b.js", "var b = {b: 1}\nmodule.exports = function() {return b.b}") bundle(ns + "a.js", ns + "out.js") - - o(read("out.js")).equals(`new function() {\nvar b0 = {b: 1}\nvar b = function() {return b0.b}\n}`) - + + o(read("out.js")).equals("new function() {\nvar b0 = {b: 1}\nvar b = function() {return b0.b}\n}") + remove("a.js") remove("b.js") remove("out.js") }) -}) \ No newline at end of file +}) diff --git a/docs/generate.js b/docs/generate.js index bcf5465e..789563c8 100644 --- a/docs/generate.js +++ b/docs/generate.js @@ -1,15 +1,17 @@ +"use strict" + var fs = require("fs") var path = require("path") var marked = require("marked") var layout = fs.readFileSync("./docs/layout.html", "utf-8") var version = JSON.parse(fs.readFileSync("./package.json", "utf-8")).version -try {fs.mkdirSync("../mithril")} catch (e) {} -try {fs.mkdirSync("../mithril/archive")} catch (e) {} -try {fs.mkdirSync("../mithril/archive/v" + version)} catch (e) {} -try {fs.mkdirSync("../mithril/archive/v" + version + "/lib")} catch (e) {} -try {fs.mkdirSync("../mithril/archive/v" + version + "/lib/prism")} catch (e) {} -try {fs.mkdirSync("../mithril/lib")} catch (e) {} -try {fs.mkdirSync("../mithril/lib/prism")} catch (e) {} +try {fs.mkdirSync("../mithril")} catch (e) {/* ignore */} +try {fs.mkdirSync("../mithril/archive")} catch (e) {/* ignore */} +try {fs.mkdirSync("../mithril/archive/v" + version)} catch (e) {/* ignore */} +try {fs.mkdirSync("../mithril/archive/v" + version + "/lib")} catch (e) {/* ignore */} +try {fs.mkdirSync("../mithril/archive/v" + version + "/lib/prism")} catch (e) {/* ignore */} +try {fs.mkdirSync("../mithril/lib")} catch (e) {/* ignore */} +try {fs.mkdirSync("../mithril/lib/prism")} catch (e) {/* ignore */} var guides = fs.readFileSync("docs/guides.md", "utf-8") var methods = fs.readFileSync("docs/methods.md", "utf-8") @@ -33,7 +35,7 @@ function generate(pathname) { .replace(/`((?:\S| -> |, )+)(\|)(\S+)`/gim, function(match, a, b, c) { // fix pipes in code tags return "" + (a + b + c).replace(/\|/g, "|") + "" }) - .replace(/(^# .+?(?:\r?\n){2,}?)(?:(-(?:.|\r|\n)+?)((?:\r?\n){2,})|)/m, function(match, title, nav, space) { // inject menu + .replace(/(^# .+?(?:\r?\n){2,}?)(?:(-(?:.|\r|\n)+?)((?:\r?\n){2,})|)/m, function(match, title, nav) { // inject menu var file = path.basename(pathname) var link = new RegExp("([ \t]*)(- )(\\[.+?\\]\\(" + file + "\\))") var replace = function(match, space, li, link) { @@ -53,7 +55,7 @@ function generate(pathname) { .replace(/\[version\]/, version) // update version .replace(/\[body\]/, markedHtml) .replace(/(.+?)<\/h.>/gim, function(match, n, id, text) { // fix anchors - return "/g, "").replace(/.+?<\/a>/g, "").replace(/\.|\[|\]|"|\/|\(|\)/g, "").replace(/\s/g, "-") + "\">" + text + "" + return ".+?<\/a>/g, "").replace(/\.|\[|\]|"|\/|\(|\)/g, "").replace(/\s/g, "-") + '">' + text + "" }) fs.writeFileSync("../mithril/archive/v" + version + "/" + outputFilename.replace(/^docs\//, ""), html, "utf-8") fs.writeFileSync("../mithril/" + outputFilename.replace(/^docs\//, ""), html, "utf-8") diff --git a/docs/lint.js b/docs/lint.js index 3ab0d559..2fb39e3b 100644 --- a/docs/lint.js +++ b/docs/lint.js @@ -1,4 +1,5 @@ #!/usr/bin/env node +"use strict" var fs = require("fs") var path = require("path") @@ -100,11 +101,11 @@ function ensureLinkIsValid(file, data) { } function initMocks() { - global.window = require("../test-utils/browserMock")() + global.window = require("../test-utils/browserMock")() // eslint-disable-line global-require global.document = window.document - global.m = require("../index") - global.o = require("../ospec/ospec") - global.stream = require("../stream") + global.m = require("../index") // eslint-disable-line global-require + global.o = require("../ospec/ospec") // eslint-disable-line global-require + global.stream = require("../stream") // eslint-disable-line global-require global.alert = function() {} //routes consumed by request.md @@ -121,7 +122,7 @@ function initMocks() { "GET /api/v1/todos": function() { return {status: 200, responseText: JSON.stringify([])} }, - "PUT /api/v1/users/1": function() { + "PUT /api/v1/users/1": function(request) { return {status: 200, responseText: request.query.callback ? request.query.callback + "([])" : "[]"} }, "POST /api/v1/upload": function() { diff --git a/hyperscript.js b/hyperscript.js index 880a1594..16bf033a 100644 --- a/hyperscript.js +++ b/hyperscript.js @@ -1,3 +1,5 @@ +"use strict" + var hyperscript = require("./render/hyperscript") hyperscript.trust = require("./render/trust") diff --git a/mount.js b/mount.js index 7e54a628..73fa1ce9 100644 --- a/mount.js +++ b/mount.js @@ -1,3 +1,5 @@ +"use strict" + var redrawService = require("./redraw") -module.exports = require("./api/mount")(redrawService) \ No newline at end of file +module.exports = require("./api/mount")(redrawService) diff --git a/ospec/bin/ospec b/ospec/bin/ospec index c5034d09..11777a09 100644 --- a/ospec/bin/ospec +++ b/ospec/bin/ospec @@ -1,4 +1,5 @@ #!/usr/bin/env node +"use strict" var fs = require("fs") var path = require("path") @@ -31,9 +32,9 @@ function traverseDirectory(pathname, callback) { }) } -traverseDirectory(".", function(pathname, stat, children) { +traverseDirectory(".", function(pathname) { if (pathname.match(/(?:^|\/)tests\/.*\.js$/)) { - require(path.normalize(process.cwd()) + "/" + pathname) + require(path.normalize(process.cwd()) + "/" + pathname) // eslint-disable-line global-require } }) .then(o.run) diff --git a/ospec/ospec.js b/ospec/ospec.js index 7edc7ecb..0c58b347 100644 --- a/ospec/ospec.js +++ b/ospec/ospec.js @@ -1,3 +1,4 @@ +/* eslint-disable no-bitwise, no-process-exit */ "use strict" module.exports = new function init() { @@ -121,8 +122,8 @@ module.exports = new function init() { } function unique(subject) { if (hasOwn.call(ctx, subject)) { - console.warn("A test or a spec named `" + subject + "` was already defined") - while (hasOwn.call(ctx, subject)) subject += '*' + console.warn("A test or a spec named `" + subject + "` was already defined") + while (hasOwn.call(ctx, subject)) subject += "*" } return subject } diff --git a/ospec/tests/test-ospec.js b/ospec/tests/test-ospec.js index ff5d1d8f..0367e6ec 100644 --- a/ospec/tests/test-ospec.js +++ b/ospec/tests/test-ospec.js @@ -28,11 +28,11 @@ o.spec("ospec", function() { o.beforeEach(function() {b = 1}) o.afterEach(function() {b = 0}) - try {o('illegal assertion')} catch (e) {illegalAssertionThrows = true} + try {o("illegal assertion")} catch (e) {illegalAssertionThrows = true} o("assertions", function() { var nestedTestDeclarationThrows = false - try {o('illegal nested test', function(){})} catch (e) {nestedTestDeclarationThrows = true} + try {o("illegal nested test", function(){})} catch (e) {nestedTestDeclarationThrows = true} o(illegalAssertionThrows).equals(true) o(nestedTestDeclarationThrows).equals(true) @@ -51,7 +51,7 @@ o.spec("ospec", function() { o(undef1).notDeepEquals(undef2) o(undef1).notDeepEquals({}) o({}).notDeepEquals(undef1) - + var sparse1 = [void 1, void 2, void 3] delete sparse1[0] var sparse2 = [void 1, void 2, void 3] @@ -63,7 +63,7 @@ o.spec("ospec", function() { monkeypatch1.field = 3 var monkeypatch2 = [1, 2] monkeypatch2.field = 4 - + o(monkeypatch1).notDeepEquals([1, 2]) o(monkeypatch1).notDeepEquals(monkeypatch2) diff --git a/promise/tests/test-promise.js b/promise/tests/test-promise.js index a91b0bcb..e52e792b 100644 --- a/promise/tests/test-promise.js +++ b/promise/tests/test-promise.js @@ -57,7 +57,7 @@ o.spec("promise", function() { o.spec("resolve", function() { o("resolves once", function(done) { var callCount = 0 - var promise = new Promise(function(resolve, reject) { + var promise = new Promise(function(resolve) { resolve(1) resolve(2) callAsync(function() {resolve(3)}) @@ -89,7 +89,7 @@ o.spec("promise", function() { var promise = Promise.resolve() state = 1 - promise.then(function(value) { + promise.then(function() { o(state).equals(2) done() }) @@ -104,7 +104,7 @@ o.spec("promise", function() { }) }) o("resolves asynchronously via executor", function(done) { - var promise = new Promise(function(resolve, reject) { + var promise = new Promise(function(resolve) { callAsync(function() {resolve(1)}) }) @@ -185,7 +185,7 @@ o.spec("promise", function() { var promise = Promise.reject() state = 1 - promise.then(null, function(value) { + promise.then(null, function() { o(state).equals(2) done() }) @@ -232,7 +232,7 @@ o.spec("promise", function() { }) }) o("rejects via executor on error", function(done) { - var promise = new Promise(function(resolve, reject) { + var promise = new Promise(function() { throw 1 }) @@ -281,7 +281,7 @@ o.spec("promise", function() { }).then(done) }) o("absorbs resolved promise in executor resolve", function(done) { - var promise = new Promise(function(resolve, reject) { + var promise = new Promise(function(resolve) { var p = Promise.resolve(1) resolve(p) }) @@ -310,7 +310,7 @@ o.spec("promise", function() { }) }) o("absorbs rejected promise in executor resolve", function(done) { - var promise = new Promise(function(resolve, reject) { + var promise = new Promise(function(resolve) { resolve(Promise.reject(1)) }) @@ -330,7 +330,7 @@ o.spec("promise", function() { }) }) o("absorbs pending promise that resolves via static resolver", function(done) { - var pending = new Promise(function(resolve, reject) { + var pending = new Promise(function(resolve) { setTimeout(function() {resolve(1)}, 10) }) var promise = Promise.resolve(pending) @@ -341,10 +341,10 @@ o.spec("promise", function() { }) }) o("absorbs pending promise that resolves in executor resolve", function(done) { - var pending = new Promise(function(resolve, reject) { + var pending = new Promise(function(resolve) { setTimeout(function() {resolve(1)}, 10) }) - var promise = new Promise(function(resolve, reject) { + var promise = new Promise(function(resolve) { resolve(pending) }) @@ -354,7 +354,7 @@ o.spec("promise", function() { }) }) o("absorbs pending promise that resolves on fulfillment", function(done) { - var pending = new Promise(function(resolve, reject) { + var pending = new Promise(function(resolve) { setTimeout(function() {resolve(1)}, 10) }) var promise = Promise.resolve() @@ -381,7 +381,7 @@ o.spec("promise", function() { var pending = new Promise(function(resolve, reject) { setTimeout(function() {reject(1)}, 10) }) - var promise = new Promise(function(resolve, reject) { + var promise = new Promise(function(resolve) { resolve(pending) }) @@ -521,7 +521,7 @@ o.spec("promise", function() { o.spec("race", function() { o("resolves to first resolved", function(done) { var a = Promise.resolve(1) - var b = new Promise(function(resolve, reject) { + var b = new Promise(function(resolve) { callAsync(function() {resolve(2)}) }) Promise.race([a, b]).then(function(value) { @@ -542,7 +542,7 @@ o.spec("promise", function() { }) o.spec("all", function() { o("resolves to array", function(done) { - var a = new Promise(function(resolve, reject) { + var a = new Promise(function(resolve) { callAsync(function() {resolve(1)}) }) var b = Promise.resolve(2) @@ -558,7 +558,7 @@ o.spec("promise", function() { }) }) o("resolves non-promise to itself", function(done) { - var a = new Promise(function(resolve, reject) { + var a = new Promise(function(resolve) { callAsync(function() {resolve(1)}) }) var b = Promise.resolve(2) @@ -595,7 +595,7 @@ o.spec("promise", function() { }) }) - promise.then(function(value) { + promise.then(function() { o(readCount).equals(1) done() }) diff --git a/redraw.js b/redraw.js index 01ac46f2..314e5002 100644 --- a/redraw.js +++ b/redraw.js @@ -1 +1,3 @@ -module.exports = require("./api/redraw")(window) \ No newline at end of file +"use strict" + +module.exports = require("./api/redraw")(window) diff --git a/render.js b/render.js index 5497c9bd..a6f8ce8d 100644 --- a/render.js +++ b/render.js @@ -1 +1,3 @@ -module.exports = require("./render/render")(window) \ No newline at end of file +"use strict" + +module.exports = require("./render/render")(window) diff --git a/render/tests/test-attributes.js b/render/tests/test-attributes.js index 2d08007b..c6a3df31 100644 --- a/render/tests/test-attributes.js +++ b/render/tests/test-attributes.js @@ -12,19 +12,19 @@ o.spec("attributes", function() { render = vdom($window).render }) o.spec("customElements", function(){ - + o("when vnode is customElement, custom setAttribute called", function(){ - var normal = [ - { tag: "input", attrs: { value: 'hello' } }, - { tag: "input", attrs: { value: 'hello' } }, - { tag: "input", attrs: { value: 'hello' } } + var normal = [ + {tag: "input", attrs: {value: "hello"}}, + {tag: "input", attrs: {value: "hello"}}, + {tag: "input", attrs: {value: "hello"}} ] - + var custom = [ - { tag: "custom-element", attrs: { custom: 'x' } }, - { tag: "input", attrs: { is: 'something-special', custom: 'x' } }, - { tag: "custom-element", attrs: { is: 'something-special', custom: 'x' } } + {tag: "custom-element", attrs: {custom: "x"}}, + {tag: "input", attrs: {is: "something-special", custom: "x"}}, + {tag: "custom-element", attrs: {is: "something-special", custom: "x"}} ] var view = normal.concat(custom) @@ -43,8 +43,8 @@ o.spec("attributes", function() { } render(root, view) - - o(spy.callCount).equals( custom.length ) + + o(spy.callCount).equals(custom.length) }) }) @@ -133,7 +133,7 @@ o.spec("attributes", function() { }) o.spec("contenteditable throws on untrusted children", function() { o("including text nodes", function() { - var div = {tag: "div", attrs: {contenteditable: true}, text: ''} + var div = {tag: "div", attrs: {contenteditable: true}, text: ""} var succeeded = false try { @@ -141,7 +141,7 @@ o.spec("attributes", function() { succeeded = true } - catch(e){} + catch(e){/* ignore */} o(succeeded).equals(false) }) @@ -154,7 +154,7 @@ o.spec("attributes", function() { succeeded = true } - catch(e){} + catch(e){/* ignore */} o(succeeded).equals(false) }) @@ -167,7 +167,7 @@ o.spec("attributes", function() { succeeded = true } - catch(e){} + catch(e){/* ignore */} o(succeeded).equals(true) }) @@ -180,7 +180,7 @@ o.spec("attributes", function() { succeeded = true } - catch(e){} + catch(e){/* ignore */} o(succeeded).equals(true) }) diff --git a/render/tests/test-component.js b/render/tests/test-component.js index 64cd6fcb..fcf206e8 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -63,7 +63,7 @@ o.spec("component", function() { o("updates root from null", function() { var visible = false var component = createComponent({ - view: function(vnode) { + view: function() { return visible ? {tag: "div"} : null } }) @@ -76,7 +76,7 @@ o.spec("component", function() { o("updates root from primitive", function() { var visible = false var component = createComponent({ - view: function(vnode) { + view: function() { return visible ? {tag: "div"} : false } }) @@ -89,7 +89,7 @@ o.spec("component", function() { o("updates root to null", function() { var visible = true var component = createComponent({ - view: function(vnode) { + view: function() { return visible ? {tag: "div"} : null } }) @@ -102,7 +102,7 @@ o.spec("component", function() { o("updates root to primitive", function() { var visible = true var component = createComponent({ - view: function(vnode) { + view: function() { return visible ? {tag: "div"} : false } }) @@ -114,7 +114,7 @@ o.spec("component", function() { }) o("updates root from null to null", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return null } }) @@ -125,7 +125,7 @@ o.spec("component", function() { }) o("removes", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return {tag: "div"} } }) @@ -138,7 +138,7 @@ o.spec("component", function() { }) o("svg works when creating across component boundary", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return {tag: "g"} } }) @@ -148,7 +148,7 @@ o.spec("component", function() { }) o("svg works when updating across component boundary", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return {tag: "g"} } }) @@ -161,7 +161,7 @@ o.spec("component", function() { o.spec("return value", function() { o("can return fragments", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return [ {tag: "label"}, {tag: "input"}, @@ -176,7 +176,7 @@ o.spec("component", function() { }) o("can return string", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return "a" } }) @@ -187,7 +187,7 @@ o.spec("component", function() { }) o("can return falsy string", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return "" } }) @@ -198,7 +198,7 @@ o.spec("component", function() { }) o("can return number", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return 1 } }) @@ -209,7 +209,7 @@ o.spec("component", function() { }) o("can return falsy number", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return 0 } }) @@ -220,7 +220,7 @@ o.spec("component", function() { }) o("can return boolean", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return true } }) @@ -231,7 +231,7 @@ o.spec("component", function() { }) o("can return falsy boolean", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return false } }) @@ -242,7 +242,7 @@ o.spec("component", function() { }) o("can return null", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return null } }) @@ -252,7 +252,7 @@ o.spec("component", function() { }) o("can return undefined", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return undefined } }) @@ -278,7 +278,7 @@ o.spec("component", function() { }) o("can update when returning fragments", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return [ {tag: "label"}, {tag: "input"}, @@ -294,7 +294,7 @@ o.spec("component", function() { }) o("can update when returning primitive", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return "a" } }) @@ -306,7 +306,7 @@ o.spec("component", function() { }) o("can update when returning null", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return null } }) @@ -317,7 +317,7 @@ o.spec("component", function() { }) o("can remove when returning fragments", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return [ {tag: "label"}, {tag: "input"}, @@ -334,7 +334,7 @@ o.spec("component", function() { }) o("can remove when returning primitive", function() { var component = createComponent({ - view: function(vnode) { + view: function() { return "a" } }) @@ -403,7 +403,7 @@ o.spec("component", function() { viewCalled = true return [{tag: "div", attrs: {id: "a"}, text: "b"}] }, - oninit: function(vnode) { + oninit: function() { o(viewCalled).equals(false) }, } @@ -646,7 +646,6 @@ o.spec("component", function() { return {tag: "div"} } }) - var update = o.spy() var vnode = {tag: component, key: 1} var updated = {tag: component, key: 1} @@ -659,7 +658,6 @@ o.spec("component", function() { }) o.spec("state", function() { o("initializes state", function() { - var called = 0 var data = {a: 1} var component = createComponent(createComponent({ data: data, @@ -676,7 +674,6 @@ o.spec("component", function() { } }) o('state "copy" is shallow', function() { - var called = 0 var body = {a: 1} var data = [body] var component = createComponent(createComponent({ @@ -701,7 +698,6 @@ o.spec("component", function() { o.spec("POJO state", function() { o("copies state", function() { - var called = 0 var data = {a: 1} var component = { data: data, diff --git a/render/tests/test-createElement.js b/render/tests/test-createElement.js index 31599cfd..9e2827cf 100644 --- a/render/tests/test-createElement.js +++ b/render/tests/test-createElement.js @@ -1,3 +1,4 @@ +/* eslint-disable no-script-url */ "use strict" var o = require("../../ospec/ospec") diff --git a/render/tests/test-hyperscript.js b/render/tests/test-hyperscript.js index 9e554541..2264fe18 100644 --- a/render/tests/test-hyperscript.js +++ b/render/tests/test-hyperscript.js @@ -1,3 +1,5 @@ +"use strict" + var o = require("../../ospec/ospec") var m = require("../../render/hyperscript") diff --git a/render/tests/test-onbeforeremove.js b/render/tests/test-onbeforeremove.js index 4395f90a..2e23215b 100644 --- a/render/tests/test-onbeforeremove.js +++ b/render/tests/test-onbeforeremove.js @@ -17,7 +17,6 @@ o.spec("onbeforeremove", function() { o("does not call onbeforeremove when creating", function() { var create = o.spy() - var update = o.spy() var vnode = {tag: "div", attrs: {onbeforeremove: create}} render(root, [vnode]) @@ -142,7 +141,7 @@ o.spec("onbeforeremove", function() { o(vnode.dom.attributes["onbeforeremove"]).equals(undefined) }) o("does not recycle when there's an onbeforeremove", function() { - var remove = function(vnode) {} + var remove = function() {} var vnode = {tag: "div", key: 1, attrs: {onbeforeremove: remove}} var updated = {tag: "div", key: 1, attrs: {onbeforeremove: remove}} @@ -153,7 +152,7 @@ o.spec("onbeforeremove", function() { o(vnode.dom).notEquals(updated.dom) }) o("does not leave elements out of order during removal", function(done) { - var remove = function(vnode) {return Promise.resolve()} + var remove = function() {return Promise.resolve()} var vnodes = [{tag: "div", key: 1, attrs: {onbeforeremove: remove}, text: "1"}, {tag: "div", key: 2, attrs: {onbeforeremove: remove}, text: "2"}] var updated = {tag: "div", key: 2, attrs: {onbeforeremove: remove}, text: "2"} diff --git a/render/tests/test-onbeforeupdate.js b/render/tests/test-onbeforeupdate.js index cf16dd1c..a3a49a8a 100644 --- a/render/tests/test-onbeforeupdate.js +++ b/render/tests/test-onbeforeupdate.js @@ -92,11 +92,10 @@ o.spec("onbeforeupdate", function() { o("is not called on creation", function() { var count = 0 var vnode = {tag: "div", attrs: {id: "a", onbeforeupdate: onbeforeupdate}} - var updated = {tag: "div", attrs: {id: "b", onbeforeupdate: onbeforeupdate}} render(root, [vnode]) - function onbeforeupdate(vnode, old) { + function onbeforeupdate() { count++ return true } @@ -112,7 +111,7 @@ o.spec("onbeforeupdate", function() { render(root, [vnode]) render(root, [updated]) - function onbeforeupdate(vnode, old) { + function onbeforeupdate() { count++ return true } @@ -248,7 +247,7 @@ o.spec("onbeforeupdate", function() { }) o("is not called on component creation", function() { - var component = createComponent({ + createComponent({ onbeforeupdate: onbeforeupdate, view: function(vnode) { return {tag: "div", attrs: vnode.attrs} @@ -257,11 +256,10 @@ o.spec("onbeforeupdate", function() { var count = 0 var vnode = {tag: "div", attrs: {id: "a"}} - var updated = {tag: "div", attrs: {id: "b"}} render(root, [vnode]) - function onbeforeupdate(vnode, old) { + function onbeforeupdate() { count++ return true } @@ -284,7 +282,7 @@ o.spec("onbeforeupdate", function() { render(root, [vnode]) render(root, [updated]) - function onbeforeupdate(vnode, old) { + function onbeforeupdate() { count++ return true } @@ -293,4 +291,4 @@ o.spec("onbeforeupdate", function() { }) }) }) -}) \ No newline at end of file +}) diff --git a/render/tests/test-oncreate.js b/render/tests/test-oncreate.js index d6f275c2..eb1daa5e 100644 --- a/render/tests/test-oncreate.js +++ b/render/tests/test-oncreate.js @@ -128,7 +128,6 @@ o.spec("oncreate", function() { }) o("does not call oncreate when removing", function() { var create = o.spy() - var update = o.spy() var vnode = {tag: "div", attrs: {oncreate: create}, state: {}} render(root, [vnode]) diff --git a/render/tests/test-oninit.js b/render/tests/test-oninit.js index 3265cd8b..4d94cae4 100644 --- a/render/tests/test-oninit.js +++ b/render/tests/test-oninit.js @@ -128,7 +128,6 @@ o.spec("oninit", function() { }) o("does not call oninit when removing", function() { var create = o.spy() - var update = o.spy() var vnode = {tag: "div", attrs: {oninit: create}, state: {}} render(root, [vnode]) diff --git a/render/tests/test-onupdate.js b/render/tests/test-onupdate.js index 448a44f0..13f62c46 100644 --- a/render/tests/test-onupdate.js +++ b/render/tests/test-onupdate.js @@ -59,7 +59,6 @@ o.spec("onupdate", function() { }) o("does not call old onupdate when removing the onupdate property in new vnode", function() { var create = o.spy() - var update = o.spy() var vnode = {tag: "a", attrs: {onupdate: create}} var updated = {tag: "a"} diff --git a/render/tests/test-render.js b/render/tests/test-render.js index 82e5ddba..92bcd93c 100644 --- a/render/tests/test-render.js +++ b/render/tests/test-render.js @@ -1,7 +1,6 @@ "use strict" var o = require("../../ospec/ospec") -var components = require("../../test-utils/components") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") @@ -124,7 +123,7 @@ o.spec("render", function() { var onbeforeupdate = o.spy() function A() { return { - view: function(vnode) {throw new Error("error")}, + view: function() {throw new Error("error")}, oninit: oninit, onbeforeupdate: onbeforeupdate } @@ -143,11 +142,11 @@ o.spec("render", function() { o(onbeforeupdate.callCount).equals(0) }) 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() {throw new Error("error")}) var onbeforeupdate = o.spy() function A() { return { - view: function(vnode) {}, + view: function() {}, oninit: oninit, onbeforeupdate: onbeforeupdate } diff --git a/render/tests/test-updateElement.js b/render/tests/test-updateElement.js index 2211efd1..1774e61f 100644 --- a/render/tests/test-updateElement.js +++ b/render/tests/test-updateElement.js @@ -213,10 +213,10 @@ o.spec("updateElement", function() { }) o("updates svg child", function() { var vnode = {tag: "svg", children: [{ - tag: 'circle' + tag: "circle" }]} var updated = {tag: "svg", children: [{ - tag: 'line' + tag: "line" }]} render(root, [vnode]) @@ -235,7 +235,7 @@ o.spec("updateElement", function() { render(root, [vnode]) var c = vnode.dom - + o(root.childNodes.length).equals(1) o(a).equals(c) }) @@ -254,7 +254,7 @@ o.spec("updateElement", function() { render(root, [e, b, f]) var y = root.childNodes[1] - + o(root.childNodes.length).equals(3) o(x).equals(y) }) diff --git a/render/tests/test-updateNodes.js b/render/tests/test-updateNodes.js index c58a29b4..fa5231ea 100644 --- a/render/tests/test-updateNodes.js +++ b/render/tests/test-updateNodes.js @@ -727,7 +727,7 @@ o.spec("updateNodes", function() { }) o("change type, position and length", function() { var vnodes = {tag: "div", children: [ - undefined, + undefined, {tag: "#", children: "a"} ]} var updated = {tag: "div", children: [ @@ -738,7 +738,7 @@ o.spec("updateNodes", function() { render(root, vnodes) render(root, updated) - + o(root.firstChild.childNodes.length).equals(1) }) o("removes then recreates then reverses children", function() { @@ -864,13 +864,13 @@ o.spec("updateNodes", function() { var vnodes = [{tag: "div"}, {tag: "a", attrs: {oncreate: create, onupdate: update, onremove: remove}}] var temp = [null, {tag: "a", attrs: {oncreate: create, onupdate: update, onremove: remove}}] var updated = [{tag: "div"}, {tag: "a", attrs: {oncreate: create, onupdate: update, onremove: remove}}] - + render(root, vnodes) var before = vnodes[1].dom render(root, temp) render(root, updated) var after = updated[1].dom - + o(before).equals(after) o(create.callCount).equals(1) o(update.callCount).equals(2) @@ -883,13 +883,13 @@ o.spec("updateNodes", function() { var vnodes = [{tag: "b"}, {tag: "div"}, {tag: "a", attrs: {oncreate: create, onupdate: update, onremove: remove}}] var temp = [{tag: "b"}, null, {tag: "a", attrs: {oncreate: create, onupdate: update, onremove: remove}}] var updated = [{tag: "b"}, {tag: "div"}, {tag: "a", attrs: {oncreate: create, onupdate: update, onremove: remove}}] - + render(root, vnodes) var before = vnodes[2].dom render(root, temp) render(root, updated) var after = updated[2].dom - + o(before).equals(after) o(create.callCount).equals(1) o(update.callCount).equals(2) @@ -898,11 +898,10 @@ o.spec("updateNodes", function() { o("node is recreated if key changes to undefined", function () { var vnode = {tag: "b", key: 1} var updated = {tag: "b"} - + render(root, vnode) - var dom = vnode.dom render(root, updated) - + o(vnode.dom).notEquals(updated.dom) }) components.forEach(function(cmp){ @@ -943,4 +942,4 @@ o.spec("updateNodes", function() { }) }) }) -}) \ No newline at end of file +}) diff --git a/render/vnode.js b/render/vnode.js index 56df8c81..48d37239 100644 --- a/render/vnode.js +++ b/render/vnode.js @@ -1,3 +1,5 @@ +"use strict" + function Vnode(tag, key, attrs, children, text, dom) { return {tag: tag, key: key, attrs: attrs, children: children, text: text, dom: dom, domSize: undefined, state: {}, events: undefined, instance: undefined, skip: false} } diff --git a/request.js b/request.js index 7e86421a..cb3bfbbe 100644 --- a/request.js +++ b/request.js @@ -1,2 +1,4 @@ +"use strict" + var PromisePolyfill = require("./promise/promise") module.exports = require("./request/request")(window, PromisePolyfill) diff --git a/request/tests/test-jsonp.js b/request/tests/test-jsonp.js index 7042274c..076ab26e 100644 --- a/request/tests/test-jsonp.js +++ b/request/tests/test-jsonp.js @@ -7,7 +7,7 @@ var Promise = require("../../promise/promise") var parseQueryString = require("../../querystring/parse") o.spec("jsonp", function() { - var mock, jsonp, spy, complete + var mock, jsonp, complete o.beforeEach(function() { mock = xhrMock() var requestService = Request(mock, Promise) @@ -28,7 +28,6 @@ o.spec("jsonp", function() { }).then(done) }) o("first argument can be a string aliasing url property", function(done){ - var s = new Date mock.$defineRoutes({ "GET /item": function(request) { var queryData = parseQueryString(request.query) @@ -104,8 +103,8 @@ o.spec("jsonp", function() { return {status: 200, responseText: queryData["callback"] + "([])"} } }) - var promise = jsonp("/item", {background: true}).then(function() {}) - + jsonp("/item", {background: true}).then(function() {}) + setTimeout(function() { o(complete.callCount).equals(0) done() diff --git a/request/tests/test-request.js b/request/tests/test-request.js index 273d592c..7f965498 100644 --- a/request/tests/test-request.js +++ b/request/tests/test-request.js @@ -18,7 +18,6 @@ o.spec("xhr", function() { o.spec("success", function() { o("works via GET", function(done) { - var s = new Date mock.$defineRoutes({ "GET /item": function() { return {status: 200, responseText: JSON.stringify({a: 1})} @@ -31,7 +30,6 @@ o.spec("xhr", function() { }) }) o("implicit GET method", function(done){ - var s = new Date mock.$defineRoutes({ "GET /item": function() { return {status: 200, responseText: JSON.stringify({a: 1})} @@ -44,7 +42,6 @@ o.spec("xhr", function() { }) }) o("first argument can be a string aliasing url property", function(done){ - var s = new Date mock.$defineRoutes({ "GET /item": function() { return {status: 200, responseText: JSON.stringify({a: 1})} @@ -172,7 +169,7 @@ o.spec("xhr", function() { } mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 200, responseText: JSON.stringify([{id: 1}, {id: 2}, {id: 3}])} } }) @@ -186,7 +183,7 @@ o.spec("xhr", function() { } mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 200, responseText: JSON.stringify({id: 1})} } }) @@ -228,12 +225,12 @@ o.spec("xhr", function() { } mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 200, responseText: JSON.stringify({test: 123})} } }) xhr({method: "GET", url: "/item", deserialize: deserialize}).then(function(data) { - o(data).equals("{\"test\":123}") + o(data).equals('{"test":123}') }).then(done) }) o("deserialize parameter works in POST", function(done) { @@ -242,40 +239,40 @@ o.spec("xhr", function() { } mock.$defineRoutes({ - "POST /item": function(request) { + "POST /item": function() { return {status: 200, responseText: JSON.stringify({test: 123})} } }) xhr({method: "POST", url: "/item", deserialize: deserialize}).then(function(data) { - o(data).equals("{\"test\":123}") + o(data).equals('{"test":123}') }).then(done) }) o("extract parameter works in GET", function(done) { - var extract = function(data) { + var extract = function() { return JSON.stringify({test: 123}) } mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 200, responseText: ""} } }) xhr({method: "GET", url: "/item", extract: extract}).then(function(data) { - o(data).equals("{\"test\":123}") + o(data).equals('{"test":123}') }).then(done) }) o("extract parameter works in POST", function(done) { - var extract = function(data) { + var extract = function() { return JSON.stringify({test: 123}) } mock.$defineRoutes({ - "POST /item": function(request) { + "POST /item": function() { return {status: 200, responseText: ""} } }) xhr({method: "POST", url: "/item", extract: extract}).then(function(data) { - o(data).equals("{\"test\":123}") + o(data).equals('{"test":123}') }).then(done) }) o("ignores deserialize if extract is defined", function(done) { @@ -285,7 +282,7 @@ o.spec("xhr", function() { var deserialize = o.spy() mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 200, responseText: ""} } }) @@ -297,7 +294,7 @@ o.spec("xhr", function() { }) o("config parameter works", function(done) { mock.$defineRoutes({ - "POST /item": function(request) { + "POST /item": function() { return {status: 200, responseText: ""} } }) @@ -311,7 +308,7 @@ o.spec("xhr", function() { }) o("requests don't block each other", function(done) { mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 200, responseText: "[]"} } }) @@ -328,7 +325,7 @@ o.spec("xhr", function() { }) o("requests trigger finally once with a chained then", function(done) { mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 200, responseText: "[]"} } }) @@ -342,11 +339,11 @@ o.spec("xhr", function() { }) o("requests does not trigger finally when background: true", function(done) { mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 200, responseText: "[]"} } }) - var promise = xhr("/item", {background: true}).then(function() {}) + xhr("/item", {background: true}).then(function() {}) setTimeout(function() { o(complete.callCount).equals(0) @@ -355,7 +352,7 @@ o.spec("xhr", function() { }) o("headers are set when header arg passed", function(done) { mock.$defineRoutes({ - "POST /item": function(request) { + "POST /item": function() { return {status: 200, responseText: ""} } }) @@ -367,7 +364,7 @@ o.spec("xhr", function() { }) o("headers are with higher precedence than default headers", function(done) { mock.$defineRoutes({ - "POST /item": function(request) { + "POST /item": function() { return {status: 200, responseText: ""} } }) @@ -379,7 +376,7 @@ o.spec("xhr", function() { }) o("json headers are set to the correct default value", function(done) { mock.$defineRoutes({ - "POST /item": function(request) { + "POST /item": function() { return {status: 200, responseText: ""} } }) @@ -391,7 +388,6 @@ o.spec("xhr", function() { } }) o("doesn't fail on abort", function(done) { - var s = new Date mock.$defineRoutes({ "GET /item": function() { return {status: 200, responseText: JSON.stringify({a: 1})} @@ -410,9 +406,9 @@ o.spec("xhr", function() { done() }, 0) } - Object.defineProperty(xhr, 'onreadystatechange', { - set: function(val) { onreadystatechange = val } - , get: function() { return testonreadystatechange } + Object.defineProperty(xhr, "onreadystatechange", { + set: function(val) { onreadystatechange = val }, + get: function() { return testonreadystatechange } }) xhr.abort() } @@ -424,7 +420,6 @@ o.spec("xhr", function() { }) }) o("doesn't fail on file:// status 0", function(done) { - var s = new Date mock.$defineRoutes({ "GET /item": function() { return {status: 0, responseText: JSON.stringify({a: 1})} @@ -456,7 +451,7 @@ o.spec("xhr", function() { o.spec("failure", function() { o("rejects on server error", function(done) { mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 500, responseText: JSON.stringify({error: "error"})} } }) @@ -467,7 +462,7 @@ o.spec("xhr", function() { }) o("extends Error with JSON response", function(done) { mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 500, responseText: JSON.stringify({message: "error", stack: "error on line 1"})} } }) @@ -479,7 +474,7 @@ o.spec("xhr", function() { }) o("rejects on non-JSON server error", function(done) { mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 500, responseText: "error"} } }) @@ -489,7 +484,7 @@ o.spec("xhr", function() { }) o("triggers all branched catches upon rejection", function(done) { mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 500, responseText: "error"} } }) @@ -515,7 +510,7 @@ o.spec("xhr", function() { }) o("rejects on cors-like error", function(done) { mock.$defineRoutes({ - "GET /item": function(request) { + "GET /item": function() { return {status: 0} } }) diff --git a/route.js b/route.js index b8c8a15a..4e829527 100644 --- a/route.js +++ b/route.js @@ -1,3 +1,5 @@ +"use strict" + var redrawService = require("./redraw") -module.exports = require("./api/router")(window, redrawService) \ No newline at end of file +module.exports = require("./api/router")(window, redrawService) diff --git a/router/tests/test-getPath.js b/router/tests/test-getPath.js index 6c5dc2b2..9b2603fa 100644 --- a/router/tests/test-getPath.js +++ b/router/tests/test-getPath.js @@ -6,7 +6,7 @@ var Router = require("../../router/router") o.spec("Router.getPath", function() { void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) { - void ["#", "?", "", "#!", "?!", '/foo'].forEach(function(prefix) { + void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) { o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() { var $window, router, onRouteChange, onFail diff --git a/stream.js b/stream.js index 06dcd5d4..89e5baab 100644 --- a/stream.js +++ b/stream.js @@ -1 +1,3 @@ -module.exports = require("./stream/stream") \ No newline at end of file +"use strict" + +module.exports = require("./stream/stream") diff --git a/stream/tests/test-scan.js b/stream/tests/test-scan.js index 72ef0277..5b44cdab 100644 --- a/stream/tests/test-scan.js +++ b/stream/tests/test-scan.js @@ -22,7 +22,7 @@ o.spec("scan", function() { parent(7) parent("11") parent(undefined) - parent({ a: 1 }) + parent({a: 1}) var result = child() // deepEquals fails on arrays? @@ -32,4 +32,3 @@ o.spec("scan", function() { o(result[3]).deepEquals({a: 1}) }) }) - diff --git a/stream/tests/test-stream.js b/stream/tests/test-stream.js index 92e16620..e435c888 100644 --- a/stream/tests/test-stream.js +++ b/stream/tests/test-stream.js @@ -1,7 +1,6 @@ "use strict" var o = require("../../ospec/ospec") -var callAsync = require("../../test-utils/callAsync") var Stream = require("../stream") o.spec("stream", function() { @@ -105,7 +104,7 @@ o.spec("stream", function() { var streams = [] var a = Stream() var b = Stream() - var c = Stream.combine(function(a, b, changed) { + Stream.combine(function(a, b, changed) { streams = changed }, [a, b]) @@ -119,7 +118,7 @@ o.spec("stream", function() { var streams = [] var a = Stream(3) var b = Stream(5) - var c = Stream.combine(function(a, b, changed) { + Stream.combine(function(a, b, changed) { streams = changed }, [a, b]) @@ -130,7 +129,7 @@ o.spec("stream", function() { }) o("combine can return undefined", function() { var a = Stream(1) - var b = Stream.combine(function(a) { + var b = Stream.combine(function() { return undefined }, [a]) @@ -138,7 +137,7 @@ o.spec("stream", function() { }) o("combine can return stream", function() { var a = Stream(1) - var b = Stream.combine(function(a) { + var b = Stream.combine(function() { return Stream(2) }, [a]) @@ -146,7 +145,7 @@ o.spec("stream", function() { }) o("combine can return pending stream", function() { var a = Stream(1) - var b = Stream.combine(function(a) { + var b = Stream.combine(function() { return Stream() }, [a]) @@ -155,10 +154,9 @@ o.spec("stream", function() { o("combine can halt", function() { var count = 0 var a = Stream(1) - var b = Stream.combine(function(a) { + var b = Stream.combine(function() { return Stream.HALT - }, [a]) - ["fantasy-land/map"](function() { + }, [a])["fantasy-land/map"](function() { count++ return 1 }) @@ -170,7 +168,7 @@ o.spec("stream", function() { var a = Stream(1) var thrown = null; try { - var b = Stream.combine(spy, [a, '']) + Stream.combine(spy, [a, ""]) } catch (e) { thrown = e } @@ -210,8 +208,9 @@ o.spec("stream", function() { var a = Stream() var b = Stream() - var all = Stream.merge([a.map(id), b.map(id)]).map(function(data) { + Stream.merge([a.map(id), b.map(id)]).map(function(data) { value = data[0] + data[1] + return undefined }) a(1) @@ -344,7 +343,7 @@ o.spec("stream", function() { }) o("works with pending stream", function() { var stream = Stream(undefined) - var mapped = stream["fantasy-land/map"](function(value) {return Stream()}) + var mapped = stream["fantasy-land/map"](function() {return Stream()}) o(mapped()()).equals(undefined) }) diff --git a/test-utils/components.js b/test-utils/components.js index c25ad5da..1e6fdd72 100644 --- a/test-utils/components.js +++ b/test-utils/components.js @@ -1,24 +1,26 @@ +"use strict" + module.exports = [ { - kind: 'POJO', + kind: "POJO", create: function(methods) { - var res = {view: function() {return {tag:'div'}}} + var res = {view: function() {return {tag:"div"}}} Object.keys(methods || {}).forEach(function(m){res[m] = methods[m]}) return res } }, { - kind: 'constructible', + kind: "constructible", create: function(methods) { function res(){} - res.prototype.view = function() {return {tag:'div'}} + res.prototype.view = function() {return {tag:"div"}} Object.keys(methods || {}).forEach(function(m){res.prototype[m] = methods[m]}) return res } }, { - kind: 'closure', + kind: "closure", create: function(methods) { return function() { - var res = {view: function() {return {tag:'div'}}} + var res = {view: function() {return {tag:"div"}}} Object.keys(methods || {}).forEach(function(m){res[m] = methods[m]}) return res } diff --git a/test-utils/domMock.js b/test-utils/domMock.js index 5da93b98..a827070a 100644 --- a/test-utils/domMock.js +++ b/test-utils/domMock.js @@ -96,7 +96,7 @@ module.exports = function() { declList = declList.replace( /("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*')|\/\*[\s\S]*?\*\//g, function(m, str){ - return str || '' + return str || "" } ) /*eslint-disable no-cond-assign*/ @@ -115,7 +115,7 @@ module.exports = function() { var activeElement var $window = { document: { - createElement: function(tag, is) { + createElement: function(tag) { var cssText = "" var style = {} Object.defineProperty(style, "cssText", { @@ -211,11 +211,11 @@ module.exports = function() { else this.setAttribute("class", value) }, focus: function() {activeElement = this}, - addEventListener: function(type, callback, useCapture) { + addEventListener: function(type, callback) { if (events[type] == null) events[type] = [callback] else events[type].push(callback) }, - removeEventListener: function(type, callback, useCapture) { + removeEventListener: function(type, callback) { if (events[type] != null) { var index = events[type].indexOf(callback) if (index > -1) events[type].splice(index, 1) @@ -241,7 +241,6 @@ module.exports = function() { } if (element.nodeName === "A") { - var href Object.defineProperty(element, "href", { get: function() {return this.attributes["href"] === undefined ? "" : "[FIXME implement]"}, set: function(value) {this.setAttribute("href", value)}, @@ -271,7 +270,9 @@ module.exports = function() { enumerable: true, }) } - + + /* eslint-disable radix */ + if (element.nodeName === "CANVAS") { Object.defineProperty(element, "width", { get: function() {return this.attributes["width"] ? Math.floor(parseInt(this.attributes["width"].nodeValue) || 0) : 300}, @@ -283,6 +284,8 @@ module.exports = function() { }) } + /* eslint-enable radix */ + function getOptions(element) { var options = [] for (var i = 0; i < element.childNodes.length; i++) { @@ -297,17 +300,18 @@ module.exports = function() { element.firstChild != null ? element.firstChild.nodeValue : "" } if (element.nodeName === "SELECT") { - var selectedValue, selectedIndex = 0 + // var selectedValue + var selectedIndex = 0 Object.defineProperty(element, "selectedIndex", { get: function() {return getOptions(this).length > 0 ? selectedIndex : -1}, set: function(value) { var options = getOptions(this) if (value >= 0 && value < options.length) { - selectedValue = getOptionValue(options[selectedIndex]) + // selectedValue = getOptionValue(options[selectedIndex]) selectedIndex = value } else { - selectedValue = "" + // selectedValue = "" selectedIndex = -1 } }, @@ -323,12 +327,12 @@ module.exports = function() { var stringValue = String(value) for (var i = 0; i < options.length; i++) { if (getOptionValue(options[i]) === stringValue) { - selectedValue = stringValue + // selectedValue = stringValue selectedIndex = i return } } - selectedValue = stringValue + // selectedValue = stringValue selectedIndex = -1 }, enumerable: true, diff --git a/test-utils/parseURL.js b/test-utils/parseURL.js index f5e76e8d..e60cc531 100644 --- a/test-utils/parseURL.js +++ b/test-utils/parseURL.js @@ -3,7 +3,7 @@ module.exports = function parseURL(url, root) { var data = {} var protocolIndex = url.indexOf("://") - var pathnameIndex = protocolIndex > - 1 ? url.indexOf("/", protocolIndex + 3) : url.indexOf("/") + var pathnameIndex = protocolIndex > -1 ? url.indexOf("/", protocolIndex + 3) : url.indexOf("/") var searchIndex = url.indexOf("?") var hashIndex = url.indexOf("#") if ((pathnameIndex > searchIndex && searchIndex > -1) || (pathnameIndex > hashIndex && hashIndex > -1)) pathnameIndex = -1 diff --git a/test-utils/pushStateMock.js b/test-utils/pushStateMock.js index 91e7a136..ebd963fd 100644 --- a/test-utils/pushStateMock.js +++ b/test-utils/pushStateMock.js @@ -4,7 +4,7 @@ var parseURL = require("../test-utils/parseURL") module.exports = function(options) { if (options == null) options = {} - + var $window = options.window || {} var protocol = options.protocol || "http:" var hostname = options.hostname || "localhost" @@ -33,7 +33,7 @@ module.exports = function(options) { } return isNew } - + function prefix(prefix, value) { if (value === "") return "" return (value.charAt(0) !== prefix ? prefix : "") + value diff --git a/test-utils/tests/test-browserMock.js b/test-utils/tests/test-browserMock.js index d47c58ce..861f1ef0 100644 --- a/test-utils/tests/test-browserMock.js +++ b/test-utils/tests/test-browserMock.js @@ -17,7 +17,7 @@ o.spec("browserMock", function() { }) o("$window.onhashchange can be reached from the pushStateMock functions", function(done) { $window.onhashchange = o.spy() - $window.location.hash = '#a' + $window.location.hash = "#a" callAsync(function(){ o($window.onhashchange.callCount).equals(1) @@ -33,7 +33,7 @@ o.spec("browserMock", function() { }) o("$window.onunload can be reached from the pushStateMock functions", function() { $window.onunload = o.spy() - $window.location.href = '/a' + $window.location.href = "/a" o($window.onunload.callCount).equals(1) }) diff --git a/test-utils/tests/test-components.js b/test-utils/tests/test-components.js index cc9a091b..30ea6175 100644 --- a/test-utils/tests/test-components.js +++ b/test-utils/tests/test-components.js @@ -6,8 +6,8 @@ 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') + o("works", function() { + o(typeof component.kind).equals("string") var methods = {oninit: function(){}, view: function(){}} @@ -21,7 +21,7 @@ o.spec("test-utils/components", function() { cmp2 = new (component.create(methods)) } else if (component.kind === "closure") { cmp1 = component.create()() - cmp2 = component.create(methods)() + cmp2 = component.create(methods)() } else { throw new Error("unexpected component kind") } @@ -34,7 +34,7 @@ o.spec("test-utils/components", function() { o(vnode != null).equals(true) o(vnode).deepEquals({tag: "div"}) - if (component.kind !== 'constructible') { + if (component.kind !== "constructible") { o(cmp2).deepEquals(methods) } else { // deepEquals doesn't search the prototype, do it manually diff --git a/test-utils/tests/test-domMock.js b/test-utils/tests/test-domMock.js index edd896d5..aee68832 100644 --- a/test-utils/tests/test-domMock.js +++ b/test-utils/tests/test-domMock.js @@ -321,7 +321,7 @@ o.spec("domMock", function() { o(div.getAttribute("id")).equals("aaa") }) }) - + o.spec("setAttribute", function() { o("works", function() { var div = $document.createElement("div") @@ -393,7 +393,6 @@ o.spec("domMock", function() { o.spec("textContent", function() { o("works", function() { var div = $document.createElement("div") - var a = $document.createElement("a") div.textContent = "aaa" o(div.childNodes.length).equals(1) @@ -402,7 +401,6 @@ o.spec("domMock", function() { }) o("works with empty string", function() { var div = $document.createElement("div") - var a = $document.createElement("a") div.textContent = "" o(div.childNodes.length).equals(0) @@ -514,8 +512,8 @@ o.spec("domMock", function() { var div = $document.createElement("div") div.style.cssText = "background: url(';'); font-family: \";\"" - o(div.style.background).equals("url(';')") - o(div.style.fontFamily).equals("\";\"") + o(div.style.background).equals("url(';')") + o(div.style.fontFamily).equals('";"') o(div.style.cssText).equals("background: url(';'); font-family: \";\";") }) o("comments in style.cssText are stripped", function(){ @@ -534,9 +532,10 @@ o.spec("domMock", function() { }) o("setting style throws", function () { + var div = $document.createElement("div") var err = false try { - div.style = '' + div.style = "" } catch (e) { err = e } @@ -919,55 +918,55 @@ o.spec("domMock", function() { o.spec("canvas width and height", function() { o("setting property works", function() { var canvas = $document.createElement("canvas") - + canvas.width = 100 o(canvas.attributes["width"].nodeValue).equals("100") o(canvas.width).equals(100) - + canvas.height = 100 o(canvas.attributes["height"].nodeValue).equals("100") o(canvas.height).equals(100) }) o("setting string casts to number", function() { var canvas = $document.createElement("canvas") - + canvas.width = "100" o(canvas.attributes["width"].nodeValue).equals("100") o(canvas.width).equals(100) - + canvas.height = "100" o(canvas.attributes["height"].nodeValue).equals("100") o(canvas.height).equals(100) }) o("setting float casts to int", function() { var canvas = $document.createElement("canvas") - + canvas.width = 1.2 o(canvas.attributes["width"].nodeValue).equals("1") o(canvas.width).equals(1) - + canvas.height = 1.2 o(canvas.attributes["height"].nodeValue).equals("1") o(canvas.height).equals(1) }) o("setting percentage fails", function() { var canvas = $document.createElement("canvas") - + canvas.width = "100%" o(canvas.attributes["width"].nodeValue).equals("0") o(canvas.width).equals(0) - + canvas.height = "100%" o(canvas.attributes["height"].nodeValue).equals("0") o(canvas.height).equals(0) }) o("setting attribute works", function() { var canvas = $document.createElement("canvas") - + canvas.setAttribute("width", "100%") o(canvas.attributes["width"].nodeValue).equals("100%") o(canvas.width).equals(100) - + canvas.setAttribute("height", "100%") o(canvas.attributes["height"].nodeValue).equals("100%") o(canvas.height).equals(100) diff --git a/test-utils/tests/test-pushStateMock.js b/test-utils/tests/test-pushStateMock.js index 3cb51716..41efacab 100644 --- a/test-utils/tests/test-pushStateMock.js +++ b/test-utils/tests/test-pushStateMock.js @@ -168,13 +168,13 @@ o.spec("pushStateMock", function() { }) o.spec("set protocol", function() { o("setting protocol throws", function(done) { - var old = $window.location.href try { $window.location.protocol = "https://" } catch (e) { - done() + return done() } + throw new Error("Expected an error") }) }) o.spec("set port", function() { @@ -413,17 +413,17 @@ o.spec("pushStateMock", function() { }) o("replaceState does not break forward history", function() { $window.onpopstate = o.spy() - + $window.history.pushState(null, null, "b") $window.history.back() o($window.onpopstate.callCount).equals(1) o($window.location.href).equals("http://localhost/") - + $window.history.replaceState(null, null, "a") - + o($window.location.href).equals("http://localhost/a") - + $window.history.forward() o($window.onpopstate.callCount).equals(2) @@ -431,46 +431,46 @@ o.spec("pushStateMock", function() { }) o("pushstate retains state", function() { $window.onpopstate = o.spy() - + $window.history.pushState({a: 1}, null, "#a") $window.history.pushState({b: 2}, null, "#b") - + o($window.onpopstate.callCount).equals(0) $window.history.back() - + o($window.onpopstate.callCount).equals(1) o($window.onpopstate.args[0].type).equals("popstate") o($window.onpopstate.args[0].state).deepEquals({a: 1}) $window.history.back() - + o($window.onpopstate.callCount).equals(2) o($window.onpopstate.args[0].type).equals("popstate") o($window.onpopstate.args[0].state).equals(null) $window.history.forward() - + o($window.onpopstate.callCount).equals(3) o($window.onpopstate.args[0].type).equals("popstate") o($window.onpopstate.args[0].state).deepEquals({a: 1}) $window.history.forward() - + o($window.onpopstate.callCount).equals(4) o($window.onpopstate.args[0].type).equals("popstate") o($window.onpopstate.args[0].state).deepEquals({b: 2}) }) o("replacestate replaces state", function() { $window.onpopstate = o.spy(pop) - + $window.history.replaceState({a: 1}, null, "a") - + o($window.history.state).deepEquals({a: 1}) - + $window.history.pushState(null, null, "a") $window.history.back() - + function pop(e) { o(e.state).deepEquals({a: 1}) o($window.history.state).deepEquals({a: 1}) diff --git a/test-utils/tests/test-xhrMock.js b/test-utils/tests/test-xhrMock.js index cef2d9ca..8bdfcf21 100644 --- a/test-utils/tests/test-xhrMock.js +++ b/test-utils/tests/test-xhrMock.js @@ -5,13 +5,13 @@ var xhrMock = require("../../test-utils/xhrMock") var parseQueryString = require("../../querystring/parse") o.spec("xhrMock", function() { - var $window, ajax + var $window o.beforeEach(function() { $window = xhrMock() }) o.spec("xhr", function() { - o("works", function(done, timeout) { + o("works", function(done) { $window.$defineRoutes({ "GET /item": function(request) { o(request.url).equals("/item") @@ -29,7 +29,7 @@ o.spec("xhrMock", function() { } xhr.send() }) - o("works w/ search", function(done, timeout) { + o("works w/ search", function(done) { $window.$defineRoutes({ "GET /item": function(request) { o(request.query).equals("?a=b") @@ -45,7 +45,7 @@ o.spec("xhrMock", function() { } xhr.send() }) - o("works w/ body", function(done, timeout) { + o("works w/ body", function(done) { $window.$defineRoutes({ "POST /item": function(request) { o(request.body).equals("a=b") @@ -61,7 +61,7 @@ o.spec("xhrMock", function() { } xhr.send("a=b") }) - o("handles routing error", function(done, timeout) { + o("handles routing error", function(done) { var xhr = new $window.XMLHttpRequest() xhr.open("GET", "/nonexistent") xhr.onreadystatechange = function() { @@ -113,7 +113,7 @@ o.spec("xhrMock", function() { done() } }) - o("works with other querystring params", function(done, timeout) { + o("works with other querystring params", function(done) { $window.$defineRoutes({ "GET /test": function(request) { var queryData = parseQueryString(request.query) diff --git a/test-utils/xhrMock.js b/test-utils/xhrMock.js index 57c2887f..d3bbf5b8 100644 --- a/test-utils/xhrMock.js +++ b/test-utils/xhrMock.js @@ -6,7 +6,7 @@ var parseQueryString = require("../querystring/parse") module.exports = function() { var routes = {} - var callback = "callback" + // var callback = "callback" var serverErrorHandler = function(url) { return {status: 500, responseText: "server error, most likely the URL was not defined " + url} } @@ -43,7 +43,6 @@ module.exports = function() { } self.readyState = 4 if (args.async === true) { - var s = new Date callAsync(function() { if (typeof self.onreadystatechange === "function") self.onreadystatechange() }) @@ -64,7 +63,7 @@ module.exports = function() { var urlData = parseURL(element.src, {protocol: "http:", hostname: "localhost", port: "", pathname: "/"}) var handler = routes["GET " + urlData.pathname] || serverErrorHandler.bind(null, element.src) var data = handler({url: urlData.pathname, query: urlData.search, body: null}) - var query = parseQueryString(urlData.search) + parseQueryString(urlData.search) callAsync(function() { if (data.status === 200) { new Function("$window", "with ($window) return " + data.responseText).call($window, $window) @@ -83,8 +82,8 @@ module.exports = function() { $defineRoutes: function(rules) { routes = rules }, - $defineJSONPCallbackKey: function(key) { - callback = key + $defineJSONPCallbackKey: function(/* key */) { + // callback = key }, } return $window diff --git a/tests/test-api.js b/tests/test-api.js index ec57a0f3..3240141c 100644 --- a/tests/test-api.js +++ b/tests/test-api.js @@ -10,7 +10,7 @@ o.spec("api", function() { o.beforeEach(function() { var mock = browserMock() if (typeof global !== "undefined") global.window = mock - m = require("../mithril") + m = require("../mithril") // eslint-disable-line global-require }) o.spec("m", function() { @@ -174,4 +174,4 @@ o.spec("api", function() { }) }) }) -}) \ No newline at end of file +}) From 23e4a571146b979eee66b3eb0e402b829f4fa7f8 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Fri, 3 Mar 2017 20:41:29 -0600 Subject: [PATCH 073/113] Put Stream in IIFE so variables aren't global ...when imported into the browser via +``` + +When loaded directly with a ` ``` -When loaded directly with a `