From 960812308c6d9ac34fe9f9113d3ab6e371b93578 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 18 May 2016 17:18:52 -0700 Subject: [PATCH 01/15] Move public APIs into their own folder And start writing actual tests for them --- limiter.js => api/limiter.js | 0 mount.js => api/mount.js | 2 +- router.js => api/router.js | 4 +- api/tests/async.js | 12 ++++ api/tests/index.html | 31 ++++++++++ api/tests/test-limiter.js | 29 +++++++++ api/tests/test-mount.js | 114 +++++++++++++++++++++++++++++++++++ index.js | 4 +- test-utils/domMock.js | 2 +- 9 files changed, 192 insertions(+), 6 deletions(-) rename limiter.js => api/limiter.js (100%) rename mount.js => api/mount.js (87%) rename router.js => api/router.js (85%) create mode 100644 api/tests/async.js create mode 100644 api/tests/index.html create mode 100644 api/tests/test-limiter.js create mode 100644 api/tests/test-mount.js diff --git a/limiter.js b/api/limiter.js similarity index 100% rename from limiter.js rename to api/limiter.js diff --git a/mount.js b/api/mount.js similarity index 87% rename from mount.js rename to api/mount.js index cf633938..53010bd0 100644 --- a/mount.js +++ b/api/mount.js @@ -1,4 +1,4 @@ -var createRenderer = require("./render/render") +var createRenderer = require("../render/render") var limiter = require("./limiter"); module.exports = function($window, redraw) { diff --git a/router.js b/api/router.js similarity index 85% rename from router.js rename to api/router.js index 5a3f5f62..eabd0e4c 100644 --- a/router.js +++ b/api/router.js @@ -1,5 +1,5 @@ -var createRenderer = require("./render/render") -var createRouter = require("./router/router") +var createRenderer = require("../render/render") +var createRouter = require("../router/router") var limiter = require("./limiter") module.exports = function($window, redraw) { diff --git a/api/tests/async.js b/api/tests/async.js new file mode 100644 index 00000000..e599b0bc --- /dev/null +++ b/api/tests/async.js @@ -0,0 +1,12 @@ +module.exports = { + setTimeout : function($window) { + $window.setTimeout = window.setTimeout; + $window.clearTimeout = window.clearTimeout; + }, + + requestAnimationFrame : function($window) { + $window.requestAnimationFrame = window.requestAnimationFrame; + $window.cancelAnimationFrame = window.cancelAnimationFrame; + } +} + diff --git a/api/tests/index.html b/api/tests/index.html new file mode 100644 index 00000000..6235b121 --- /dev/null +++ b/api/tests/index.html @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/tests/test-limiter.js b/api/tests/test-limiter.js new file mode 100644 index 00000000..e5cfb679 --- /dev/null +++ b/api/tests/test-limiter.js @@ -0,0 +1,29 @@ +"use strict" + +var o = require("../../ospec/ospec") +var domMock = require("../../test-utils/domMock") +var async = require("./async") + +var limiter = require("../limiter") + +o.spec("fps limiter", function() { + var $window, root + + [ "setTimeout", "requestAnimationFrame" ].forEach(function(type) { + o.spec(type, function() { + o.beforeEach(function() { + $window = domMock() + + async[type]($window) + }) + + o("is a function", function() { + o(typeof limiter).equals("function") + }) + + o("it returns a function", function() { + o(typeof limiter(false)).equals("function") + }) + }) + }) +}) diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js new file mode 100644 index 00000000..4a5665f2 --- /dev/null +++ b/api/tests/test-mount.js @@ -0,0 +1,114 @@ +"use strict" + +var o = require("../../ospec/ospec") +var domMock = require("../../test-utils/domMock") +var async = require("./async") + +var m = require("../../render/hyperscript") +var createMounter = require("../mount") + +o.spec("m.mount", function() { + var $window, root + + o.beforeEach(function() { + $window = domMock() + async.setTimeout($window) + root = $window.document.body + }) + + o("is a function", function() { + o(typeof createMounter).equals("function") + }) + + o("returns a function after invocation", function() { + o(typeof createMounter()).equals("function") + }) + + o("updates passed in redraw object", function() { + var redraw = {} + var mount = createMounter($window, redraw) + + mount(root, { + view : function() { + return m("div") + } + }) + + o(typeof redraw.run).equals("function") + }) + + o("renders into `root`", function() { + var mount = createMounter($window, {}) + + mount(root, { + view : function() { + return m("div") + } + }) + + o(root.firstChild.nodeName).equals("DIV") + }) + + o("redraws on redraw.run()", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var redraw = {} + var mount = createMounter($window, redraw) + + mount(root, { + view : function() { + return m("div", { + oninit : oninit, + onupdate : onupdate + }) + } + }) + + o(oninit.callCount).equals(1) + + redraw.run() + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, 20) + }) + + o("redraws on events", function(done, timeout) { + var onupdate = o.spy() + var oninit = o.spy() + var onclick = o.spy() + var mount = createMounter($window, {}) + 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(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() + }, 20) + }) +}) diff --git a/index.js b/index.js index c2bf5b9e..deaf3d1f 100644 --- a/index.js +++ b/index.js @@ -2,8 +2,8 @@ var m = require("./render/hyperscript") var trust = require("./render/trust") -var createMounter = require("./mount") -var createRouterInstance = require("./router") +var createMounter = require("./api/mount") +var createRouterInstance = require("./api/router") var createRequester = require("./request/request") var redraw = {run: function() {}} diff --git a/test-utils/domMock.js b/test-utils/domMock.js index 753a803f..9406d175 100644 --- a/test-utils/domMock.js +++ b/test-utils/domMock.js @@ -333,4 +333,4 @@ module.exports = function() { activeElement = $window.document.body return $window -} \ No newline at end of file +} From 86ce23a2bd8c665291b9fff65a2cae4b80d13444 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 18 May 2016 22:22:29 -0700 Subject: [PATCH 02/15] Fix bundler w/ relative paths from sub-dirs --- bundler/bundler.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundler/bundler.js b/bundler/bundler.js index c551649a..5656a305 100644 --- a/bundler/bundler.js +++ b/bundler/bundler.js @@ -8,12 +8,12 @@ var modules = {} function resolve(dir, data) { var replacements = [] data = data.replace(/((?:var|let|const|)\s*)([\w_$]+)(\s*=\s*)require\(([^\)]+)\)/g, function(match, def, variable, eq, dep) { - var filename = new Function("return " + dep).call() - var pathname = path.dirname(filename) + var filename = new Function("return " + dep).call() + var pathname = path.dirname(path.resolve(dir, filename)) var normalized = path.normalize(dir + "/" + filename) if (modules[normalized] === undefined) { modules[normalized] = variable - return resolve(pathname, + return resolve(pathname, fs.readFileSync(dir + "/" + filename + ".js", "utf8") .replace(/"use strict"\s*/gm, "") .replace(/module\.exports\s*=\s*/gm, def + variable + eq) From 0cf509f674ea3899293450b45108728c52a25d2e Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 18 May 2016 23:08:35 -0700 Subject: [PATCH 03/15] Add forcing support and improve tests Also verify that we're using tabs, whee. --- api/limiter.js | 15 ++++---- api/tests/async.js | 42 +++++++++++++++++++--- api/tests/test-limiter.js | 76 +++++++++++++++++++++++++++++---------- 3 files changed, 104 insertions(+), 29 deletions(-) diff --git a/api/limiter.js b/api/limiter.js index ec35a80e..477c715e 100644 --- a/api/limiter.js +++ b/api/limiter.js @@ -5,22 +5,23 @@ module.exports = function($window, render) { var cAF = $window.cancelAnimationFrame || $window.clearTimeout var last = 0 - var pending + var pending = null - return function() { + return function(force) { var now = new Date() - // First render, OR if the time since the last render is greater - // than the frame budget - // just immediately render - if(!last || now - last > FRAME_BUDGET) { + // Immediately render if: + // Forced + // Haven't rendered yet + // Time since the last render is greater than the frame budget + if(force || !last || now - last > FRAME_BUDGET) { last = now; return render() } // Redraw already pending, abort - if(pending) { + if(pending !== null) { return } diff --git a/api/tests/async.js b/api/tests/async.js index e599b0bc..f3206fd7 100644 --- a/api/tests/async.js +++ b/api/tests/async.js @@ -1,12 +1,46 @@ +var _fns = [] +var _last = 0 +var _frame = 1000 / 60 + module.exports = { setTimeout : function($window) { - $window.setTimeout = window.setTimeout; - $window.clearTimeout = window.clearTimeout; + $window.setTimeout = typeof window === "object" ? window.setTimeout : global.setTimeout; + $window.clearTimeout = typeof window === "object" ? window.clearTimeout : global.setTimeout; }, requestAnimationFrame : function($window) { - $window.requestAnimationFrame = window.requestAnimationFrame; - $window.cancelAnimationFrame = window.cancelAnimationFrame; + // Modified version of https://github.com/chrisdickinson/raf + // Copyright chrisdickinson I guess? + $window.requestAnimationFrame = typeof window === "object" ? window.requestAnimationFrame : function(fn) { + if(!_fns.length) { + var now = Date.now() + var next = Math.max(0, _frame - (now - _last)) + + _last = next + now + + setTimeout(function() { + var fns = _fns.slice() + + _fns = [] + + for(var i = 0; i < fns.length; i++) { + if(typeof fns[i] !== "function") { + continue + } + + fns[i](_last) + } + }, Math.round(next)) + } + + _fns.push(fn) + + return _fns.length - 1; + } + + $window.cancelAnimationFrame = typeof window === "object" ? window.cancelAnimationFrame : function(handle) { + _fns[handle] = null + }; } } diff --git a/api/tests/test-limiter.js b/api/tests/test-limiter.js index e5cfb679..ece28c69 100644 --- a/api/tests/test-limiter.js +++ b/api/tests/test-limiter.js @@ -7,23 +7,63 @@ var async = require("./async") var limiter = require("../limiter") o.spec("fps limiter", function() { - var $window, root + var $window, root - [ "setTimeout", "requestAnimationFrame" ].forEach(function(type) { - o.spec(type, function() { - o.beforeEach(function() { - $window = domMock() - - async[type]($window) - }) - - o("is a function", function() { - o(typeof limiter).equals("function") - }) - - o("it returns a function", function() { - o(typeof limiter(false)).equals("function") - }) - }) - }) + [ + "setTimeout", + "requestAnimationFrame", + ].forEach(function(type) { + o.spec(type, function() { + o.beforeEach(function() { + $window = domMock() + + async[type]($window) + }) + + o("is a function", function() { + o(typeof limiter).equals("function") + }) + + o("it returns a function", function() { + o(typeof limiter($window, false)).equals("function") + }) + + o("it runs synchronously the first time", function() { + var spy = o.spy() + var run = limiter($window, spy) + + run() + + o(spy.callCount).equals(1) + }) + + o("it only runs once per tick", function(done) { + var spy = o.spy() + var run = limiter($window, spy) + + run() + run() + run() + + o(spy.callCount).equals(1) + + setTimeout(function() { + o(spy.callCount).equals(2) + + done() + }, 17) + }) + + o("it supports forcing a synchronous redraw", function() { + var spy = o.spy() + var run = limiter($window, spy) + + run() + run() + run(true) + + o(spy.callCount).equals(2) + }) + }) + }) }) From fc481ff4976489abde0ac46ac195319ab29155a0 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 18 May 2016 23:10:00 -0700 Subject: [PATCH 04/15] strict all the things --- api/limiter.js | 2 ++ api/router.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/api/limiter.js b/api/limiter.js index 477c715e..f6fc80cf 100644 --- a/api/limiter.js +++ b/api/limiter.js @@ -1,3 +1,5 @@ +"use strict" + var FRAME_BUDGET = 16 // 60 frames per second = 1 call per 16 ms module.exports = function($window, render) { diff --git a/api/router.js b/api/router.js index eabd0e4c..4da9795d 100644 --- a/api/router.js +++ b/api/router.js @@ -1,3 +1,5 @@ +"use strict" + var createRenderer = require("../render/render") var createRouter = require("../router/router") var limiter = require("./limiter") From 8f7cc0cb114cd06a00c376865b01a431e2ab8fea Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 18 May 2016 23:11:02 -0700 Subject: [PATCH 05/15] Stricter --- api/mount.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/mount.js b/api/mount.js index 53010bd0..f1526613 100644 --- a/api/mount.js +++ b/api/mount.js @@ -1,3 +1,5 @@ +"use strict" + var createRenderer = require("../render/render") var limiter = require("./limiter"); From ce42adb720359cc4dd69fd8bc40b0744dac2e7da Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 18 May 2016 23:17:22 -0700 Subject: [PATCH 06/15] Basic router tests --- api/tests/index.html | 1 + api/tests/test-router.js | 92 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 api/tests/test-router.js diff --git a/api/tests/index.html b/api/tests/index.html index 6235b121..78526db3 100644 --- a/api/tests/index.html +++ b/api/tests/index.html @@ -25,6 +25,7 @@ + diff --git a/api/tests/test-router.js b/api/tests/test-router.js new file mode 100644 index 00000000..473338b7 --- /dev/null +++ b/api/tests/test-router.js @@ -0,0 +1,92 @@ +"use strict" + +var o = require("../../ospec/ospec") +var pushStateMock = require("../../test-utils/pushStateMock") +var domMock = require("../../test-utils/domMock") +var async = require("./async") + +var m = require("../../render/hyperscript") +var createRouter = require("../router") + +o.spec("m.mount", function() { + var $window, root + + o.beforeEach(function() { + var dom = domMock() + var location = pushStateMock() + + Object.keys(location).forEach(function(key) { + dom[key] = location[key] + }) + + $window = dom + async.setTimeout($window) + root = $window.document.body + }) + + o("is a function", function() { + o(typeof createRouter).equals("function") + }) + + o("returns a function after invocation", function() { + o(typeof createRouter($window)).equals("function") + }) + + o("updates passed in redraw object", function() { + var redraw = {} + var router = createRouter($window, redraw) + + router(root, "/", { + "/" : { + view : function() { + return m("div") + } + } + }) + + o(typeof redraw.run).equals("function") + }) + + o("renders into `root`", function() { + var router = createRouter($window, {}) + + router(root, "/", { + "/" : { + view : function() { + return m("div") + } + } + }) + + o(root.firstChild.nodeName).equals("DIV") + }) + + o("redraws on redraw.run()", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var redraw = {} + var router = createRouter($window, redraw) + + router(root, "/", { + "/" : { + view : function() { + return m("div", { + oninit : oninit, + onupdate : onupdate + }) + } + } + }) + + o(oninit.callCount).equals(1) + + redraw.run() + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, 20) + }) +}) From 89dfd6e97d10ca9a4b25b22fe32abe9a092ee0cf Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 18 May 2016 23:59:44 -0700 Subject: [PATCH 07/15] Remove unused reference --- api/limiter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/api/limiter.js b/api/limiter.js index f6fc80cf..64202901 100644 --- a/api/limiter.js +++ b/api/limiter.js @@ -4,7 +4,6 @@ var FRAME_BUDGET = 16 // 60 frames per second = 1 call per 16 ms module.exports = function($window, render) { var rAF = $window.requestAnimationFrame || $window.setTimeout - var cAF = $window.cancelAnimationFrame || $window.clearTimeout var last = 0 var pending = null From 41b24b998a482299c29af9033806557a7ed0fbc3 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Thu, 19 May 2016 00:09:20 -0700 Subject: [PATCH 08/15] m.route tests should run against all prefixes Also tests `router.link` now. Had to add a `preventDefault` function to domMock events that doesn't do anything, that may need a more-complete implementation someday? --- api/tests/test-router.js | 244 ++++++++++++++++++++++++++------------- test-utils/domMock.js | 3 + 2 files changed, 166 insertions(+), 81 deletions(-) diff --git a/api/tests/test-router.js b/api/tests/test-router.js index 473338b7..5dc6572a 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -8,85 +8,167 @@ var async = require("./async") var m = require("../../render/hyperscript") var createRouter = require("../router") -o.spec("m.mount", function() { - var $window, root - - o.beforeEach(function() { - var dom = domMock() - var location = pushStateMock() - - Object.keys(location).forEach(function(key) { - dom[key] = location[key] - }) - - $window = dom - async.setTimeout($window) - root = $window.document.body - }) - - o("is a function", function() { - o(typeof createRouter).equals("function") - }) - - o("returns a function after invocation", function() { - o(typeof createRouter($window)).equals("function") - }) - - o("updates passed in redraw object", function() { - var redraw = {} - var router = createRouter($window, redraw) - - router(root, "/", { - "/" : { - view : function() { - return m("div") - } - } - }) - - o(typeof redraw.run).equals("function") - }) - - o("renders into `root`", function() { - var router = createRouter($window, {}) - - router(root, "/", { - "/" : { - view : function() { - return m("div") - } - } - }) - - o(root.firstChild.nodeName).equals("DIV") - }) - - o("redraws on redraw.run()", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var redraw = {} - var router = createRouter($window, redraw) - - router(root, "/", { - "/" : { - view : function() { - return m("div", { - oninit : oninit, - onupdate : onupdate - }) - } - } - }) - - o(oninit.callCount).equals(1) - - redraw.run() - - // Wrapped to give time for the rate-limited redraw to fire - setTimeout(function() { - o(onupdate.callCount).equals(1) - - done() - }, 20) - }) +o.spec("m.route", function() { + var $window, root, router + + ["#", "?", "", "#!", "?!"].forEach(function(prefix) { + var spec = prefix ? "prefix " + prefix : "pushstate"; + + o.spec(spec, function() { + o.beforeEach(function() { + var dom = domMock() + var location = pushStateMock() + + // Generate a DOM + Location mock + Object.keys(location).forEach(function(key) { + dom[key] = location[key] + }) + + $window = dom + async.setTimeout($window) + root = $window.document.body + }) + + o("is a function", function() { + o(typeof createRouter).equals("function") + }) + + o("returns a function after invocation", function() { + o(typeof createRouter($window)).equals("function") + }) + + o("updates passed in redraw object", function() { + var redraw = {} + var router = createRouter($window, redraw) + + router.prefix = prefix + + router(root, "/", { + "/" : { + view: function() { + return m("div") + } + } + }) + + o(typeof redraw.run).equals("function") + }) + + o("renders into `root`", function() { + var router = createRouter($window, {}) + + router.prefix = prefix + + router(root, "/", { + "/" : { + view: function() { + return m("div") + } + } + }) + + o(root.firstChild.nodeName).equals("DIV") + }) + + o("redraws on redraw.run()", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var redraw = {} + var router = createRouter($window, redraw) + + router.prefix = prefix + + router(root, "/", { + "/" : { + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate + }) + } + } + }) + + o(oninit.callCount).equals(1) + + redraw.run() + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, 20) + }) + + o("redraws on events", function(done, timeout) { + var onupdate = o.spy() + var oninit = o.spy() + var onclick = o.spy() + var router = createRouter($window, {}) + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + router.prefix = prefix + + router(root, "/", { + "/" : { + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate, + onclick: onclick, + }) + } + } + }) + + root.firstChild.dispatchEvent(e) + + o(oninit.callCount).equals(1) + + 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() + }, 20) + }) + + o("changes location on route.link", function() { + var router = createRouter($window, {}) + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + router.prefix = prefix + + router(root, "/", { + "/" : { + view: function() { + return m("a", { + href: "/test", + oncreate: router.link + }) + } + }, + "/test" : { + view : function() { + return m("div") + } + } + }) + + root.firstChild.dispatchEvent(e) + + o($window.location.href).equals("http://localhost/" + prefix + "/test") + }) + }) + }) }) diff --git a/test-utils/domMock.js b/test-utils/domMock.js index 9406d175..e8bf9095 100644 --- a/test-utils/domMock.js +++ b/test-utils/domMock.js @@ -173,6 +173,9 @@ module.exports = function() { events[e.type][i].call(this, e) } } + e.preventDefault = function() { + // TODO: should this do something? + } if (typeof this["on" + e.type] === "function" && !isModernEvent(e.type)) this["on" + e.type](e) }, } From 282cc5afddcb61acf232367b15305b954dd75200 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Thu, 19 May 2016 09:51:36 -0700 Subject: [PATCH 09/15] Fix m.route() tests - Wasn't calling route.prefix(), was assigning to it - Needed to handle pushstate prefix (`""`) specially in assertions --- api/tests/test-router.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/api/tests/test-router.js b/api/tests/test-router.js index 5dc6572a..5c17ac73 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -11,7 +11,13 @@ var createRouter = require("../router") o.spec("m.route", function() { var $window, root, router - ["#", "?", "", "#!", "?!"].forEach(function(prefix) { + [ + "#", + "?", + "#!", + "?!", + "" + ].forEach(function(prefix) { var spec = prefix ? "prefix " + prefix : "pushstate"; o.spec(spec, function() { @@ -41,7 +47,7 @@ o.spec("m.route", function() { var redraw = {} var router = createRouter($window, redraw) - router.prefix = prefix + router.prefix(prefix) router(root, "/", { "/" : { @@ -57,7 +63,7 @@ o.spec("m.route", function() { o("renders into `root`", function() { var router = createRouter($window, {}) - router.prefix = prefix + router.prefix(prefix) router(root, "/", { "/" : { @@ -76,7 +82,7 @@ o.spec("m.route", function() { var redraw = {} var router = createRouter($window, redraw) - router.prefix = prefix + router.prefix(prefix) router(root, "/", { "/" : { @@ -110,7 +116,7 @@ o.spec("m.route", function() { e.initEvent("click", true, true) - router.prefix = prefix + router.prefix(prefix) router(root, "/", { "/" : { @@ -147,7 +153,7 @@ o.spec("m.route", function() { e.initEvent("click", true, true) - router.prefix = prefix + router.prefix(prefix) router(root, "/", { "/" : { @@ -165,9 +171,11 @@ o.spec("m.route", function() { } }) + o($window.location.href).equals("http://localhost/" + (prefix ? prefix + "/" : "")) + root.firstChild.dispatchEvent(e) - o($window.location.href).equals("http://localhost/" + prefix + "/test") + o($window.location.href).equals("http://localhost/" + (prefix ? prefix + "/test" : "test")) }) }) }) From 74f3e0d2055a0915d32a025a735508657315e82b Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Thu, 19 May 2016 10:36:10 -0700 Subject: [PATCH 10/15] Remove old manual testing file --- test-redraw.html | 57 ------------------------------------------------ 1 file changed, 57 deletions(-) delete mode 100644 test-redraw.html diff --git a/test-redraw.html b/test-redraw.html deleted file mode 100644 index bffd0396..00000000 --- a/test-redraw.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - -
- - - - - From 37b0ce20c5d9462aa1789b26a79b35d3ac4eb55e Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Thu, 19 May 2016 11:08:26 -0700 Subject: [PATCH 11/15] Add missing dependencies --- api/tests/index.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/tests/index.html b/api/tests/index.html index 78526db3..bc0d8b51 100644 --- a/api/tests/index.html +++ b/api/tests/index.html @@ -7,7 +7,9 @@ + + From 749ff74bea33f2fc25bbb3704fdd3364ec1917b1 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Thu, 19 May 2016 11:35:45 -0700 Subject: [PATCH 12/15] Test both async types when using router --- api/tests/test-router.js | 327 ++++++++++++++++++++------------------- 1 file changed, 167 insertions(+), 160 deletions(-) diff --git a/api/tests/test-router.js b/api/tests/test-router.js index 5c17ac73..ce8c558d 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -12,170 +12,177 @@ o.spec("m.route", function() { var $window, root, router [ - "#", - "?", - "#!", - "?!", - "" - ].forEach(function(prefix) { - var spec = prefix ? "prefix " + prefix : "pushstate"; - - o.spec(spec, function() { - o.beforeEach(function() { - var dom = domMock() - var location = pushStateMock() + "setTimeout", + "requestAnimationFrame" + ].forEach(function(timing) { + o.spec(timing, function() { + [ + "#", + "?", + "#!", + "?!", + "" + ].forEach(function(prefix) { + var spec = prefix ? "prefix " + prefix : "pushstate"; - // Generate a DOM + Location mock - Object.keys(location).forEach(function(key) { - dom[key] = location[key] - }) - - $window = dom - async.setTimeout($window) - root = $window.document.body - }) - - o("is a function", function() { - o(typeof createRouter).equals("function") - }) - - o("returns a function after invocation", function() { - o(typeof createRouter($window)).equals("function") - }) - - o("updates passed in redraw object", function() { - var redraw = {} - var router = createRouter($window, redraw) - - router.prefix(prefix) - - router(root, "/", { - "/" : { - view: function() { - return m("div") - } - } - }) - - o(typeof redraw.run).equals("function") - }) - - o("renders into `root`", function() { - var router = createRouter($window, {}) - - router.prefix(prefix) - - router(root, "/", { - "/" : { - view: function() { - return m("div") - } - } - }) - - o(root.firstChild.nodeName).equals("DIV") - }) - - o("redraws on redraw.run()", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var redraw = {} - var router = createRouter($window, redraw) - - router.prefix(prefix) - - router(root, "/", { - "/" : { - view: function() { - return m("div", { - oninit: oninit, - onupdate: onupdate - }) - } - } - }) - - o(oninit.callCount).equals(1) - - redraw.run() - - // Wrapped to give time for the rate-limited redraw to fire - setTimeout(function() { - o(onupdate.callCount).equals(1) + o.spec(spec, function() { + o.beforeEach(function() { + var dom = domMock() + var location = pushStateMock() + + // Generate a DOM + Location mock + Object.keys(location).forEach(function(key) { + dom[key] = location[key] + }) + + $window = dom + async[timing]($window) + root = $window.document.body + }) - done() - }, 20) - }) - - o("redraws on events", function(done, timeout) { - var onupdate = o.spy() - var oninit = o.spy() - var onclick = o.spy() - var router = createRouter($window, {}) - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - router.prefix(prefix) - - router(root, "/", { - "/" : { - view: function() { - return m("div", { - oninit: oninit, - onupdate: onupdate, - onclick: onclick, - }) - } - } - }) - - root.firstChild.dispatchEvent(e) - - o(oninit.callCount).equals(1) - - 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) + o("is a function", function() { + o(typeof createRouter).equals("function") + }) - done() - }, 20) - }) - - o("changes location on route.link", function() { - var router = createRouter($window, {}) - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - router.prefix(prefix) - - router(root, "/", { - "/" : { - view: function() { - return m("a", { - href: "/test", - oncreate: router.link - }) - } - }, - "/test" : { - view : function() { - return m("div") - } - } + o("returns a function after invocation", function() { + o(typeof createRouter($window)).equals("function") + }) + + o("updates passed in redraw object", function() { + var redraw = {} + var router = createRouter($window, redraw) + + router.prefix(prefix) + + router(root, "/", { + "/" : { + view: function() { + return m("div") + } + } + }) + + o(typeof redraw.run).equals("function") + }) + + o("renders into `root`", function() { + var router = createRouter($window, {}) + + router.prefix(prefix) + + router(root, "/", { + "/" : { + view: function() { + return m("div") + } + } + }) + + o(root.firstChild.nodeName).equals("DIV") + }) + + o("redraws on redraw.run()", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var redraw = {} + var router = createRouter($window, redraw) + + router.prefix(prefix) + + router(root, "/", { + "/" : { + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate + }) + } + } + }) + + o(oninit.callCount).equals(1) + + redraw.run() + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, 20) + }) + + o("redraws on events", function(done, timeout) { + var onupdate = o.spy() + var oninit = o.spy() + var onclick = o.spy() + var router = createRouter($window, {}) + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + router.prefix(prefix) + + router(root, "/", { + "/" : { + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate, + onclick: onclick, + }) + } + } + }) + + root.firstChild.dispatchEvent(e) + + o(oninit.callCount).equals(1) + + 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() + }, 20) + }) + + o("changes location on route.link", function() { + var router = createRouter($window, {}) + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + router.prefix(prefix) + + router(root, "/", { + "/" : { + view: function() { + return m("a", { + href: "/test", + oncreate: router.link + }) + } + }, + "/test" : { + view : function() { + return m("div") + } + } + }) + + o($window.location.href).equals("http://localhost/" + (prefix ? prefix + "/" : "")) + + root.firstChild.dispatchEvent(e) + + o($window.location.href).equals("http://localhost/" + (prefix ? prefix + "/test" : "test")) + }) }) - - o($window.location.href).equals("http://localhost/" + (prefix ? prefix + "/" : "")) - - root.firstChild.dispatchEvent(e) - - o($window.location.href).equals("http://localhost/" + (prefix ? prefix + "/test" : "test")) }) }) }) From 36bfc6892b3f8746bd6b1d25328844f88dc6c2da Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Thu, 19 May 2016 11:52:14 -0700 Subject: [PATCH 13/15] Fix var shadowing infinite loop bug in browsers --- api/tests/test-router.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/api/tests/test-router.js b/api/tests/test-router.js index ce8c558d..c203e020 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -6,7 +6,9 @@ var domMock = require("../../test-utils/domMock") var async = require("./async") var m = require("../../render/hyperscript") -var createRouter = require("../router") +// Convention would be `createRouter`, but that causes variable shadowing bugs +// in browsers when running tests, so `makeRouter` it is +var makeRouter = require("../router") o.spec("m.route", function() { var $window, root, router @@ -41,16 +43,16 @@ o.spec("m.route", function() { }) o("is a function", function() { - o(typeof createRouter).equals("function") + o(typeof makeRouter).equals("function") }) o("returns a function after invocation", function() { - o(typeof createRouter($window)).equals("function") + o(typeof makeRouter($window)).equals("function") }) o("updates passed in redraw object", function() { var redraw = {} - var router = createRouter($window, redraw) + var router = makeRouter($window, redraw) router.prefix(prefix) @@ -66,7 +68,7 @@ o.spec("m.route", function() { }) o("renders into `root`", function() { - var router = createRouter($window, {}) + var router = makeRouter($window, {}) router.prefix(prefix) @@ -85,7 +87,7 @@ o.spec("m.route", function() { var onupdate = o.spy() var oninit = o.spy() var redraw = {} - var router = createRouter($window, redraw) + var router = makeRouter($window, redraw) router.prefix(prefix) @@ -116,7 +118,7 @@ o.spec("m.route", function() { var onupdate = o.spy() var oninit = o.spy() var onclick = o.spy() - var router = createRouter($window, {}) + var router = makeRouter($window, {}) var e = $window.document.createEvent("MouseEvents") e.initEvent("click", true, true) @@ -153,7 +155,7 @@ o.spec("m.route", function() { }) o("changes location on route.link", function() { - var router = createRouter($window, {}) + var router = makeRouter($window, {}) var e = $window.document.createEvent("MouseEvents") e.initEvent("click", true, true) From 9f1afea9e197a2956e0d1c4023e88d116999f458 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Thu, 19 May 2016 11:53:41 -0700 Subject: [PATCH 14/15] Match @lhorie style I don't usually write JS w/o semicolons, so no surprise I got this wrong at first. --- api/tests/test-limiter.js | 2 +- api/tests/test-router.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/tests/test-limiter.js b/api/tests/test-limiter.js index ece28c69..8d4456ca 100644 --- a/api/tests/test-limiter.js +++ b/api/tests/test-limiter.js @@ -9,7 +9,7 @@ var limiter = require("../limiter") o.spec("fps limiter", function() { var $window, root - [ + void [ "setTimeout", "requestAnimationFrame", ].forEach(function(type) { diff --git a/api/tests/test-router.js b/api/tests/test-router.js index c203e020..e7a0aab5 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -13,12 +13,12 @@ var makeRouter = require("../router") o.spec("m.route", function() { var $window, root, router - [ + void [ "setTimeout", "requestAnimationFrame" ].forEach(function(timing) { o.spec(timing, function() { - [ + void [ "#", "?", "#!", From da6b697d19e0bbf1eabdb0b29234a0d1c9bd56c3 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Thu, 19 May 2016 11:57:41 -0700 Subject: [PATCH 15/15] spaces -> tabs --- api/tests/test-mount.js | 198 ++++++++++++++++++++-------------------- bundler/bundler.js | 4 +- 2 files changed, 101 insertions(+), 101 deletions(-) diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js index 4a5665f2..a9474761 100644 --- a/api/tests/test-mount.js +++ b/api/tests/test-mount.js @@ -8,107 +8,107 @@ var m = require("../../render/hyperscript") var createMounter = require("../mount") o.spec("m.mount", function() { - var $window, root - - o.beforeEach(function() { + var $window, root + + o.beforeEach(function() { $window = domMock() - async.setTimeout($window) + async.setTimeout($window) root = $window.document.body - }) - + }) + o("is a function", function() { - o(typeof createMounter).equals("function") - }) - - o("returns a function after invocation", function() { - o(typeof createMounter()).equals("function") - }) - - o("updates passed in redraw object", function() { - var redraw = {} - var mount = createMounter($window, redraw) - - mount(root, { - view : function() { - return m("div") - } - }) - - o(typeof redraw.run).equals("function") - }) - - o("renders into `root`", function() { - var mount = createMounter($window, {}) - - mount(root, { - view : function() { - return m("div") - } - }) - - o(root.firstChild.nodeName).equals("DIV") - }) - - o("redraws on redraw.run()", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var redraw = {} - var mount = createMounter($window, redraw) - - mount(root, { - view : function() { - return m("div", { - oninit : oninit, - onupdate : onupdate - }) - } - }) - - o(oninit.callCount).equals(1) - - redraw.run() - - // Wrapped to give time for the rate-limited redraw to fire - setTimeout(function() { - o(onupdate.callCount).equals(1) - - done() - }, 20) - }) - - o("redraws on events", function(done, timeout) { - var onupdate = o.spy() - var oninit = o.spy() - var onclick = o.spy() - var mount = createMounter($window, {}) - var e = $window.document.createEvent("MouseEvents") - + o(typeof createMounter).equals("function") + }) + + o("returns a function after invocation", function() { + o(typeof createMounter()).equals("function") + }) + + o("updates passed in redraw object", function() { + var redraw = {} + var mount = createMounter($window, redraw) + + mount(root, { + view : function() { + return m("div") + } + }) + + o(typeof redraw.run).equals("function") + }) + + o("renders into `root`", function() { + var mount = createMounter($window, {}) + + mount(root, { + view : function() { + return m("div") + } + }) + + o(root.firstChild.nodeName).equals("DIV") + }) + + o("redraws on redraw.run()", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var redraw = {} + var mount = createMounter($window, redraw) + + mount(root, { + view : function() { + return m("div", { + oninit : oninit, + onupdate : onupdate + }) + } + }) + + o(oninit.callCount).equals(1) + + redraw.run() + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, 20) + }) + + o("redraws on events", function(done, timeout) { + var onupdate = o.spy() + var oninit = o.spy() + var onclick = o.spy() + var mount = createMounter($window, {}) + 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(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() - }, 20) - }) + + mount(root, { + view : function() { + return m("div", { + oninit : oninit, + onupdate : onupdate, + onclick : onclick, + }) + } + }) + + root.firstChild.dispatchEvent(e) + + o(oninit.callCount).equals(1) + + 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() + }, 20) + }) }) diff --git a/bundler/bundler.js b/bundler/bundler.js index 5656a305..46787186 100644 --- a/bundler/bundler.js +++ b/bundler/bundler.js @@ -8,12 +8,12 @@ var modules = {} function resolve(dir, data) { var replacements = [] data = data.replace(/((?:var|let|const|)\s*)([\w_$]+)(\s*=\s*)require\(([^\)]+)\)/g, function(match, def, variable, eq, dep) { - var filename = new Function("return " + dep).call() + var filename = new Function("return " + dep).call() var pathname = path.dirname(path.resolve(dir, filename)) var normalized = path.normalize(dir + "/" + filename) if (modules[normalized] === undefined) { modules[normalized] = variable - return resolve(pathname, + return resolve(pathname, fs.readFileSync(dir + "/" + filename + ".js", "utf8") .replace(/"use strict"\s*/gm, "") .replace(/module\.exports\s*=\s*/gm, def + variable + eq)