diff --git a/api/tests/test-router.js b/api/tests/test-router.js index 627ffc34..713f9bf1 100644 --- a/api/tests/test-router.js +++ b/api/tests/test-router.js @@ -11,325 +11,327 @@ var apiPubSub = require("../../api/pubsub") var apiRouter = require("../../api/router") o.spec("route", function() { - void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) { - o.spec("using prefix `" + prefix + "`", function() { - var FRAME_BUDGET = Math.floor(1000 / 60) - var $window, root, redraw, route + void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) { + void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) { + o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() { + var FRAME_BUDGET = Math.floor(1000 / 60) + var $window, root, redraw, route - o.beforeEach(function() { - $window = {} + o.beforeEach(function() { + $window = {} - var dom = domMock() - for (var key in dom) $window[key] = dom[key] + var dom = domMock() + for (var key in dom) $window[key] = dom[key] - var loc = pushStateMock() - for (var key in loc) $window[key] = loc[key] + var loc = pushStateMock(env) + for (var key in loc) $window[key] = loc[key] - root = $window.document.body + root = $window.document.body - redraw = apiPubSub() - route = apiRouter($window, coreRenderer($window), redraw) - route.prefix(prefix) - }) + redraw = apiPubSub() + route = apiRouter($window, coreRenderer($window), redraw) + route.prefix(prefix) + }) - o("renders into `root`", function(done) { - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - view: function() { - return m("div") + o("renders into `root`", function(done) { + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + view: function() { + return m("div") + } } - } - }) - - callAsync(function() { - o(root.firstChild.nodeName).equals("DIV") - - done() - }) - }) - - o("default route doesn't break back button", function(done) { - $window.location.href = "http://google.com" - route(root, "/a", { - "/a" : { - view: function() { - return m("div") - } - } - }) - - setTimeout(function() { - o(root.firstChild.nodeName).equals("DIV") - - $window.history.back() - - o($window.location.pathname).equals("/") - - done() - }, FRAME_BUDGET) - }) - - o("default route does not inherit params", function(done) { - $window.location.href = "/invalid?foo=bar" - route(root, "/a", { - "/a" : { - oninit: init, - view: function() { - return m("div") - } - } - }) - - function init(vnode) { - o(vnode.attrs).deepEquals({}) - - done() - } - }) - - o("redraws when render function is executed", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - view: function() { - return m("div", { - oninit: oninit, - onupdate: onupdate - }) - } - } - }) - - callAsync(function() { - o(oninit.callCount).equals(1) - - redraw.publish() - - // Wrapped to give time for the rate-limited redraw to fire - setTimeout(function() { - o(onupdate.callCount).equals(1) + }) + callAsync(function() { + o(root.firstChild.nodeName).equals("DIV") + done() - }, FRAME_BUDGET) + }) }) - }) - o("redraws on events", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var onclick = o.spy() - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - view: function() { - return m("div", { - oninit: oninit, - onupdate: onupdate, - onclick: onclick, - }) + o("default route doesn't break back button", function(done) { + $window.location.href = "http://google.com" + route(root, "/a", { + "/a" : { + view: function() { + return m("div") + } } - } - }) - - callAsync(function() { - 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() - }, FRAME_BUDGET) - }) - }) - - o("event handlers can skip redraw", function(done) { - var onupdate = o.spy() - var oninit = o.spy() - var onclick = o.spy() - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - view: function() { - return m("div", { - oninit: oninit, - onupdate: onupdate, - onclick: function(e) { - e.redraw = false - }, - }) - } - } - }) - - callAsync(function() { - root.firstChild.dispatchEvent(e) - - o(oninit.callCount).equals(1) - - // Wrapped to ensure no redraw fired - setTimeout(function() { - o(onupdate.callCount).equals(0) - - done() - }, FRAME_BUDGET) - }) - }) - - o("changes location on route.link", function(done) { - var e = $window.document.createEvent("MouseEvents") - - e.initEvent("click", true, true) - - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - view: function() { - return m("a", { - href: "/test", - oncreate: route.link - }) - } - }, - "/test" : { - view : function() { - return m("div") - } - } - }) - - callAsync(function() { - var slash = prefix[0] === "/" ? "" : "/" - - o($window.location.href).equals("http://localhost" + slash + (prefix ? prefix + "/" : "")) - - root.firstChild.dispatchEvent(e) - - o($window.location.href).equals("http://localhost" + slash + (prefix ? prefix + "/" : "") + "test") - - done() - }) - }) - - o("accepts object as payload", function(done) { - var Component = { - view: function() { - return m("div") - } - } - - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - resolve: function(resolve) {resolve(Component)}, - render: function(vnode) {return vnode}, - }, - }) - - callAsync(function() { - o(root.firstChild.nodeName).equals("DIV") - - done() - }) - }) - - o("accepts object without `render` method as payload", function(done) { - var Component = { - view: function() { - return m("div") - } - } - - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - resolve: function(resolve) {resolve(Component)}, - }, - }) - - callAsync(function() { - o(root.firstChild.nodeName).equals("DIV") - - done() - }) - }) - - o("accepts object without `resolve` method as payload", function(done) { - var Component = { - view: function() { - return m("div") - } - } - - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - render: function() {return m(Component)}, - }, - }) - - callAsync(function() { - o(root.firstChild.nodeName).equals("DIV") - - done() - }) - }) - - o("calls resolve and render correct number of times", function(done) { - var resolveCount = 0 - var renderCount = 0 - var Component = { - view: function() { - return m("div") - } - } - - $window.location.href = prefix + "/" - route(root, "/", { - "/" : { - resolve: function(resolve) { - resolveCount++ - resolve(Component) - }, - render: function(vnode) { - renderCount++ - return vnode - }, - }, - }) - - callAsync(function() { - o(resolveCount).equals(1) - o(renderCount).equals(1) - - redraw.publish() + }) setTimeout(function() { - o(resolveCount).equals(1) - o(renderCount).equals(2) + o(root.firstChild.nodeName).equals("DIV") + + $window.history.back() + + o($window.location.pathname).equals("/") done() }, FRAME_BUDGET) }) + + o("default route does not inherit params", function(done) { + $window.location.href = "/invalid?foo=bar" + route(root, "/a", { + "/a" : { + oninit: init, + view: function() { + return m("div") + } + } + }) + + function init(vnode) { + o(vnode.attrs).deepEquals({}) + + done() + } + }) + + o("redraws when render function is executed", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate + }) + } + } + }) + + callAsync(function() { + o(oninit.callCount).equals(1) + + redraw.publish() + + // Wrapped to give time for the rate-limited redraw to fire + setTimeout(function() { + o(onupdate.callCount).equals(1) + + done() + }, FRAME_BUDGET) + }) + }) + + o("redraws on events", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var onclick = o.spy() + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate, + onclick: onclick, + }) + } + } + }) + + callAsync(function() { + 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() + }, FRAME_BUDGET) + }) + }) + + o("event handlers can skip redraw", function(done) { + var onupdate = o.spy() + var oninit = o.spy() + var onclick = o.spy() + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + view: function() { + return m("div", { + oninit: oninit, + onupdate: onupdate, + onclick: function(e) { + e.redraw = false + }, + }) + } + } + }) + + callAsync(function() { + root.firstChild.dispatchEvent(e) + + o(oninit.callCount).equals(1) + + // Wrapped to ensure no redraw fired + setTimeout(function() { + o(onupdate.callCount).equals(0) + + done() + }, FRAME_BUDGET) + }) + }) + + o("changes location on route.link", function(done) { + var e = $window.document.createEvent("MouseEvents") + + e.initEvent("click", true, true) + + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + view: function() { + return m("a", { + href: "/test", + oncreate: route.link + }) + } + }, + "/test" : { + view : function() { + return m("div") + } + } + }) + + callAsync(function() { + var slash = prefix[0] === "/" ? "" : "/" + + o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "")) + + root.firstChild.dispatchEvent(e) + + o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test") + + done() + }) + }) + + o("accepts object as payload", function(done) { + var Component = { + view: function() { + return m("div") + } + } + + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + resolve: function(resolve) {resolve(Component)}, + render: function(vnode) {return vnode}, + }, + }) + + callAsync(function() { + o(root.firstChild.nodeName).equals("DIV") + + done() + }) + }) + + o("accepts object without `render` method as payload", function(done) { + var Component = { + view: function() { + return m("div") + } + } + + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + resolve: function(resolve) {resolve(Component)}, + }, + }) + + callAsync(function() { + o(root.firstChild.nodeName).equals("DIV") + + done() + }) + }) + + o("accepts object without `resolve` method as payload", function(done) { + var Component = { + view: function() { + return m("div") + } + } + + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + render: function() {return m(Component)}, + }, + }) + + callAsync(function() { + o(root.firstChild.nodeName).equals("DIV") + + done() + }) + }) + + o("calls resolve and render correct number of times", function(done) { + var resolveCount = 0 + var renderCount = 0 + var Component = { + view: function() { + return m("div") + } + } + + $window.location.href = prefix + "/" + route(root, "/", { + "/" : { + resolve: function(resolve) { + resolveCount++ + resolve(Component) + }, + render: function(vnode) { + renderCount++ + return vnode + }, + }, + }) + + callAsync(function() { + o(resolveCount).equals(1) + o(renderCount).equals(1) + + redraw.publish() + + setTimeout(function() { + o(resolveCount).equals(1) + o(renderCount).equals(2) + + done() + }, FRAME_BUDGET) + }) + }) }) }) }) diff --git a/router/router.js b/router/router.js index 6d522916..61ded804 100644 --- a/router/router.js +++ b/router/router.js @@ -4,7 +4,7 @@ var buildQueryString = require("../querystring/build") var parseQueryString = require("../querystring/parse") module.exports = function($window) { - var supportsPushState = typeof $window.history.pushState === "function" && $window.location.protocol !== "file:" + var supportsPushState = typeof $window.history.pushState === "function" var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout var prefix = "#!" @@ -75,7 +75,7 @@ module.exports = function($window) { var path = getPath() var params = {} var pathname = parsePath(path, params, params) - + callAsync(function() { for (var route in routes) { var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$") diff --git a/router/tests/test-defineRoutes.js b/router/tests/test-defineRoutes.js index 646660d5..9ad33d67 100644 --- a/router/tests/test-defineRoutes.js +++ b/router/tests/test-defineRoutes.js @@ -6,295 +6,297 @@ var pushStateMock = require("../../test-utils/pushStateMock") var Router = require("../../router/router") o.spec("Router.defineRoutes", function() { - void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) { - o.spec("using prefix `" + prefix + "`", function() { - var $window, router, onRouteChange, onFail + void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) { + void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) { + o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() { + var $window, router, onRouteChange, onFail - o.beforeEach(function() { - $window = pushStateMock() - router = new Router($window) - router.setPrefix(prefix) - onRouteChange = o.spy() - onFail = o.spy() - }) + o.beforeEach(function() { + $window = pushStateMock(env) + router = new Router($window) + router.setPrefix(prefix) + onRouteChange = o.spy() + onFail = o.spy() + }) - o("calls onRouteChange on init", function(done) { - $window.location.href = prefix + "/a" - router.defineRoutes({"/a": {data: 1}}, onRouteChange, onFail) + o("calls onRouteChange on init", function(done) { + $window.location.href = prefix + "/a" + router.defineRoutes({"/a": {data: 1}}, onRouteChange, onFail) + + callAsync(function() { + o(onRouteChange.callCount).equals(1) + + done() + }) + }) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - - done() + o("resolves to route", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) + + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - - o("resolves to route", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"]) - o(onFail.callCount).equals(0) - - done() + o("resolves to route w/ escaped unicode", function(done) { + $window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6" + router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail) + + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("resolves to route w/ escaped unicode", function(done) { - $window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6" - router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail) + o("resolves to route w/ unicode", function(done) { + $window.location.href = prefix + "/ö?ö=ö#ö=ö" + router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("resolves to route w/ unicode", function(done) { - $window.location.href = prefix + "/ö?ö=ö#ö=ö" - router.defineRoutes({"/ö": {data: 2}}, onRouteChange, onFail) + o("resolves to route on fallback mode", function(done) { + $window.location.href = "file://" + prefix + "/test" - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 2}, {"ö": "ö"}, "/ö?ö=ö#ö=ö", "/ö"]) - o(onFail.callCount).equals(0) - - done() + router = new Router($window) + router.setPrefix(prefix) + + router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) + + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("resolves to route on fallback mode", function(done) { - $window.location.href = "file://" + prefix + "/test" + o("handles parameterized route", function(done) { + $window.location.href = prefix + "/test/x" + router.defineRoutes({"/test/:a": {data: 1}}, onRouteChange, onFail) - router = new Router($window) - router.setPrefix(prefix) - - router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) - - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {a: "x"}, "/test/x", "/test/:a"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("handles parameterized route", function(done) { - $window.location.href = prefix + "/test/x" - router.defineRoutes({"/test/:a": {data: 1}}, onRouteChange, onFail) + o("handles multi-parameterized route", function(done) { + $window.location.href = prefix + "/test/x/y" + router.defineRoutes({"/test/:a/:b": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {a: "x"}, "/test/x", "/test/:a"]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {a: "x", b: "y"}, "/test/x/y", "/test/:a/:b"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("handles multi-parameterized route", function(done) { - $window.location.href = prefix + "/test/x/y" - router.defineRoutes({"/test/:a/:b": {data: 1}}, onRouteChange, onFail) + o("handles rest parameterized route", function(done) { + $window.location.href = prefix + "/test/x/y" + router.defineRoutes({"/test/:a...": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {a: "x", b: "y"}, "/test/x/y", "/test/:a/:b"]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {a: "x/y"}, "/test/x/y", "/test/:a..."]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("handles rest parameterized route", function(done) { - $window.location.href = prefix + "/test/x/y" - router.defineRoutes({"/test/:a...": {data: 1}}, onRouteChange, onFail) + o("handles route with search", function(done) { + $window.location.href = prefix + "/test?a=b&c=d" + router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {a: "x/y"}, "/test/x/y", "/test/:a..."]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {a: "b", c: "d"}, "/test?a=b&c=d", "/test"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("handles route with search", function(done) { - $window.location.href = prefix + "/test?a=b&c=d" - router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) + o("handles route with hash", function(done) { + $window.location.href = prefix + "/test#a=b&c=d" + router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {a: "b", c: "d"}, "/test?a=b&c=d", "/test"]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {a: "b", c: "d"}, "/test#a=b&c=d", "/test"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("handles route with hash", function(done) { - $window.location.href = prefix + "/test#a=b&c=d" - router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) + o("handles route with search and hash", function(done) { + $window.location.href = prefix + "/test?a=b#c=d" + router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {a: "b", c: "d"}, "/test#a=b&c=d", "/test"]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {a: "b", c: "d"}, "/test?a=b#c=d", "/test"]) + o(onFail.callCount).equals(0) + + done() + }) }) - }) - o("handles route with search and hash", function(done) { - $window.location.href = prefix + "/test?a=b#c=d" - router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) + o("calls reject", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/other": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {a: "b", c: "d"}, "/test?a=b#c=d", "/test"]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onFail.callCount).equals(1) + o(onFail.args).deepEquals(["/test", {}]) + + done() + }) }) - }) - o("calls reject", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/other": {data: 1}}, onRouteChange, onFail) + o("calls reject w/ search and hash", function(done) { + $window.location.href = prefix + "/test?a=b#c=d" + router.defineRoutes({"/other": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onFail.callCount).equals(1) - o(onFail.args).deepEquals(["/test", {}]) - - done() + callAsync(function() { + o(onFail.callCount).equals(1) + o(onFail.args).deepEquals(["/test?a=b#c=d", {a: "b", c: "d"}]) + + done() + }) }) - }) - o("calls reject w/ search and hash", function(done) { - $window.location.href = prefix + "/test?a=b#c=d" - router.defineRoutes({"/other": {data: 1}}, onRouteChange, onFail) + o("handles out of order routes", function(done) { + $window.location.href = prefix + "/z/y/x" + router.defineRoutes({"/z/y/x": {data: 1}, "/:a...": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - o(onFail.callCount).equals(1) - o(onFail.args).deepEquals(["/test?a=b#c=d", {a: "b", c: "d"}]) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"]) + + done() + }) }) - }) - o("handles out of order routes", function(done) { - $window.location.href = prefix + "/z/y/x" - router.defineRoutes({"/z/y/x": {data: 1}, "/:a...": {data: 2}}, onRouteChange, onFail) + o("handles reverse out of order routes", function(done) { + $window.location.href = prefix + "/z/y/x" + router.defineRoutes({"/:a...": {data: 2}, "/z/y/x": {data: 1}}, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"]) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."]) + + done() + }) }) - }) - o("handles reverse out of order routes", function(done) { - $window.location.href = prefix + "/z/y/x" - router.defineRoutes({"/:a...": {data: 2}, "/z/y/x": {data: 1}}, onRouteChange, onFail) + o("handles dynamically added out of order routes", function(done) { + var routes = {} + routes["/z/y/x"] = {data: 1} + routes["/:a..."] = {data: 2} - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."]) - - done() + $window.location.href = prefix + "/z/y/x" + router.defineRoutes(routes, onRouteChange, onFail) + + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"]) + + done() + }) }) - }) - o("handles dynamically added out of order routes", function(done) { - var routes = {} - routes["/z/y/x"] = {data: 1} - routes["/:a..."] = {data: 2} + o("handles reversed dynamically added out of order routes", function(done) { + var routes = {} + routes["/:a..."] = {data: 2} + routes["/z/y/x"] = {data: 1} - $window.location.href = prefix + "/z/y/x" - router.defineRoutes(routes, onRouteChange, onFail) + $window.location.href = prefix + "/z/y/x" + router.defineRoutes(routes, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"]) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."]) + + done() + }) }) - }) - o("handles reversed dynamically added out of order routes", function(done) { - var routes = {} - routes["/:a..."] = {data: 2} - routes["/z/y/x"] = {data: 1} + o("handles mixed out of order routes", function(done) { + var routes = {"/z/y/x": {data: 1}} + routes["/:a..."] = {data: 2} - $window.location.href = prefix + "/z/y/x" - router.defineRoutes(routes, onRouteChange, onFail) + $window.location.href = prefix + "/z/y/x" + router.defineRoutes(routes, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."]) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"]) + + done() + }) }) - }) - o("handles mixed out of order routes", function(done) { - var routes = {"/z/y/x": {data: 1}} - routes["/:a..."] = {data: 2} + o("handles reverse mixed out of order routes", function(done) { + var routes = {"/:a...": {data: 2}} + routes["/z/y/x"] = {data: 12} - $window.location.href = prefix + "/z/y/x" - router.defineRoutes(routes, onRouteChange, onFail) + $window.location.href = prefix + "/z/y/x" + router.defineRoutes(routes, onRouteChange, onFail) - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 1}, {}, "/z/y/x", "/z/y/x"]) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."]) + + done() + }) }) - }) - o("handles reverse mixed out of order routes", function(done) { - var routes = {"/:a...": {data: 2}} - routes["/z/y/x"] = {data: 12} + o("handles non-ascii routes", function(done) { + $window.location.href = prefix + "/ö" + router.defineRoutes({"/ö": "aaa"}, onRouteChange, onFail) - $window.location.href = prefix + "/z/y/x" - router.defineRoutes(routes, onRouteChange, onFail) - - callAsync(function() { - o(onRouteChange.callCount).equals(1) - o(onRouteChange.args).deepEquals([{data: 2}, {a: "z/y/x"}, "/z/y/x", "/:a..."]) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(1) + + done() + }) }) - }) - o("handles non-ascii routes", function(done) { - $window.location.href = prefix + "/ö" - router.defineRoutes({"/ö": "aaa"}, onRouteChange, onFail) + o("replays", function(done) { + $window.location.href = prefix + "/test" + var replay = router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) + replay() - callAsync(function() { - o(onRouteChange.callCount).equals(1) - - done() - }) - }) - - o("replays", function(done) { - $window.location.href = prefix + "/test" - var replay = router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) - replay() - - callAsync(function() { - o(onRouteChange.callCount).equals(2) - o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"]) - o(onFail.callCount).equals(0) - - done() + callAsync(function() { + o(onRouteChange.callCount).equals(2) + o(onRouteChange.args).deepEquals([{data: 1}, {}, "/test", "/test"]) + o(onFail.callCount).equals(0) + + done() + }) }) }) }) diff --git a/router/tests/test-getPath.js b/router/tests/test-getPath.js index 25980404..119c1713 100644 --- a/router/tests/test-getPath.js +++ b/router/tests/test-getPath.js @@ -5,41 +5,43 @@ var pushStateMock = require("../../test-utils/pushStateMock") var Router = require("../../router/router") o.spec("Router.getPath", function() { - void ["#", "?", "", "#!", "?!", '/foo'].forEach(function(prefix) { - o.spec("using prefix `" + prefix + "`", function() { - var $window, router, onRouteChange, onFail + void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) { + void ["#", "?", "", "#!", "?!", '/foo'].forEach(function(prefix) { + o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() { + var $window, router, onRouteChange, onFail - o.beforeEach(function() { - $window = pushStateMock() - router = new Router($window) - router.setPrefix(prefix) - onRouteChange = o.spy() - onFail = o.spy() - }) + o.beforeEach(function() { + $window = pushStateMock(env) + router = new Router($window) + router.setPrefix(prefix) + onRouteChange = o.spy() + onFail = o.spy() + }) - o("gets route", function() { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) + o("gets route", function() { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}}, onRouteChange, onFail) - o(router.getPath()).equals("/test") - }) - o("gets route w/ params", function() { - $window.location.href = prefix + "/other/x/y/z?c=d#e=f" - router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) + o(router.getPath()).equals("/test") + }) + o("gets route w/ params", function() { + $window.location.href = prefix + "/other/x/y/z?c=d#e=f" + router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) - o(router.getPath()).equals("/other/x/y/z?c=d#e=f") - }) - o("gets route w/ escaped unicode", function() { - $window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6" - router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail) + o(router.getPath()).equals("/other/x/y/z?c=d#e=f") + }) + o("gets route w/ escaped unicode", function() { + $window.location.href = prefix + "/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6" + router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail) - o(router.getPath()).equals("/ö?ö=ö#ö=ö") - }) - o("gets route w/ unicode", function() { - $window.location.href = prefix + "/ö?ö=ö#ö=ö" - router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail) + o(router.getPath()).equals("/ö?ö=ö#ö=ö") + }) + o("gets route w/ unicode", function() { + $window.location.href = prefix + "/ö?ö=ö#ö=ö" + router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail) - o(router.getPath()).equals("/ö?ö=ö#ö=ö") + o(router.getPath()).equals("/ö?ö=ö#ö=ö") + }) }) }) }) diff --git a/router/tests/test-setPath.js b/router/tests/test-setPath.js index 2d78ee8f..6b817cfb 100644 --- a/router/tests/test-setPath.js +++ b/router/tests/test-setPath.js @@ -6,150 +6,152 @@ var pushStateMock = require("../../test-utils/pushStateMock") var Router = require("../../router/router") o.spec("Router.setPath", function() { - void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) { - o.spec("using prefix `" + prefix + "`", function() { - var $window, router, onRouteChange, onFail + void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) { + void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) { + o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() { + var $window, router, onRouteChange, onFail - o.beforeEach(function() { - $window = pushStateMock() - router = new Router($window) - router.setPrefix(prefix) - onRouteChange = o.spy() - onFail = o.spy() - }) + o.beforeEach(function() { + $window = pushStateMock(env) + router = new Router($window) + router.setPrefix(prefix) + onRouteChange = o.spy() + onFail = o.spy() + }) - o("setPath calls onRouteChange asynchronously", function(done) { - $window.location.href = prefix + "/a" - router.defineRoutes({"/a": {data: 1}, "/b": {data: 2}}, onRouteChange, onFail) + o("setPath calls onRouteChange asynchronously", function(done) { + $window.location.href = prefix + "/a" + router.defineRoutes({"/a": {data: 1}, "/b": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - router.setPath("/b") - - o(onRouteChange.callCount).equals(1) callAsync(function() { - o(onRouteChange.callCount).equals(2) + router.setPath("/b") + + o(onRouteChange.callCount).equals(1) + callAsync(function() { + o(onRouteChange.callCount).equals(2) + done() + }) + }) + }) + o("setPath calls onFail asynchronously", function(done) { + $window.location.href = prefix + "/a" + router.defineRoutes({"/a": {data: 1}, "/b": {data: 2}}, onRouteChange, onFail) + + callAsync(function() { + router.setPath("/c") + + o(onFail.callCount).equals(0) + callAsync(function() { + o(onFail.callCount).equals(1) + done() + }) + }) + }) + o("sets route via API", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) + + callAsync(function() { + router.setPath("/other/x/y/z?c=d#e=f") + + o(router.getPath()).equals("/other/x/y/z?c=d#e=f") + done() }) }) - }) - o("setPath calls onFail asynchronously", function(done) { - $window.location.href = prefix + "/a" - router.defineRoutes({"/a": {data: 1}, "/b": {data: 2}}, onRouteChange, onFail) + o("sets route w/ escaped unicode", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - router.setPath("/c") - - o(onFail.callCount).equals(0) callAsync(function() { - o(onFail.callCount).equals(1) + router.setPath("/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6") + + o(router.getPath()).equals("/ö?ö=ö#ö=ö") + done() }) }) - }) - o("sets route via API", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) + o("sets route w/ unicode", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - router.setPath("/other/x/y/z?c=d#e=f") + callAsync(function() { + router.setPath("/ö?ö=ö#ö=ö") - o(router.getPath()).equals("/other/x/y/z?c=d#e=f") - - done() + o(router.getPath()).equals("/ö?ö=ö#ö=ö") + + done() + }) }) - }) - o("sets route w/ escaped unicode", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - router.setPath("/%C3%B6?%C3%B6=%C3%B6#%C3%B6=%C3%B6") + o("sets route on fallback mode", function(done) { + $window.location.href = "file://" + prefix + "/test" - o(router.getPath()).equals("/ö?ö=ö#ö=ö") - - done() + router = new Router($window) + router.setPrefix(prefix) + + router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) + + callAsync(function() { + router.setPath("/other/x/y/z?c=d#e=f") + + o(router.getPath()).equals("/other/x/y/z?c=d#e=f") + + done() + }) }) - }) - o("sets route w/ unicode", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}, "/ö/:a/:b...": {data: 2}}, onRouteChange, onFail) + o("sets route via pushState/onpopstate", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - router.setPath("/ö?ö=ö#ö=ö") + callAsync(function() { + $window.history.pushState(null, null, prefix + "/other/x/y/z?c=d#e=f") + $window.onpopstate() - o(router.getPath()).equals("/ö?ö=ö#ö=ö") - - done() + o(router.getPath()).equals("/other/x/y/z?c=d#e=f") + + done() + }) }) - }) + o("sets parameterized route", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) - o("sets route on fallback mode", function(done) { - $window.location.href = "file://" + prefix + "/test" + callAsync(function() { + router.setPath("/other/:a/:b", {a: "x", b: "y/z", c: "d", e: "f"}) - router = new Router($window) - router.setPrefix(prefix) - - router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) - - callAsync(function() { - router.setPath("/other/x/y/z?c=d#e=f") - - o(router.getPath()).equals("/other/x/y/z?c=d#e=f") - - done() + o(router.getPath()).equals("/other/x/y/z?c=d&e=f") + + done() + }) }) - }) - o("sets route via pushState/onpopstate", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) + o("replace:true works", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - $window.history.pushState(null, null, prefix + "/other/x/y/z?c=d#e=f") - $window.onpopstate() + callAsync(function() { + router.setPath("/other", null, {replace: true}) + $window.history.back() - o(router.getPath()).equals("/other/x/y/z?c=d#e=f") - - done() + o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + "/") + + done() + }) }) - }) - o("sets parameterized route", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}, "/other/:a/:b...": {data: 2}}, onRouteChange, onFail) + o("replace:false works", function(done) { + $window.location.href = prefix + "/test" + router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail) - callAsync(function() { - router.setPath("/other/:a/:b", {a: "x", b: "y/z", c: "d", e: "f"}) + callAsync(function() { + router.setPath("/other", null, {replace: false}) + $window.history.back() - o(router.getPath()).equals("/other/x/y/z?c=d&e=f") - - done() - }) - }) - o("replace:true works", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail) + var slash = prefix[0] === "/" ? "" : "/" - callAsync(function() { - router.setPath("/other", null, {replace: true}) - $window.history.back() - - o($window.location.href).equals("http://localhost/") - - done() - }) - }) - o("replace:false works", function(done) { - $window.location.href = prefix + "/test" - router.defineRoutes({"/test": {data: 1}, "/other": {data: 2}}, onRouteChange, onFail) - - callAsync(function() { - router.setPath("/other", null, {replace: false}) - $window.history.back() - - var slash = prefix[0] === "/" ? "" : "/" - - o($window.location.href).equals("http://localhost" + slash + (prefix ? prefix + "/" : "") + "test") - - done() + o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test") + + done() + }) }) }) }) diff --git a/test-utils/pushStateMock.js b/test-utils/pushStateMock.js index a7690c27..3e4ed1f7 100644 --- a/test-utils/pushStateMock.js +++ b/test-utils/pushStateMock.js @@ -2,9 +2,11 @@ var parseURL = require("../test-utils/parseURL") -module.exports = function() { - var protocol = "http:" - var hostname = "localhost" +module.exports = function(options) { + if (options == null) options = {} + + var protocol = options.protocol || "http:" + var hostname = options.hostname || "localhost" var port = "" var pathname = "/" var search = ""