When a controller calls preventDefault on its onunload method, it prevents the current root element from being unmounted via a route change. But it does not prevent it from being unmounted via m.mount(root, null). In that case, the root element is unmounted, but any unloaders that it had keep hanging on the `unloaders` array, and that prevented any future m.mount(root, component) calls from succeeding. This is what was happening on the pending route() tests. The fix is pretty simple: just reset the `unloaders` array when removing the root element, just like it's done when no unloader calls preventDefault. Also, 2 of the 4 pending tests were removed because they were identical to the other 2.
1178 lines
25 KiB
JavaScript
1178 lines
25 KiB
JavaScript
describe("m.route()", function () {
|
|
"use strict"
|
|
|
|
// Use this instead of m.route() unless you have to call m.route and do
|
|
// something else in the same frame.
|
|
function route() {
|
|
var res = m.route.apply(null, arguments)
|
|
mock.requestAnimationFrame.$resolve()
|
|
return res
|
|
}
|
|
|
|
var mode = (function () {
|
|
var types = {
|
|
search: "?",
|
|
hash: "#",
|
|
pathname: "/"
|
|
}
|
|
|
|
return function (type) {
|
|
if (!{}.hasOwnProperty.call(types, type)) {
|
|
throw new RangeError("bad mode type")
|
|
}
|
|
mock.location[type] = types[type]
|
|
m.route.mode = type
|
|
}
|
|
})()
|
|
|
|
// Little helper utility
|
|
function noop() {}
|
|
|
|
// Use this if all you need to do is render a view (i.e. a pure component).
|
|
function pure(view) {
|
|
return {
|
|
controller: noop,
|
|
view: view
|
|
}
|
|
}
|
|
|
|
// Use these instead of `it` and `xit` in this set of tests if you need a
|
|
// root element.
|
|
var dit = makeIt(it)
|
|
var xdit = makeIt(xit)
|
|
|
|
// Wraps the `it` function for dependency injection that doesn't require
|
|
// `this`
|
|
/* eslint-disable no-invalid-this */
|
|
function makeIt(it) {
|
|
return function (name, callback) {
|
|
return it(name, function () {
|
|
var args = [this.root]
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
args.push(arguments[i])
|
|
}
|
|
callback.apply(null, args)
|
|
})
|
|
}
|
|
}
|
|
|
|
beforeEach(function () {
|
|
mock.requestAnimationFrame.$resolve()
|
|
this.root = mock.document.createElement("div")
|
|
})
|
|
|
|
afterEach(function () {
|
|
m.mount(this.root, null)
|
|
})
|
|
/* eslint-enable no-invalid-this */
|
|
|
|
it("exists", function () {
|
|
expect(m.route).to.be.a("function")
|
|
})
|
|
|
|
dit("routes to the right location by default", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/test1", {
|
|
"/test1": pure(function () { return "foo" })
|
|
})
|
|
|
|
expect(mock.location.search).to.equal("?/test1")
|
|
expect(root.childNodes[0].nodeValue).to.equal("foo")
|
|
})
|
|
|
|
dit("gets the right right location when routed to it", function (root) {
|
|
mode("search")
|
|
|
|
var route1, route2
|
|
route(root, "/", {
|
|
"/": {
|
|
controller: function () { route1 = m.route() },
|
|
view: noop
|
|
},
|
|
"/test13": {
|
|
controller: function () { route2 = m.route() },
|
|
view: noop
|
|
}
|
|
})
|
|
|
|
m.route("/test13")
|
|
|
|
expect(route1).to.equal("/")
|
|
expect(route2).to.equal("/test13")
|
|
})
|
|
|
|
dit("skips route change if component ctrl.onunload calls preventDefault", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var spy = sinon.spy()
|
|
|
|
var sub = {
|
|
controller: function () {
|
|
this.onunload = function (e) { e.preventDefault() }
|
|
},
|
|
view: function () {
|
|
return m("div")
|
|
}
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () { return sub }),
|
|
|
|
"/b": {
|
|
controller: spy,
|
|
view: noop
|
|
}
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(spy).to.not.have.been.called
|
|
})
|
|
|
|
dit("skips route change if subcomponent ctrl.onunload calls preventDefault", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
|
|
var spy = sinon.spy()
|
|
|
|
var subsub = {
|
|
controller: function () {
|
|
this.onunload = function (e) { e.preventDefault() }
|
|
},
|
|
view: function () {
|
|
return m("div")
|
|
}
|
|
}
|
|
|
|
var sub = pure(function () { return subsub })
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () { return sub }),
|
|
|
|
"/b": {
|
|
controller: spy,
|
|
view: noop
|
|
}
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(spy).to.not.have.been.called
|
|
})
|
|
|
|
dit("initializes a component's constructor on route change", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
|
|
var ctrl1 = sinon.spy()
|
|
var ctrl2 = sinon.spy()
|
|
|
|
var sub1 = {
|
|
controller: ctrl1,
|
|
view: function () { return m("div") }
|
|
}
|
|
|
|
var sub2 = {
|
|
controller: ctrl2,
|
|
view: function () { return m("div") }
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () {
|
|
return m(".page-a", [
|
|
m("h1"), m.component(sub1, {x: 11})
|
|
])
|
|
}),
|
|
|
|
"/b": pure(function () {
|
|
return m(".page-b", [
|
|
m("h2"), m.component(sub2, {y: 22})
|
|
])
|
|
})
|
|
})
|
|
|
|
route("/b")
|
|
route("/a")
|
|
|
|
expect(ctrl1).to.have.been.calledTwice
|
|
expect(ctrl2).to.have.been.calledOnce
|
|
})
|
|
|
|
dit("doesn't require components to have a view", function (root) {
|
|
mode("search")
|
|
|
|
var Component = pure(function () { return m(".comp") })
|
|
|
|
route(root, "/foo", {
|
|
"/foo": pure(function () { return [Component] })
|
|
})
|
|
|
|
expect(root.childNodes[0].nodeName).to.equal("DIV")
|
|
})
|
|
|
|
// https://github.com/lhorie/mithril.js/issues/555
|
|
dit("reinstantiates the controller when redraw strategy is `all`", function (root) { // eslint-disable-line
|
|
var MyComponent = {
|
|
controller: function (args) {
|
|
this.name = args.name
|
|
},
|
|
|
|
view: function (ctrl) {
|
|
return m("div", ctrl.name)
|
|
}
|
|
}
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () {
|
|
return m("div", [
|
|
m("a[href=/]", {config: m.route}, "foo"),
|
|
m("a[href=/bar]", {config: m.route}, "bar"),
|
|
m.component(MyComponent, {name: "Jane"})
|
|
])
|
|
}),
|
|
|
|
"/bar": pure(function () {
|
|
return m("div", [
|
|
m("a[href=/]", {config: m.route}, "foo"),
|
|
m("a[href=/bar]", {config: m.route}, "bar"),
|
|
m.component(MyComponent, {name: "Bob"})
|
|
])
|
|
})
|
|
})
|
|
|
|
route("/bar")
|
|
|
|
expect(root.childNodes[0].childNodes[2].childNodes[0].nodeValue)
|
|
.to.equal("Bob")
|
|
})
|
|
|
|
dit("sets the correct href with config: m.route", function (root) {
|
|
mode("pathname")
|
|
|
|
route(root, "/test2", {
|
|
"/test2": pure(function () {
|
|
return [
|
|
"foo",
|
|
m("a", {href: "/test2", config: m.route}, "Test2")
|
|
]
|
|
})
|
|
})
|
|
|
|
expect(mock.location.pathname).to.equal("/test2")
|
|
expect(root.childNodes[0].nodeValue).to.equal("foo")
|
|
expect(root.childNodes[1].href).to.equal("/test2")
|
|
})
|
|
|
|
dit("can use a hash", function (root) {
|
|
mode("hash")
|
|
|
|
route(root, "/test3", {
|
|
"/test3": pure(function () { return "foo" })
|
|
})
|
|
|
|
expect(mock.location.hash).to.equal("#/test3")
|
|
expect(root.childNodes[0].nodeValue).to.equal("foo")
|
|
})
|
|
|
|
dit("can use a query", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/test4/foo", {
|
|
"/test4/:test": pure(function () { return m.route.param("test") })
|
|
})
|
|
|
|
expect(mock.location.search).to.equal("?/test4/foo")
|
|
expect(root.childNodes[0].nodeValue).to.equal("foo")
|
|
})
|
|
|
|
context("m.route.param()", function () {
|
|
it("exists", function () {
|
|
expect(m.route.param).to.be.a("function")
|
|
})
|
|
|
|
dit("can get params (1)", function (root) {
|
|
mode("search")
|
|
|
|
var component = pure(function () { return m.route.param("test") })
|
|
|
|
m.route(root, "/test5/foo", {
|
|
"/": component,
|
|
"/test5/:test": component
|
|
})
|
|
|
|
var paramValueBefore = m.route.param("test")
|
|
|
|
mock.requestAnimationFrame.$resolve()
|
|
m.route("/")
|
|
|
|
var paramValueAfter = m.route.param("test")
|
|
|
|
mock.requestAnimationFrame.$resolve()
|
|
|
|
expect(mock.location.search).to.equal("?/")
|
|
expect(paramValueBefore).to.equal("foo")
|
|
expect(paramValueAfter).to.not.exist
|
|
})
|
|
|
|
dit("can deal with params (2)", function (root) {
|
|
mode("search")
|
|
|
|
var component = pure(function () { return m.route.param("a1") })
|
|
|
|
m.route(root, "/test6/foo", {
|
|
"/": component,
|
|
"/test6/:a1": component
|
|
})
|
|
|
|
var paramValueBefore = m.route.param("a1")
|
|
|
|
mock.requestAnimationFrame.$resolve()
|
|
m.route("/")
|
|
|
|
var paramValueAfter = m.route.param("a1")
|
|
|
|
mock.requestAnimationFrame.$resolve()
|
|
|
|
expect(mock.location.search).to.equal("?/")
|
|
expect(paramValueBefore).to.equal("foo")
|
|
expect(paramValueAfter).to.not.exist
|
|
})
|
|
|
|
// https://github.com/lhorie/mithril.js/issues/61
|
|
dit("can get the route via m.route()", function (root) {
|
|
mode("search")
|
|
|
|
var component = pure(function () { return m.route.param("a1") })
|
|
|
|
m.route(root, "/test7/foo", {
|
|
"/": component,
|
|
"/test7/:a1": component
|
|
})
|
|
|
|
var routeValueBefore = m.route()
|
|
|
|
mock.requestAnimationFrame.$resolve()
|
|
m.route("/")
|
|
|
|
var routeValueAfter = m.route()
|
|
|
|
mock.requestAnimationFrame.$resolve()
|
|
|
|
expect(routeValueBefore).to.equal("/test7/foo")
|
|
expect(routeValueAfter).to.equal("/")
|
|
})
|
|
|
|
dit("can deal with rest paths at the end", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/test8/foo/SEP/bar/baz", {
|
|
"/test8/:test/SEP/:path...": pure(function () {
|
|
return m.route.param("test") + "_" + m.route.param("path")
|
|
})
|
|
})
|
|
|
|
expect(mock.location.search).to.equal("?/test8/foo/SEP/bar/baz")
|
|
expect(root.childNodes[0].nodeValue).to.equal("foo_bar/baz")
|
|
})
|
|
|
|
dit("can deal with rest paths in the middle", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/test9/foo/bar/SEP/baz", {
|
|
"/test9/:test.../SEP/:path": pure(function () {
|
|
return m.route.param("test") + "_" + m.route.param("path")
|
|
})
|
|
})
|
|
|
|
expect(mock.location.search).to.equal("?/test9/foo/bar/SEP/baz")
|
|
expect(root.childNodes[0].nodeValue).to.equal("foo/bar_baz")
|
|
})
|
|
|
|
dit("unescapes urls for m.route.param()", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/test10/foo%20bar", {
|
|
"/test10/:test": pure(function () {
|
|
return m.route.param("test")
|
|
})
|
|
})
|
|
|
|
expect(root.childNodes[0].nodeValue).to.equal("foo bar")
|
|
})
|
|
|
|
dit("renders the correct path", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () { return "foo" }),
|
|
"/test11": pure(function () { return "bar" })
|
|
})
|
|
|
|
route("/test11/")
|
|
|
|
expect(mock.location.search).to.equal("?/test11/")
|
|
expect(root.childNodes[0].nodeValue).to.equal("bar")
|
|
})
|
|
|
|
dit("reads params by parsing query string", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": pure(noop),
|
|
"/test12": pure(noop)
|
|
})
|
|
|
|
route("/test12?a=foo&b=bar")
|
|
|
|
expect(mock.location.search).to.equal("?/test12?a=foo&b=bar")
|
|
expect(m.route.param("a")).to.equal("foo")
|
|
expect(m.route.param("b")).to.equal("bar")
|
|
})
|
|
|
|
dit("prefers local params to global params", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () { return "bar" }),
|
|
"/test13/:test": pure(function () {
|
|
return m.route.param("test")
|
|
})
|
|
})
|
|
|
|
route("/test13/foo?test=bar")
|
|
|
|
expect(mock.location.search).to.equal("?/test13/foo?test=bar")
|
|
expect(root.childNodes[0].nodeValue).to.equal("foo")
|
|
})
|
|
|
|
dit("reads global params", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () { return "bar" }),
|
|
"/test14": pure(function () { return "foo" })
|
|
})
|
|
|
|
route("/test14?test&test2=")
|
|
|
|
expect(mock.location.search).to.equal("?/test14?test&test2=")
|
|
expect(m.route.param("test")).to.not.exist
|
|
expect(m.route.param("test2")).to.equal("")
|
|
})
|
|
|
|
dit("parses params when using m.route(path, params)", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": pure(noop),
|
|
"/test12": pure(noop)
|
|
})
|
|
|
|
route("/test12", {a: "foo", b: "bar"})
|
|
|
|
expect(mock.location.search).to.equal("?/test12?a=foo&b=bar")
|
|
expect(m.route.param("a")).to.equal("foo")
|
|
expect(m.route.param("b")).to.equal("bar")
|
|
})
|
|
|
|
dit("gets params object by using m.route.param()", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": pure(noop),
|
|
"/test12": pure(noop)
|
|
})
|
|
|
|
route("/test12", {a: "foo", b: "bar"})
|
|
|
|
var params = m.route.param()
|
|
|
|
expect(params.a).to.equal("foo")
|
|
expect(params.b).to.equal("bar")
|
|
})
|
|
})
|
|
|
|
dit("only calls onunload once when routed away (1)", function (root) {
|
|
mode("search")
|
|
|
|
var onunload = sinon.spy()
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () {
|
|
return m("div", {
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
}),
|
|
"/test14": pure(noop)
|
|
})
|
|
|
|
route("/test14")
|
|
|
|
expect(onunload).to.be.calledOnce
|
|
})
|
|
|
|
dit("only calls onunload once when routed away (2)", function (root) {
|
|
mode("search")
|
|
|
|
var onunload = sinon.spy()
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () {
|
|
return [
|
|
m("div"),
|
|
m("div", {
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
]
|
|
}),
|
|
"/test15": pure(function () { return [m("div")] })
|
|
})
|
|
|
|
route("/test15")
|
|
|
|
expect(onunload).to.be.calledOnce
|
|
})
|
|
|
|
dit("only calls onunload once when routed away (3)", function (root) {
|
|
mode("search")
|
|
|
|
var onunload = sinon.spy()
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () {
|
|
return m("div", {
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
}),
|
|
"/test16": pure(function () { return m("a") })
|
|
})
|
|
|
|
route("/test16")
|
|
|
|
expect(onunload).to.be.calledOnce
|
|
})
|
|
|
|
dit("only calls onunload once when routed away (4)", function (root) {
|
|
mode("search")
|
|
|
|
var onunload = sinon.spy()
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () {
|
|
return [
|
|
m("div", {
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
]
|
|
}),
|
|
"/test17": pure(function () { return m("a") })
|
|
})
|
|
|
|
route("/test17")
|
|
|
|
expect(onunload).to.be.calledOnce
|
|
})
|
|
|
|
dit("only calls onunload once when routed away (5)", function (root) {
|
|
mode("search")
|
|
|
|
var onunload = sinon.spy()
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () {
|
|
return m("div", {
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
}),
|
|
"/test18": pure(function () { return [m("a")] })
|
|
})
|
|
|
|
route("/test18")
|
|
|
|
expect(onunload).to.be.calledOnce
|
|
})
|
|
|
|
dit("only calls onunload once when routed away (6)", function (root) {
|
|
mode("search")
|
|
|
|
var onunload = sinon.spy()
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () {
|
|
return [
|
|
m("div", {
|
|
key: 1,
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
]
|
|
}),
|
|
"/test20": pure(function () {
|
|
return [
|
|
m("div", {
|
|
key: 2,
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
]
|
|
})
|
|
})
|
|
|
|
route("/test20")
|
|
|
|
expect(onunload).to.be.calledOnce
|
|
})
|
|
|
|
dit("only calls onunload once when routed away (7)", function (root) {
|
|
mode("search")
|
|
|
|
var onunload = sinon.spy()
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () {
|
|
return [
|
|
m("div", {
|
|
key: 1,
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
]
|
|
}),
|
|
"/test21": pure(function () {
|
|
return [
|
|
m("div", {
|
|
config: function (el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
})
|
|
]
|
|
})
|
|
})
|
|
|
|
route("/test21")
|
|
|
|
expect(onunload).to.be.calledOnce
|
|
})
|
|
|
|
dit("renders the right virtual node when routed to it", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/foo", {
|
|
"/foo": pure(function () { return m("div", "foo") }),
|
|
"/bar": pure(function () { return m("div", "bar") })
|
|
})
|
|
var foo = root.childNodes[0].childNodes[0].nodeValue
|
|
|
|
route("/bar")
|
|
var bar = root.childNodes[0].childNodes[0].nodeValue
|
|
|
|
expect(foo).to.equal("foo")
|
|
expect(bar).to.equal("bar")
|
|
})
|
|
|
|
dit("keeps identity with unchanged nodes", function (root) {
|
|
mode("search")
|
|
|
|
var onunload = sinon.spy()
|
|
function config(el, init, ctx) {
|
|
ctx.onunload = onunload
|
|
}
|
|
|
|
route(root, "/foo1", {
|
|
"/foo1": pure(function () {
|
|
return m("div", m("a", {config: config}, "foo"))
|
|
}),
|
|
"/bar1": pure(function () {
|
|
return m("main", m("a", {config: config}, "foo"))
|
|
})
|
|
})
|
|
|
|
route("/bar1")
|
|
|
|
expect(onunload).to.be.calledOnce
|
|
})
|
|
|
|
dit("allows illegal URL characters in paths", function (root) {
|
|
mode("search")
|
|
|
|
var value
|
|
m.route(root, "/foo+bar", {
|
|
"/:arg": {
|
|
controller: function () { value = m.route.param("arg") },
|
|
view: function () {
|
|
return ""
|
|
}
|
|
}
|
|
})
|
|
expect(value).to.equal("foo+bar")
|
|
})
|
|
|
|
dit("allows trailing slashes in paths", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () { return "foo" }),
|
|
"/test22": pure(function () { return "bar" })
|
|
})
|
|
|
|
m.route("/test22/")
|
|
|
|
expect(mock.location.search).to.equal("?/test22/")
|
|
expect(root.childNodes[0].nodeValue).to.equal("bar")
|
|
})
|
|
|
|
dit("reads non-primitive String objects in route changes", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": pure(function () { return "foo" }),
|
|
"/test23": pure(function () { return "bar" })
|
|
})
|
|
|
|
route(new String("/test23/")) // eslint-disable-line no-new-wrappers
|
|
|
|
expect(mock.location.search).to.equal("?/test23/")
|
|
expect(root.childNodes[0].nodeValue).to.equal("bar")
|
|
})
|
|
|
|
dit("reads primitive Strings in default routes", function (root) {
|
|
mode("search")
|
|
|
|
var value
|
|
m.route(root, "/foo+bar", {
|
|
"/:arg": {
|
|
controller: function () { value = m.route.param("arg") },
|
|
view: function () {
|
|
return ""
|
|
}
|
|
}
|
|
})
|
|
expect(value).to.equal("foo+bar")
|
|
})
|
|
|
|
dit("reads non-primitive Strings in default routes", function (root) {
|
|
mode("search")
|
|
|
|
var value
|
|
m.route(root, new String("/foo+bar"), { // eslint-disable-line
|
|
"/:arg": {
|
|
controller: function () { value = m.route.param("arg") },
|
|
view: function () {
|
|
return ""
|
|
}
|
|
}
|
|
})
|
|
|
|
expect(value).to.equal("foo+bar")
|
|
})
|
|
|
|
dit("can redirect to another route while loading default", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
|
|
route(root, "/a", {
|
|
"/a": {
|
|
controller: function () { m.route("/b") },
|
|
view: function () { return "a" }
|
|
},
|
|
|
|
"/b": pure(function () { return "b" })
|
|
})
|
|
|
|
expect(root.childNodes[0].nodeValue).to.equal("b")
|
|
})
|
|
|
|
dit("can redirect to another route with params while loading default", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
|
|
route(root, "/", {
|
|
"/": {
|
|
controller: function () {
|
|
m.route("/b?foo=1", {foo: 2})
|
|
},
|
|
view: function () { return "a" }
|
|
},
|
|
"/b": pure(function () { return "b" })
|
|
})
|
|
|
|
expect(mock.location.search).to.equal("?/b?foo=2")
|
|
})
|
|
|
|
dit("modifies history when changing route", function (root) {
|
|
mode("search")
|
|
mock.history.$$length = 0
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () { return "a" }),
|
|
"/b": pure(function () { return "b" })
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(mock.history.$$length).to.equal(1)
|
|
})
|
|
|
|
dit("doesn't modify history when redirecting to same route", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
mock.history.$$length = 0
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () { return "a" }),
|
|
"/b": pure(function () { return "b" })
|
|
})
|
|
|
|
route("/a")
|
|
|
|
expect(mock.history.$$length).to.equal(0)
|
|
})
|
|
|
|
context("m.route.strategy() === \"all\", identical views", function () {
|
|
context("parent nodes", function () {
|
|
dit("renders routes independently", function (root) {
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
var a = pure(function () {
|
|
return m("a", {
|
|
config: function (el, init) {
|
|
if (!init) initCount++
|
|
}
|
|
})
|
|
})
|
|
|
|
route(root, "/a", {
|
|
"/a": a,
|
|
"/b": pure(a.view)
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(2)
|
|
})
|
|
|
|
dit("renders routes independently with `context.retain === false`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
var a = pure(function () {
|
|
return m("a", {
|
|
config: function (el, init, ctx) {
|
|
ctx.retain = false
|
|
if (!init) initCount++
|
|
}
|
|
})
|
|
})
|
|
|
|
route(root, "/a", {
|
|
"/a": a,
|
|
"/b": pure(a.view)
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(2)
|
|
})
|
|
|
|
dit("renders routes independently with `context.retain === true`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
var a = pure(function () {
|
|
return m("a", {
|
|
config: function (el, init, ctx) {
|
|
ctx.retain = true
|
|
if (!init) initCount++
|
|
}
|
|
})
|
|
})
|
|
|
|
route(root, "/a", {
|
|
"/a": a,
|
|
"/b": pure(a.view)
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(1)
|
|
})
|
|
})
|
|
|
|
context("child nodes", function () {
|
|
dit("reinitialize unchanged child nodes without `context.retain`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
function config(el, init) {
|
|
if (!init) initCount++
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () {
|
|
return m("div", m("a", {config: config}))
|
|
}),
|
|
"/b": pure(function () {
|
|
return m("section", m("a", {config: config}))
|
|
})
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(2)
|
|
})
|
|
|
|
dit("doesn't reinitialize unchanged child nodes with `context.retain === true`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
function config(el, init, ctx) {
|
|
ctx.retain = true
|
|
if (!init) initCount++
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () {
|
|
return m("div", m("a", {config: config}))
|
|
}),
|
|
"/b": pure(function () {
|
|
return m("section", m("a", {config: config}))
|
|
})
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(1)
|
|
})
|
|
|
|
dit("reinitializes unchanged child nodes with `context.retain === false`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
function config(el, init, ctx) {
|
|
ctx.retain = false
|
|
if (!init) initCount++
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () {
|
|
return m("div", m("a", {config: config}))
|
|
}),
|
|
"/b": pure(function () {
|
|
return m("section", m("a", {config: config}))
|
|
})
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(2)
|
|
})
|
|
})
|
|
})
|
|
|
|
context("m.route.strategy() === \"diff\"", function () {
|
|
function diff(view) {
|
|
return {
|
|
controller: function () { m.redraw.strategy("diff") },
|
|
view: view
|
|
}
|
|
}
|
|
|
|
context("parent nodes", function () {
|
|
dit("renders routes independently", function (root) {
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
var a = diff(function () {
|
|
return m("a", {
|
|
config: function (el, init) {
|
|
if (!init) initCount++
|
|
}
|
|
})
|
|
})
|
|
|
|
route(root, "/a", {
|
|
"/a": a,
|
|
"/b": diff(a.view)
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(1)
|
|
})
|
|
|
|
dit("renders routes independently with `context.retain === false`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
var a = diff(function () {
|
|
return m("a", {
|
|
config: function (el, init, ctx) {
|
|
ctx.retain = true
|
|
if (!init) initCount++
|
|
}
|
|
})
|
|
})
|
|
|
|
route(root, "/a", {
|
|
"/a": a,
|
|
"/b": diff(a.view)
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(1)
|
|
})
|
|
|
|
dit("renders routes independently with `context.retain === true`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
var a = diff(function () {
|
|
return m("a", {
|
|
config: function (el, init, ctx) {
|
|
ctx.retain = false
|
|
if (!init) initCount++
|
|
}
|
|
})
|
|
})
|
|
|
|
route(root, "/a", {
|
|
"/a": a,
|
|
"/b": diff(a.view)
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(2)
|
|
})
|
|
})
|
|
|
|
context("child nodes", function () {
|
|
dit("reinitialize unchanged child nodes without `context.retain`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
function config(el, init) {
|
|
if (!init) initCount++
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": diff(function () {
|
|
return m("div", m("a", {config: config}))
|
|
}),
|
|
"/b": diff(function () {
|
|
return m("section", m("a", {config: config}))
|
|
})
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(1)
|
|
})
|
|
|
|
dit("doesn't reinitialize unchanged child nodes with `context.retain === true`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
function config(el, init, ctx) {
|
|
ctx.retain = true
|
|
if (!init) initCount++
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": diff(function () {
|
|
return m("div", m("a", {config: config}))
|
|
}),
|
|
"/b": diff(function () {
|
|
return m("section", m("a", {config: config}))
|
|
})
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(1)
|
|
})
|
|
|
|
dit("reinitializes unchanged child nodes with `context.retain === false`", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
function config(el, init, ctx) {
|
|
ctx.retain = false
|
|
if (!init) initCount++
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": diff(function () {
|
|
return m("div", m("a", {config: config}))
|
|
}),
|
|
"/b": diff(function () {
|
|
return m("section", m("a", {config: config}))
|
|
})
|
|
})
|
|
|
|
m.route("/b")
|
|
|
|
expect(initCount).to.equal(2)
|
|
})
|
|
})
|
|
})
|
|
|
|
dit("honors retain flag inside child components during route change", function (root) { // eslint-disable-line
|
|
mode("search")
|
|
var initCount = 0
|
|
|
|
function config(el, init, ctx) {
|
|
ctx.retain = true
|
|
if (!init) initCount++
|
|
}
|
|
|
|
var a = pure(function () {
|
|
return m("div", m("a", {config: config}))
|
|
})
|
|
|
|
var b = {
|
|
controller: function () { m.redraw.strategy("diff") },
|
|
view: function () {
|
|
return m("section", m("a", {config: config}))
|
|
}
|
|
}
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () { return m("div", a) }),
|
|
"/b": pure(function () { return m("div", b) })
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(initCount).to.equal(1)
|
|
})
|
|
|
|
// https://github.com/lhorie/mithril.js/pull/571
|
|
dit("clears nodes with config on route change", function (root) {
|
|
mode("search")
|
|
|
|
route(root, "/a", {
|
|
"/a": pure(function () {
|
|
return m("div", {
|
|
config: function (el) {
|
|
el.childNodes[0].modified = true
|
|
}
|
|
}, m("div"))
|
|
}),
|
|
"/b": pure(function () { return m("div", m("div")) })
|
|
})
|
|
|
|
route("/b")
|
|
|
|
expect(root.childNodes[0].childNodes[0])
|
|
.to.not.have.property("modified")
|
|
})
|
|
})
|