From 2f878813b18663d378bcec20c474b8fe6c36e35b Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Mon, 11 Jan 2016 09:33:46 +0100 Subject: [PATCH 01/13] Added a bunch of failing tests for thunks --- test/thunk.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/test/thunk.js b/test/thunk.js index ade371d..38ccd7a 100644 --- a/test/thunk.js +++ b/test/thunk.js @@ -63,4 +63,78 @@ describe('thunk', function() { assert.equal(elm.firstChild.innerHTML, 'Number is 2'); assert.equal(called, 2); }); + it('renders correctly when root', function() { + var called = 0; + function numberInSpan(n) { + called++; + return h('span', 'Number is ' + n); + } + var vnode1 = thunk('num', numberInSpan, 1); + var vnode2 = thunk('num', numberInSpan, 1); + var vnode3 = thunk('num', numberInSpan, 2); + + patch(vnode0, vnode1); + assert.equal(elm.innerHTML, 'Number is 1'); + + patch(vnode1, vnode2); + assert.equal(elm.innerHTML, 'Number is 1'); + + patch(vnode2, vnode3); + assert.equal(elm.innerHTML, 'Number is 2'); + assert.equal(called, 2); + }); + it('can mutate its root tag', function() { + function oddEven(n) { + var oddEvenSel = (n % 2) ? 'div.odd' : 'p.even'; + return h(oddEvenSel, n); + } + var vnode1 = h('div', [thunk('oddEven', oddEven, 1)]); + var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); + + patch(vnode0, vnode1); + assert.equal(elm.firstChild.tagName, 'DIV'); + assert.equal(elm.firstChild.className, 'odd'); + + patch(vnode1, vnode2); + assert.equal(elm.firstChild.tagName, 'P'); + assert.equal(elm.firstChild.className, 'even'); + }); + it('can be replaced and removed', function() { + function numberInSpan(n) { + return h('span.numberInSpan', 'Number is ' + n); + } + function oddEven(n) { + var oddEvenClass = (n % 2) ? '.odd' : '.even'; + return h('div' + oddEvenClass, 'Number is ' + n); + } + var vnode1 = h('div', [thunk('num', numberInSpan, 1)]); + var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); + + patch(vnode0, vnode1); + assert.equal(elm.firstChild.tagName, 'SPAN'); + assert.equal(elm.firstChild.className, 'numberInSpan'); + + patch(vnode1, vnode2); + assert.equal(elm.firstChild.tagName, 'DIV'); + assert.equal(elm.firstChild.className, 'even'); + }); + it('can be replaced and removed when root', function() { + function numberInSpan(n) { + return h('span.numberInSpan', 'Number is ' + n); + } + function oddEven(n) { + var oddEvenClass = (n % 2) ? '.odd' : '.even'; + return h('div' + oddEvenClass, 'Number is ' + n); + } + var vnode1 = thunk('num', numberInSpan, 1); + var vnode2 = thunk('oddEven', oddEven, 4); + + patch(vnode0, vnode1); + assert.equal(elm.firstChild.tagName, 'SPAN'); + assert.equal(elm.firstChild.className, 'numberInSpan'); + + patch(vnode1, vnode2); + assert.equal(elm.firstChild.tagName, 'DIV'); + assert.equal(elm.firstChild.className, 'even'); + }); }); From d92dd1f0ce5b2e378829d238a2ce696ad682a3f6 Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Mon, 11 Jan 2016 19:14:33 +0100 Subject: [PATCH 02/13] Removed forgotten console.log calls --- test/attachto.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/attachto.js b/test/attachto.js index 7acfe7e..fa1bd24 100644 --- a/test/attachto.js +++ b/test/attachto.js @@ -76,9 +76,6 @@ describe('attachTo', function() { }); it('remove hook recieves real element', function() { function rm(vnode, cb) { - console.log(vnode); - console.log(vnode.elm); - console.log(vnode.elm.parentElement); assert.equal(vnode.elm.tagName, 'DIV'); assert.equal(vnode.elm.innerHTML, 'First text'); cb(); From df41e1de2560ec3582db6c9ad7ff4c1caaf6e210 Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Mon, 11 Jan 2016 19:20:58 +0100 Subject: [PATCH 03/13] Passing test "renders correctly when root" --- snabbdom.js | 3 +-- thunk.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/snabbdom.js b/snabbdom.js index 641b0db..8010ffc 100644 --- a/snabbdom.js +++ b/snabbdom.js @@ -213,8 +213,7 @@ function init(modules) { for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i](); if (oldVnode instanceof Element) { if (oldVnode.parentElement !== null) { - createElm(vnode, insertedVnodeQueue); - oldVnode.parentElement.replaceChild(vnode.elm, oldVnode); + oldVnode.parentElement.replaceChild(createElm(vnode, insertedVnodeQueue), oldVnode); } else { oldVnode = emptyNodeAt(oldVnode); patchVnode(oldVnode, vnode, insertedVnodeQueue); diff --git a/thunk.js b/thunk.js index 5372782..4e8c3de 100644 --- a/thunk.js +++ b/thunk.js @@ -9,7 +9,7 @@ function prepatch(oldThunk, thunk) { var i, old = oldThunk.data, cur = thunk.data; var oldArgs = old.args, args = cur.args; cur.vnode = old.vnode; - if (oldArgs.length !== args.length) { + if (old.fn !== cur.fn || oldArgs.length !== args.length) { cur.vnode = cur.fn.apply(undefined, args); return; } From 94a3f8767b1b2be89836dba77f546a5bd673be70 Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Mon, 11 Jan 2016 21:53:25 +0100 Subject: [PATCH 04/13] Passing test "can be replaced and removed" for thunk --- snabbdom.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/snabbdom.js b/snabbdom.js index 8010ffc..d835791 100644 --- a/snabbdom.js +++ b/snabbdom.js @@ -27,9 +27,13 @@ function createKeyToOldIdx(children, beginIdx, endIdx) { return map; } -function createRmCb(childElm, listeners) { +function createRmCb(child, listeners) { + var elm; + while (isUndef(elm = child.elm)) child = child.data.vnode; return function() { - if (--listeners === 0) childElm.parentElement.removeChild(childElm); + if (--listeners === 0) { + elm.parentElement.removeChild(elm); + } }; } @@ -88,15 +92,16 @@ function init(modules) { } function invokeDestroyHook(vnode) { - var i = vnode.data, j; - if (isDef(i)) { - if (isDef(i = i.hook) && isDef(i = i.destroy)) i(vnode); + var i, j, data = vnode.data; + if (isDef(data)) { + if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode); for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode); if (isDef(i = vnode.children)) { for (j = 0; j < vnode.children.length; ++j) { invokeDestroyHook(vnode.children[j]); } } + if (isDef(i = data.vnode)) invokeDestroyHook(i); } } @@ -107,7 +112,7 @@ function init(modules) { if (isDef(ch.sel)) { invokeDestroyHook(ch); listeners = cbs.remove.length + 1; - rm = createRmCb(ch.elm, listeners); + rm = createRmCb(ch, listeners); for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm); if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) { i(ch, rm); From 645303e9865f2635ed51cabb09082d2d65cb4920 Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Mon, 11 Jan 2016 22:15:53 +0100 Subject: [PATCH 05/13] Improved/fixed some tests --- test/thunk.js | 54 +++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/test/thunk.js b/test/thunk.js index 38ccd7a..added9e 100644 --- a/test/thunk.js +++ b/test/thunk.js @@ -7,11 +7,15 @@ var h = require('../h'); var thunk = require('../thunk'); describe('thunk', function() { - var elm, vnode0; + var parent, vnode0; beforeEach(function() { - elm = document.createElement('div'); - vnode0 = elm; + parent = document.createElement('div'); + vnode0 = document.createElement('div'); + parent.appendChild(vnode0); }); + function elm() { + return parent.firstChild; + } it('returns vnode with data and render function', function() { function numberInSpan(n) { return h('span', 'Number is ' + n); @@ -56,11 +60,14 @@ describe('thunk', function() { thunk('num', numberInSpan, 2) ]); patch(vnode0, vnode1); - assert.equal(elm.firstChild.innerHTML, 'Number is 1'); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm().firstChild.innerHTML, 'Number is 1'); patch(vnode1, vnode2); - assert.equal(elm.firstChild.innerHTML, 'Number is 1'); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm().firstChild.innerHTML, 'Number is 1'); patch(vnode2, vnode3); - assert.equal(elm.firstChild.innerHTML, 'Number is 2'); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm().firstChild.innerHTML, 'Number is 2'); assert.equal(called, 2); }); it('renders correctly when root', function() { @@ -74,13 +81,16 @@ describe('thunk', function() { var vnode3 = thunk('num', numberInSpan, 2); patch(vnode0, vnode1); - assert.equal(elm.innerHTML, 'Number is 1'); + assert.equal(elm().tagName.toLowerCase(), 'span'); + assert.equal(elm().innerHTML, 'Number is 1'); patch(vnode1, vnode2); - assert.equal(elm.innerHTML, 'Number is 1'); + assert.equal(elm().tagName.toLowerCase(), 'span'); + assert.equal(elm().innerHTML, 'Number is 1'); patch(vnode2, vnode3); - assert.equal(elm.innerHTML, 'Number is 2'); + assert.equal(elm().tagName.toLowerCase(), 'span'); + assert.equal(elm().innerHTML, 'Number is 2'); assert.equal(called, 2); }); it('can mutate its root tag', function() { @@ -91,13 +101,11 @@ describe('thunk', function() { var vnode1 = h('div', [thunk('oddEven', oddEven, 1)]); var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); - patch(vnode0, vnode1); - assert.equal(elm.firstChild.tagName, 'DIV'); - assert.equal(elm.firstChild.className, 'odd'); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'div'); + assert.equal(elm().firstChild.className, 'odd'); - patch(vnode1, vnode2); - assert.equal(elm.firstChild.tagName, 'P'); - assert.equal(elm.firstChild.className, 'even'); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'p'); + assert.equal(elm().firstChild.className, 'even'); }); it('can be replaced and removed', function() { function numberInSpan(n) { @@ -111,12 +119,12 @@ describe('thunk', function() { var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); patch(vnode0, vnode1); - assert.equal(elm.firstChild.tagName, 'SPAN'); - assert.equal(elm.firstChild.className, 'numberInSpan'); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm().firstChild.className, 'numberInSpan'); patch(vnode1, vnode2); - assert.equal(elm.firstChild.tagName, 'DIV'); - assert.equal(elm.firstChild.className, 'even'); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'div'); + assert.equal(elm().firstChild.className, 'even'); }); it('can be replaced and removed when root', function() { function numberInSpan(n) { @@ -130,11 +138,11 @@ describe('thunk', function() { var vnode2 = thunk('oddEven', oddEven, 4); patch(vnode0, vnode1); - assert.equal(elm.firstChild.tagName, 'SPAN'); - assert.equal(elm.firstChild.className, 'numberInSpan'); + assert.equal(elm().tagName.toLowerCase(), 'span'); + assert.equal(elm().className, 'numberInSpan'); patch(vnode1, vnode2); - assert.equal(elm.firstChild.tagName, 'DIV'); - assert.equal(elm.firstChild.className, 'even'); + assert.equal(elm().tagName.toLowerCase(), 'div'); + assert.equal(elm().className, 'even'); }); }); From 3305f8f2533991fa48c8ae7270c3479b0c6a361b Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Mon, 11 Jan 2016 22:30:00 +0100 Subject: [PATCH 06/13] Fixed tests again --- test/thunk.js | 98 ++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/test/thunk.js b/test/thunk.js index added9e..d895119 100644 --- a/test/thunk.js +++ b/test/thunk.js @@ -71,27 +71,27 @@ describe('thunk', function() { assert.equal(called, 2); }); it('renders correctly when root', function() { - var called = 0; - function numberInSpan(n) { - called++; - return h('span', 'Number is ' + n); - } - var vnode1 = thunk('num', numberInSpan, 1); - var vnode2 = thunk('num', numberInSpan, 1); - var vnode3 = thunk('num', numberInSpan, 2); + var called = 0; + function numberInSpan(n) { + called++; + return h('span', 'Number is ' + n); + } + var vnode1 = thunk('num', numberInSpan, 1); + var vnode2 = thunk('num', numberInSpan, 1); + var vnode3 = thunk('num', numberInSpan, 2); - patch(vnode0, vnode1); - assert.equal(elm().tagName.toLowerCase(), 'span'); - assert.equal(elm().innerHTML, 'Number is 1'); + patch(vnode0, vnode1); + assert.equal(elm().tagName.toLowerCase(), 'span'); + assert.equal(elm().innerHTML, 'Number is 1'); - patch(vnode1, vnode2); - assert.equal(elm().tagName.toLowerCase(), 'span'); - assert.equal(elm().innerHTML, 'Number is 1'); + patch(vnode1, vnode2); + assert.equal(elm().tagName.toLowerCase(), 'span'); + assert.equal(elm().innerHTML, 'Number is 1'); - patch(vnode2, vnode3); - assert.equal(elm().tagName.toLowerCase(), 'span'); - assert.equal(elm().innerHTML, 'Number is 2'); - assert.equal(called, 2); + patch(vnode2, vnode3); + assert.equal(elm().tagName.toLowerCase(), 'span'); + assert.equal(elm().innerHTML, 'Number is 2'); + assert.equal(called, 2); }); it('can mutate its root tag', function() { function oddEven(n) { @@ -101,48 +101,50 @@ describe('thunk', function() { var vnode1 = h('div', [thunk('oddEven', oddEven, 1)]); var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); + patch(vnode0, vnode1); assert.equal(elm().firstChild.tagName.toLowerCase(), 'div'); assert.equal(elm().firstChild.className, 'odd'); + patch(vnode1, vnode2); assert.equal(elm().firstChild.tagName.toLowerCase(), 'p'); assert.equal(elm().firstChild.className, 'even'); }); it('can be replaced and removed', function() { - function numberInSpan(n) { - return h('span.numberInSpan', 'Number is ' + n); - } - function oddEven(n) { - var oddEvenClass = (n % 2) ? '.odd' : '.even'; - return h('div' + oddEvenClass, 'Number is ' + n); - } - var vnode1 = h('div', [thunk('num', numberInSpan, 1)]); - var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); + function numberInSpan(n) { + return h('span.numberInSpan', 'Number is ' + n); + } + function oddEven(n) { + var oddEvenClass = (n % 2) ? '.odd' : '.even'; + return h('div' + oddEvenClass, 'Number is ' + n); + } + var vnode1 = h('div', [thunk('num', numberInSpan, 1)]); + var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); - patch(vnode0, vnode1); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); - assert.equal(elm().firstChild.className, 'numberInSpan'); + patch(vnode0, vnode1); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm().firstChild.className, 'numberInSpan'); - patch(vnode1, vnode2); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'div'); - assert.equal(elm().firstChild.className, 'even'); + patch(vnode1, vnode2); + assert.equal(elm().firstChild.tagName.toLowerCase(), 'div'); + assert.equal(elm().firstChild.className, 'even'); }); it('can be replaced and removed when root', function() { - function numberInSpan(n) { - return h('span.numberInSpan', 'Number is ' + n); - } - function oddEven(n) { - var oddEvenClass = (n % 2) ? '.odd' : '.even'; - return h('div' + oddEvenClass, 'Number is ' + n); - } - var vnode1 = thunk('num', numberInSpan, 1); - var vnode2 = thunk('oddEven', oddEven, 4); + function numberInSpan(n) { + return h('span.numberInSpan', 'Number is ' + n); + } + function oddEven(n) { + var oddEvenClass = (n % 2) ? '.odd' : '.even'; + return h('div' + oddEvenClass, 'Number is ' + n); + } + var vnode1 = thunk('num', numberInSpan, 1); + var vnode2 = thunk('oddEven', oddEven, 4); - patch(vnode0, vnode1); - assert.equal(elm().tagName.toLowerCase(), 'span'); - assert.equal(elm().className, 'numberInSpan'); + patch(vnode0, vnode1); + assert.equal(elm().tagName.toLowerCase(), 'span'); + assert.equal(elm().className, 'numberInSpan'); - patch(vnode1, vnode2); - assert.equal(elm().tagName.toLowerCase(), 'div'); - assert.equal(elm().className, 'even'); + patch(vnode1, vnode2); + assert.equal(elm().tagName.toLowerCase(), 'div'); + assert.equal(elm().className, 'even'); }); }); From 85f7e9a93f09e96a51099c5339057f06e3c8c13d Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Mon, 11 Jan 2016 23:23:17 +0100 Subject: [PATCH 07/13] Only one level deep for thunks --- snabbdom.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/snabbdom.js b/snabbdom.js index d835791..87d18b3 100644 --- a/snabbdom.js +++ b/snabbdom.js @@ -27,9 +27,10 @@ function createKeyToOldIdx(children, beginIdx, endIdx) { return map; } -function createRmCb(child, listeners) { - var elm; - while (isUndef(elm = child.elm)) child = child.data.vnode; +function createRmCb(vnode, listeners) { + var i, elm; + if (isDef(i = vnode.data) && isDef(i = i.vnode)) vnode = i; + elm = vnode.elm; return function() { if (--listeners === 0) { elm.parentElement.removeChild(elm); From 7ca1d1378321e007319c022526b1343e86dfbb54 Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Tue, 12 Jan 2016 20:29:56 +0100 Subject: [PATCH 08/13] Ignore test/browserified.js --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 7937937..708cc1b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ node_modules # Vim *.swp + +test/browserified.js \ No newline at end of file From 2341c26e26a93d2772d09f13fb06a7649417198c Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Tue, 12 Jan 2016 20:36:56 +0100 Subject: [PATCH 09/13] Don't assume the root node won't be replaced --- test/attachto.js | 29 ++++----- test/core.js | 138 ++++++++++++++++++++--------------------- test/eventlisteners.js | 20 +++--- test/style.js | 12 ++-- test/thunk.js | 81 ++++++++++++------------ 5 files changed, 138 insertions(+), 142 deletions(-) diff --git a/test/attachto.js b/test/attachto.js index fa1bd24..db87e88 100644 --- a/test/attachto.js +++ b/test/attachto.js @@ -12,12 +12,13 @@ describe('attachTo', function() { vnode0 = elm; }); it('adds element to target', function() { - patch(vnode0, h('div', [ - h('div#wrapper', [ - h('div', 'Some element'), - attachTo(elm, h('div#attached', 'Test')), - ]), - ])); + var vnode1 = h('div', [ + h('div#wrapper', [ + h('div', 'Some element'), + attachTo(elm, h('div#attached', 'Test')), + ]), + ]); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 2); }); it('updates element at target', function() { @@ -33,9 +34,9 @@ describe('attachTo', function() { attachTo(elm, h('div#attached', 'New text')), ]), ]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children[0].innerHTML, 'First text'); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children[0].innerHTML, 'New text'); }); it('element can be inserted before modal', function() { @@ -52,9 +53,9 @@ describe('attachTo', function() { attachTo(elm, h('div#attached', 'Text')), ]), ]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children[0].innerHTML, 'Text'); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children[0].innerHTML, 'Text'); }); it('removes element at target', function() { @@ -69,9 +70,9 @@ describe('attachTo', function() { h('div', 'Some element'), ]), ]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children[0].innerHTML, 'First text'); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 1); }); it('remove hook recieves real element', function() { @@ -91,7 +92,7 @@ describe('attachTo', function() { h('div', 'Some element'), ]), ]); - patch(vnode0, vnode1); - patch(vnode1, vnode2); + elm = patch(vnode0, vnode1).elm; + elm = patch(vnode1, vnode2).elm; }); }); diff --git a/test/core.js b/test/core.js index 569b964..ab08e3f 100644 --- a/test/core.js +++ b/test/core.js @@ -57,49 +57,49 @@ describe('snabbdom', function() { }); describe('created element', function() { it('has tag', function() { - patch(vnode0, h('div')); + elm = patch(vnode0, h('div')).elm; assert.equal(elm.tagName, 'DIV'); }); it('has different tag and id', function() { var elm = document.createElement('div'); vnode0.appendChild(elm); var vnode1 = h('span#id'); - patch(elm, vnode1); - assert.equal(vnode1.elm.tagName, 'SPAN'); - assert.equal(vnode1.elm.id, 'id'); + elm = patch(elm, vnode1).elm; + assert.equal(elm.tagName, 'SPAN'); + assert.equal(elm.id, 'id'); }); it('has id', function() { - patch(vnode0, h('div', [h('div#unique')])); + elm = patch(vnode0, h('div', [h('div#unique')])).elm; assert.equal(elm.firstChild.id, 'unique'); }); it('has correct namespace', function() { - patch(vnode0, h('div', [h('div', {ns: 'http://www.w3.org/2000/svg'})])); + elm = patch(vnode0, h('div', [h('div', {ns: 'http://www.w3.org/2000/svg'})])).elm; assert.equal(elm.firstChild.namespaceURI, 'http://www.w3.org/2000/svg'); }); it('is recieves classes in selector', function() { - patch(vnode0, h('div', [h('i.am.a.class')])); + elm = patch(vnode0, h('div', [h('i.am.a.class')])).elm; assert(elm.firstChild.classList.contains('am')); assert(elm.firstChild.classList.contains('a')); assert(elm.firstChild.classList.contains('class')); }); it('is recieves classes in class property', function() { - patch(vnode0, h('i', {class: {am: true, a: true, class: true, not: false}})); + elm = patch(vnode0, h('i', {class: {am: true, a: true, class: true, not: false}})).elm; assert(elm.classList.contains('am')); assert(elm.classList.contains('a')); assert(elm.classList.contains('class')); assert(!elm.classList.contains('not')); }); it('handles classes from both selector and property', function() { - patch(vnode0, h('div', [h('i.has', {class: {classes: true}})])); + elm = patch(vnode0, h('div', [h('i.has', {class: {classes: true}})])).elm; assert(elm.firstChild.classList.contains('has')); assert(elm.firstChild.classList.contains('classes')); }); it('can create elements with text content', function() { - patch(vnode0, h('div', ['I am a string'])); + elm = patch(vnode0, h('div', ['I am a string'])).elm; assert.equal(elm.innerHTML, 'I am a string'); }); it('can create elements with span and text content', function() { - patch(vnode0, h('a', [h('span'), 'I am a string'])); + elm = patch(vnode0, h('a', [h('span'), 'I am a string'])).elm; assert.equal(elm.childNodes[0].tagName, 'SPAN'); assert.equal(elm.childNodes[1].textContent, 'I am a string'); }); @@ -109,7 +109,7 @@ describe('snabbdom', function() { var vnode1 = h('i', {class: {i: true, am: true, horse: true}}); var vnode2 = h('i', {class: {i: true, am: true, horse: false}}); patch(vnode0, vnode1); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert(elm.classList.contains('i')); assert(elm.classList.contains('am')); assert(!elm.classList.contains('horse')); @@ -118,7 +118,7 @@ describe('snabbdom', function() { var vnode1 = h('i', {class: {i: true, am: true, horse: true}}); var vnode2 = h('i', {class: {i: true, am: true, horse: false}}); patch(vnode0, vnode1); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert(elm.classList.contains('i')); assert(elm.classList.contains('am')); assert(!elm.classList.contains('horse')); @@ -135,9 +135,9 @@ describe('snabbdom', function() { it('appends elements', function() { var vnode1 = h('span', [1].map(spanNum)); var vnode2 = h('span', [1, 2, 3].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 1); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 3); assert.equal(elm.children[1].innerHTML, '2'); assert.equal(elm.children[2].innerHTML, '3'); @@ -145,42 +145,42 @@ describe('snabbdom', function() { it('prepends elements', function() { var vnode1 = h('span', [4, 5].map(spanNum)); var vnode2 = h('span', [1, 2, 3, 4, 5].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 2); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['1', '2', '3', '4', '5']); }); it('add elements in the middle', function() { var vnode1 = h('span', [1, 2, 4, 5].map(spanNum)); var vnode2 = h('span', [1, 2, 3, 4, 5].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 4); assert.equal(elm.children.length, 4); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['1', '2', '3', '4', '5']); }); it('add elements at begin and end', function() { var vnode1 = h('span', [2, 3, 4].map(spanNum)); var vnode2 = h('span', [1, 2, 3, 4, 5].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 3); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['1', '2', '3', '4', '5']); }); it('adds children to parent with no children', function() { var vnode1 = h('span', {key: 'span'}); var vnode2 = h('span', {key: 'span'}, [1, 2, 3].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 0); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['1', '2', '3']); }); it('removes all children from parent', function() { var vnode1 = h('span', {key: 'span'}, [1, 2, 3].map(spanNum)); var vnode2 = h('span', {key: 'span'}); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.deepEqual(map(inner, elm.children), ['1', '2', '3']); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 0); }); }); @@ -188,17 +188,17 @@ describe('snabbdom', function() { it('removes elements from the beginning', function() { var vnode1 = h('span', [1, 2, 3, 4, 5].map(spanNum)); var vnode2 = h('span', [3, 4, 5].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 5); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['3', '4', '5']); }); it('removes elements from the end', function() { var vnode1 = h('span', [1, 2, 3, 4, 5].map(spanNum)); var vnode2 = h('span', [1, 2, 3].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 5); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 3); assert.equal(elm.children[0].innerHTML, '1'); assert.equal(elm.children[1].innerHTML, '2'); @@ -207,9 +207,9 @@ describe('snabbdom', function() { it('removes elements from the middle', function() { var vnode1 = h('span', [1, 2, 3, 4, 5].map(spanNum)); var vnode2 = h('span', [1, 2, 4, 5].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 5); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 4); assert.deepEqual(elm.children[0].innerHTML, '1'); assert.equal(elm.children[0].innerHTML, '1'); @@ -222,9 +222,9 @@ describe('snabbdom', function() { it('moves element forward', function() { var vnode1 = h('span', [1, 2, 3, 4].map(spanNum)); var vnode2 = h('span', [2, 3, 1, 4].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 4); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 4); assert.equal(elm.children[0].innerHTML, '2'); assert.equal(elm.children[1].innerHTML, '3'); @@ -234,9 +234,9 @@ describe('snabbdom', function() { it('moves element to end', function() { var vnode1 = h('span', [1, 2, 3].map(spanNum)); var vnode2 = h('span', [2, 3, 1].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 3); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 3); assert.equal(elm.children[0].innerHTML, '2'); assert.equal(elm.children[1].innerHTML, '3'); @@ -245,9 +245,9 @@ describe('snabbdom', function() { it('moves element backwards', function() { var vnode1 = h('span', [1, 2, 3, 4].map(spanNum)); var vnode2 = h('span', [1, 4, 2, 3].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 4); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 4); assert.equal(elm.children[0].innerHTML, '1'); assert.equal(elm.children[1].innerHTML, '4'); @@ -257,9 +257,9 @@ describe('snabbdom', function() { it('swaps first and last', function() { var vnode1 = h('span', [1, 2, 3, 4].map(spanNum)); var vnode2 = h('span', [4, 2, 3, 1].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 4); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 4); assert.equal(elm.children[0].innerHTML, '4'); assert.equal(elm.children[1].innerHTML, '2'); @@ -271,9 +271,9 @@ describe('snabbdom', function() { it('move to left and replace', function() { var vnode1 = h('span', [1, 2, 3, 4, 5].map(spanNum)); var vnode2 = h('span', [4, 1, 2, 3, 6].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 5); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 5); assert.equal(elm.children[0].innerHTML, '4'); assert.equal(elm.children[1].innerHTML, '1'); @@ -284,17 +284,17 @@ describe('snabbdom', function() { it('moves to left and leaves hole', function() { var vnode1 = h('span', [1, 4, 5].map(spanNum)); var vnode2 = h('span', [4, 6].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 3); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['4', '6']); }); it('handles moved and set to undefined element ending at the end', function() { var vnode1 = h('span', [2, 4, 5].map(spanNum)); var vnode2 = h('span', [4, 5, 3].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 3); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.children.length, 3); assert.equal(elm.children[0].innerHTML, '4'); assert.equal(elm.children[1].innerHTML, '5'); @@ -303,10 +303,10 @@ describe('snabbdom', function() { it('moves a key in non-keyed nodes with a size up', function() { var vnode1 = h('span', [1, 'a', 'b', 'c'].map(spanNum)); var vnode2 = h('span', ['d', 'a', 'b', 'c', 1, 'e'].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.childNodes.length, 4); assert.equal(elm.textContent, '1abc'); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.childNodes.length, 6); assert.equal(elm.textContent, 'dabc1e'); }); @@ -314,17 +314,17 @@ describe('snabbdom', function() { it('reverses elements', function() { var vnode1 = h('span', [1, 2, 3, 4, 5, 6, 7, 8].map(spanNum)); var vnode2 = h('span', [8, 7, 6, 5, 4, 3, 2, 1].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 8); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['8', '7', '6', '5', '4', '3', '2', '1']); }); it('something', function() { var vnode1 = h('span', [0, 1, 2, 3, 4, 5].map(spanNum)); var vnode2 = h('span', [4, 3, 2, 1, 5, 0].map(spanNum)); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 6); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['4', '3', '2', '1', '5', '0']); }); it('handles random shuffles', function() { @@ -339,7 +339,7 @@ describe('snabbdom', function() { })); var shufArr = shuffle(arr.slice(0)); var elm = document.createElement('div'); - patch(elm, vnode1); + elm = patch(elm, vnode1).elm; for (i = 0; i < elms; ++i) { assert.equal(elm.children[i].innerHTML, i.toString()); opacities[i] = Math.random().toFixed(5).toString(); @@ -347,7 +347,7 @@ describe('snabbdom', function() { var vnode2 = h('span', arr.map(function(n) { return spanNumWithOpacity(shufArr[n], opacities[n]); })); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; for (i = 0; i < elms; ++i) { assert.equal(elm.children[i].innerHTML, shufArr[i].toString()); assert.equal(opacities[i].indexOf(elm.children[i].style.opacity), 0); @@ -359,58 +359,58 @@ describe('snabbdom', function() { it('appends elements', function() { var vnode1 = h('div', [h('span', 'Hello')]); var vnode2 = h('div', [h('span', 'Hello'), h('span', 'World')]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.deepEqual(map(inner, elm.children), ['Hello']); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['Hello', 'World']); }); it('handles unmoved text nodes', function() { var vnode1 = h('div', ['Text', h('span', 'Span')]); var vnode2 = h('div', ['Text', h('span', 'Span')]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.childNodes[0].textContent, 'Text'); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.childNodes[0].textContent, 'Text'); }); it('handles changing text children', function() { var vnode1 = h('div', ['Text', h('span', 'Span')]); var vnode2 = h('div', ['Text2', h('span', 'Span')]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.childNodes[0].textContent, 'Text'); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.childNodes[0].textContent, 'Text2'); }); it('prepends element', function() { var vnode1 = h('div', [h('span', 'World')]); var vnode2 = h('div', [h('span', 'Hello'), h('span', 'World')]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.deepEqual(map(inner, elm.children), ['World']); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['Hello', 'World']); }); it('prepends element of different tag type', function() { var vnode1 = h('div', [h('span', 'World')]); var vnode2 = h('div', [h('div', 'Hello'), h('span', 'World')]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.deepEqual(map(inner, elm.children), ['World']); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(prop('tagName'), elm.children), ['DIV', 'SPAN']); assert.deepEqual(map(inner, elm.children), ['Hello', 'World']); }); it('removes elements', function() { var vnode1 = h('div', [h('span', 'One'), h('span', 'Two'), h('span', 'Three')]); var vnode2 = h('div', [h('span', 'One'), h('span', 'Three')]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.deepEqual(map(inner, elm.children), ['One', 'Two', 'Three']); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(inner, elm.children), ['One', 'Three']); }); it('reorders elements', function() { var vnode1 = h('div', [h('span', 'One'), h('div', 'Two'), h('b', 'Three')]); var vnode2 = h('div', [h('b', 'Three'), h('span', 'One'), h('div', 'Two')]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.deepEqual(map(inner, elm.children), ['One', 'Two', 'Three']); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.deepEqual(map(prop('tagName'), elm.children), ['B', 'SPAN', 'DIV']); assert.deepEqual(map(inner, elm.children), ['Three', 'One', 'Two']); }); @@ -571,9 +571,9 @@ describe('snabbdom', function() { {remove: function(_, rm) { rm2 = rm; }}, ]); var vnode1 = h('div', [h('a', {hook: {remove: function(_, rm) { rm3 = rm; }}})]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.children.length, 1); - patch(vnode1, vnode0); + elm = patch(vnode1, vnode0).elm; assert.equal(elm.children.length, 1); rm1(); assert.equal(elm.children.length, 1); diff --git a/test/eventlisteners.js b/test/eventlisteners.js index 04b45ed..1e66b45 100644 --- a/test/eventlisteners.js +++ b/test/eventlisteners.js @@ -18,7 +18,7 @@ describe('event listeners', function() { var vnode = h('div', {on: {click: clicked}}, [ h('a', 'Click my parent'), ]); - patch(vnode0, vnode); + elm = patch(vnode0, vnode).elm; elm.click(); assert.equal(1, result.length); }); @@ -31,9 +31,9 @@ describe('event listeners', function() { var vnode2 = h('div', {on: {click: function(ev) { result.push(2); }}}, [ h('a', 'Click my parent'), ]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; elm.click(); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; elm.click(); assert.deepEqual(result, [1, 2]); }); @@ -43,7 +43,7 @@ describe('event listeners', function() { var vnode = h('div', {on: {click: [clicked, 1]}}, [ h('a', 'Click my parent'), ]); - patch(vnode0, vnode); + elm = patch(vnode0, vnode).elm; elm.click(); assert.deepEqual(result, [1]); }); @@ -59,11 +59,11 @@ describe('event listeners', function() { var vnode3 = h('div', {on: {click: [clicked, 3]}}, [ h('a', 'Click my parent'), ]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; elm.click(); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; elm.click(); - patch(vnode2, vnode3); + elm = patch(vnode2, vnode3).elm; elm.click(); assert.deepEqual(result, [1, 2, 3]); }); @@ -79,11 +79,11 @@ describe('event listeners', function() { var vnode3 = h('div', {on: {click: [clicked, 2, 3]}}, [ h('a', 'Click my parent'), ]); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; elm.click(); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; elm.click(); - patch(vnode2, vnode3); + elm = patch(vnode2, vnode3).elm; elm.click(); assert.deepEqual(result, [[1, 2, 3], [1, 2], [2, 3]]); }); diff --git a/test/style.js b/test/style.js index 2196b9f..2443a9e 100644 --- a/test/style.js +++ b/test/style.js @@ -15,20 +15,20 @@ describe('style', function() { vnode0 = elm; }); it('is being styled', function() { - patch(vnode0, h('div', {style: {fontSize: '12px'}})); + elm = patch(vnode0, h('div', {style: {fontSize: '12px'}})).elm; assert.equal(elm.style.fontSize, '12px'); }); it('updates styles', function() { var vnode1 = h('i', {style: {fontSize: '14px', display: 'inline'}}); var vnode2 = h('i', {style: {fontSize: '12px', display: 'block'}}); var vnode3 = h('i', {style: {fontSize: '10px', display: 'block'}}); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.style.fontSize, '14px'); assert.equal(elm.style.display, 'inline'); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.style.fontSize, '12px'); assert.equal(elm.style.display, 'block'); - patch(vnode2, vnode3); + elm = patch(vnode2, vnode3).elm; assert.equal(elm.style.fontSize, '10px'); assert.equal(elm.style.display, 'block'); }); @@ -38,12 +38,12 @@ describe('style', function() { ]); var vnode1 = h('i', {style: {fontSize: '14px', delayed: {fontSize: '16px'}}}); var vnode2 = h('i', {style: {fontSize: '18px', delayed: {fontSize: '20px'}}}); - patch(vnode0, vnode1); + elm = patch(vnode0, vnode1).elm; assert.equal(elm.style.fontSize, '14px'); fakeRaf.step(); fakeRaf.step(); assert.equal(elm.style.fontSize, '16px'); - patch(vnode1, vnode2); + elm = patch(vnode1, vnode2).elm; assert.equal(elm.style.fontSize, '18px'); fakeRaf.step(); fakeRaf.step(); diff --git a/test/thunk.js b/test/thunk.js index d895119..e5f24ce 100644 --- a/test/thunk.js +++ b/test/thunk.js @@ -7,15 +7,10 @@ var h = require('../h'); var thunk = require('../thunk'); describe('thunk', function() { - var parent, vnode0; + var elm, vnode0; beforeEach(function() { - parent = document.createElement('div'); - vnode0 = document.createElement('div'); - parent.appendChild(vnode0); + elm = vnode0 = document.createElement('div'); }); - function elm() { - return parent.firstChild; - } it('returns vnode with data and render function', function() { function numberInSpan(n) { return h('span', 'Number is ' + n); @@ -59,15 +54,15 @@ describe('thunk', function() { var vnode3 = h('div', [ thunk('num', numberInSpan, 2) ]); - patch(vnode0, vnode1); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); - assert.equal(elm().firstChild.innerHTML, 'Number is 1'); - patch(vnode1, vnode2); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); - assert.equal(elm().firstChild.innerHTML, 'Number is 1'); - patch(vnode2, vnode3); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); - assert.equal(elm().firstChild.innerHTML, 'Number is 2'); + elm = patch(vnode0, vnode1).elm; + assert.equal(elm.firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm.firstChild.innerHTML, 'Number is 1'); + elm = patch(vnode1, vnode2).elm; + assert.equal(elm.firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm.firstChild.innerHTML, 'Number is 1'); + elm = patch(vnode2, vnode3).elm; + assert.equal(elm.firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm.firstChild.innerHTML, 'Number is 2'); assert.equal(called, 2); }); it('renders correctly when root', function() { @@ -80,17 +75,17 @@ describe('thunk', function() { var vnode2 = thunk('num', numberInSpan, 1); var vnode3 = thunk('num', numberInSpan, 2); - patch(vnode0, vnode1); - assert.equal(elm().tagName.toLowerCase(), 'span'); - assert.equal(elm().innerHTML, 'Number is 1'); + elm = patch(vnode0, vnode1).elm; + assert.equal(elm.tagName.toLowerCase(), 'span'); + assert.equal(elm.innerHTML, 'Number is 1'); - patch(vnode1, vnode2); - assert.equal(elm().tagName.toLowerCase(), 'span'); - assert.equal(elm().innerHTML, 'Number is 1'); + elm = patch(vnode1, vnode2).elm; + assert.equal(elm.tagName.toLowerCase(), 'span'); + assert.equal(elm.innerHTML, 'Number is 1'); - patch(vnode2, vnode3); - assert.equal(elm().tagName.toLowerCase(), 'span'); - assert.equal(elm().innerHTML, 'Number is 2'); + elm = patch(vnode2, vnode3).elm; + assert.equal(elm.tagName.toLowerCase(), 'span'); + assert.equal(elm.innerHTML, 'Number is 2'); assert.equal(called, 2); }); it('can mutate its root tag', function() { @@ -101,13 +96,13 @@ describe('thunk', function() { var vnode1 = h('div', [thunk('oddEven', oddEven, 1)]); var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); - patch(vnode0, vnode1); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'div'); - assert.equal(elm().firstChild.className, 'odd'); + elm = patch(vnode0, vnode1).elm; + assert.equal(elm.firstChild.tagName.toLowerCase(), 'div'); + assert.equal(elm.firstChild.className, 'odd'); - patch(vnode1, vnode2); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'p'); - assert.equal(elm().firstChild.className, 'even'); + elm = patch(vnode1, vnode2).elm; + assert.equal(elm.firstChild.tagName.toLowerCase(), 'p'); + assert.equal(elm.firstChild.className, 'even'); }); it('can be replaced and removed', function() { function numberInSpan(n) { @@ -120,13 +115,13 @@ describe('thunk', function() { var vnode1 = h('div', [thunk('num', numberInSpan, 1)]); var vnode2 = h('div', [thunk('oddEven', oddEven, 4)]); - patch(vnode0, vnode1); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'span'); - assert.equal(elm().firstChild.className, 'numberInSpan'); + elm = patch(vnode0, vnode1).elm; + assert.equal(elm.firstChild.tagName.toLowerCase(), 'span'); + assert.equal(elm.firstChild.className, 'numberInSpan'); - patch(vnode1, vnode2); - assert.equal(elm().firstChild.tagName.toLowerCase(), 'div'); - assert.equal(elm().firstChild.className, 'even'); + elm = patch(vnode1, vnode2).elm; + assert.equal(elm.firstChild.tagName.toLowerCase(), 'div'); + assert.equal(elm.firstChild.className, 'even'); }); it('can be replaced and removed when root', function() { function numberInSpan(n) { @@ -139,12 +134,12 @@ describe('thunk', function() { var vnode1 = thunk('num', numberInSpan, 1); var vnode2 = thunk('oddEven', oddEven, 4); - patch(vnode0, vnode1); - assert.equal(elm().tagName.toLowerCase(), 'span'); - assert.equal(elm().className, 'numberInSpan'); + elm = patch(vnode0, vnode1).elm; + assert.equal(elm.tagName.toLowerCase(), 'span'); + assert.equal(elm.className, 'numberInSpan'); - patch(vnode1, vnode2); - assert.equal(elm().tagName.toLowerCase(), 'div'); - assert.equal(elm().className, 'even'); + elm = patch(vnode1, vnode2).elm; + assert.equal(elm.tagName.toLowerCase(), 'div'); + assert.equal(elm.className, 'even'); }); }); From 752908e411afc1bfb54044c6ac5f96937294ca44 Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Tue, 12 Jan 2016 20:48:32 +0100 Subject: [PATCH 10/13] The tests pass! --- snabbdom.js | 60 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/snabbdom.js b/snabbdom.js index 87d18b3..f474ebb 100644 --- a/snabbdom.js +++ b/snabbdom.js @@ -8,8 +8,9 @@ var is = require('./is'); function isUndef(s) { return s === undefined; } function isDef(s) { return s !== undefined; } +// deal with case sensivity better than that function emptyNodeAt(elm) { - return VNode(elm.tagName, {}, [], undefined, elm); + return VNode(elm.tagName.toLowerCase(), {}, [], undefined, elm); } var emptyNode = VNode('', {}, [], undefined, undefined); @@ -27,14 +28,9 @@ function createKeyToOldIdx(children, beginIdx, endIdx) { return map; } -function createRmCb(vnode, listeners) { - var i, elm; - if (isDef(i = vnode.data) && isDef(i = i.vnode)) vnode = i; - elm = vnode.elm; +function createRmCb(childElm, listeners) { return function() { - if (--listeners === 0) { - elm.parentElement.removeChild(elm); - } + if (--listeners === 0) childElm.parentElement.removeChild(childElm); }; } @@ -50,10 +46,13 @@ function init(modules) { } function createElm(vnode, insertedVnodeQueue) { - var i, data = vnode.data; + var i, thunk, data = vnode.data; if (isDef(data)) { if (isDef(i = data.hook) && isDef(i = i.init)) i(vnode); - if (isDef(i = data.vnode)) vnode = i; + if (isDef(i = data.vnode)) { + thunk = vnode; + vnode = i; + } } var elm, children = vnode.children, sel = vnode.sel; if (isDef(sel)) { @@ -83,6 +82,7 @@ function init(modules) { } else { elm = vnode.elm = document.createTextNode(vnode.text); } + if (isDef(thunk)) thunk.elm = vnode.elm; return vnode.elm; } @@ -113,7 +113,7 @@ function init(modules) { if (isDef(ch.sel)) { invokeDestroyHook(ch); listeners = cbs.remove.length + 1; - rm = createRmCb(ch, listeners); + rm = createRmCb(ch.elm, listeners); for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm); if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) { i(ch, rm); @@ -189,9 +189,20 @@ function init(modules) { i(oldVnode, vnode); } if (isDef(i = oldVnode.data) && isDef(i = i.vnode)) oldVnode = i; - if (isDef(i = vnode.data) && isDef(i = i.vnode)) vnode = i; + if (isDef(i = vnode.data) && isDef(i = i.vnode)) { + patchVnode(oldVnode, i, insertedVnodeQueue); + vnode.elm = i.elm; + return + } var elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children; if (oldVnode === vnode) return; + if (!sameVnode(oldVnode, vnode)) { + var parentElm = oldVnode.elm.parentElement; + var elm = createElm(vnode, insertedVnodeQueue); + parentElm.insertBefore(elm, oldVnode.elm); + removeVnodes(parentElm, [oldVnode], 0, 0); + return; + } if (isDef(vnode.data)) { for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode); i = vnode.data.hook; @@ -213,20 +224,23 @@ function init(modules) { } } + var dummyParent = document.createElement('div'); + return function(oldVnode, vnode) { - var i; + var i, noParent; var insertedVnodeQueue = []; for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i](); - if (oldVnode instanceof Element) { - if (oldVnode.parentElement !== null) { - oldVnode.parentElement.replaceChild(createElm(vnode, insertedVnodeQueue), oldVnode); - } else { - oldVnode = emptyNodeAt(oldVnode); - patchVnode(oldVnode, vnode, insertedVnodeQueue); - } - } else { - patchVnode(oldVnode, vnode, insertedVnodeQueue); - } + + if (oldVnode instanceof Element) oldVnode = emptyNodeAt(oldVnode); + // not sure if it's a valid use case... + if (vnode instanceof Element) vnode = emptyNodeAt(vnode); + + if (noParent = (oldVnode.elm.parentElement === null)) dummyParent.appendChild(oldVnode.elm); + + patchVnode(oldVnode, vnode, insertedVnodeQueue); + + if (noParent) dummyParent.removeChild(vnode.elm); + for (i = 0; i < insertedVnodeQueue.length; ++i) { insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]); } From 29eb18108c972fc813c60814a2771036275507f8 Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Tue, 12 Jan 2016 22:52:07 +0100 Subject: [PATCH 11/13] Repaired broken root init --- snabbdom.js | 7 ++++++- test/core.js | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/snabbdom.js b/snabbdom.js index f474ebb..0a42656 100644 --- a/snabbdom.js +++ b/snabbdom.js @@ -237,7 +237,12 @@ function init(modules) { if (noParent = (oldVnode.elm.parentElement === null)) dummyParent.appendChild(oldVnode.elm); - patchVnode(oldVnode, vnode, insertedVnodeQueue); + if (sameVnode(oldVnode, vnode)) { + patchVnode(oldVnode, vnode, insertedVnodeQueue); + } else { + createElm(vnode, insertedVnodeQueue); + oldVnode.elm.parentElement.replaceChild(vnode.elm, oldVnode.elm); + } if (noParent) dummyParent.removeChild(vnode.elm); diff --git a/test/core.js b/test/core.js index ab08e3f..a6d5c58 100644 --- a/test/core.js +++ b/test/core.js @@ -564,6 +564,23 @@ describe('snabbdom', function() { patch(vnode1, vnode2); assert.equal(1, result.length); }); + it('calls `init` and `prepatch` listeners on root', function() { + var count = 0; + function init(vnode) { + assert.strictEqual(vnode, vnode2); + count += 1; + } + function prepatch(oldVnode, vnode) { + assert.strictEqual(vnode, vnode1); + count += 1; + } + var vnode1 = h('div', {hook: {init: init, prepatch: prepatch}}); + patch(vnode0, vnode1); + assert.equal(1, count); + var vnode2 = h('span', {hook: {init: init, prepatch: prepatch}}); + patch(vnode1, vnode2); + assert.equal(2, count); + }); it('removes element when all remove listeners are done', function() { var rm1, rm2, rm3; var patch = snabbdom.init([ From 2279d3cadedbb20494a58b783344c08d7fc74ba8 Mon Sep 17 00:00:00 2001 From: Sylvain Prat Date: Wed, 13 Jan 2016 00:08:03 +0100 Subject: [PATCH 12/13] More tests: nested thunks does not work yet --- test/thunk.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/thunk.js b/test/thunk.js index e5f24ce..8bb6850 100644 --- a/test/thunk.js +++ b/test/thunk.js @@ -65,6 +65,46 @@ describe('thunk', function() { assert.equal(elm.firstChild.innerHTML, 'Number is 2'); assert.equal(called, 2); }); + it('renders correctly child thunk', function() { + function oddEven(n) { + var oddEvenSel = (n % 2) ? 'span.odd' : 'span.even'; + return h(oddEvenSel, n); + } + function numberInSpan(n) { + return h('span.number', ['Number is ', thunk('oddeven', oddEven, n)]); + } + var vnode1 = thunk('num', numberInSpan, 1); + var vnode2 = thunk('num', numberInSpan, 2); + elm = patch(vnode0, vnode1).elm; + assert.equal(elm.tagName.toLowerCase(), 'span'); + assert.equal(elm.className, 'number'); + assert.equal(elm.childNodes[1].tagName.toLowerCase(), 'span'); + assert.equal(elm.childNodes[1].className, 'odd'); + elm = patch(vnode1, vnode2).elm; + assert.equal(elm.tagName.toLowerCase(), 'span'); + assert.equal(elm.className, 'number'); + assert.equal(elm.childNodes[1].tagName.toLowerCase(), 'span'); + assert.equal(elm.childNodes[1].className, 'even'); + }); + /* NOT WORKING YET + it('renders correctly nested thunk', function() { + function oddEven(n) { + var oddEvenSel = (n % 2) ? 'span.odd' : 'span.even'; + return h(oddEvenSel, n); + } + function nested(n) { + return thunk('oddeven', oddEven, n); + } + var vnode1 = thunk('num', nested, 1); + var vnode2 = thunk('num', nested, 2); + elm = patch(vnode0, vnode1).elm; + assert.equal(elm.tagName.toLowerCase(), 'span'); + assert.equal(elm.className, 'odd'); + elm = patch(vnode1, vnode2).elm; + assert.equal(elm.tagName.toLowerCase(), 'span'); + assert.equal(elm.className, 'even'); + }); + */ it('renders correctly when root', function() { var called = 0; function numberInSpan(n) { From b55d8676bab58bd04e1801d858e3c8b56a68ee87 Mon Sep 17 00:00:00 2001 From: kay999 Date: Wed, 24 Feb 2016 00:40:57 +0100 Subject: [PATCH 13/13] remove-hook is now called after changing root-node --- snabbdom.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/snabbdom.js b/snabbdom.js index 0a42656..e929b39 100644 --- a/snabbdom.js +++ b/snabbdom.js @@ -235,15 +235,25 @@ function init(modules) { // not sure if it's a valid use case... if (vnode instanceof Element) vnode = emptyNodeAt(vnode); - if (noParent = (oldVnode.elm.parentElement === null)) dummyParent.appendChild(oldVnode.elm); - + var elm = oldVnode.elm; + var parent = elm.parentElement; + if (noParent = (parent === null)) { + dummyParent.appendChild(elm); + parent = dummyParent; + } + if (sameVnode(oldVnode, vnode)) { patchVnode(oldVnode, vnode, insertedVnodeQueue); - } else { - createElm(vnode, insertedVnodeQueue); - oldVnode.elm.parentElement.replaceChild(vnode.elm, oldVnode.elm); } + else { + var next = elm.nextSibling; + createElm(vnode, insertedVnodeQueue); + if (next) parent.insertBefore(vnode.elm, next); + else parent.appendChild(vnode.elm); + removeVnodes(parent, [oldVnode], 0, 0); + } + if (noParent) dummyParent.removeChild(vnode.elm); for (i = 0; i < insertedVnodeQueue.length; ++i) {