Details: 1. All tests now live in `test`. All test dependencies that aren't from npm live in `test-deps`. 2. The QUnit tests are gone, as well as their dependencies. Half of them duplicated existing tests, and some of them depended on the real DOM to properly test. 3. All tests are now using Mocha to run the tests, Chai for assertions, and Sinon and Sinon Chai for testing some callbacks. 4. Tests are run through mocha-phantomjs. If you want to run just the tests, run `grunt mocha_phantomjs` or fire up a server in the root and open `http://localhost:<port>/test/index.html`, e.g. `python3 -m http.server`. 5. The linter I chose is ESLint. It is relatively easy to configure, but with a lot of flexibility. The rules I chose mostly were in tune to the style the project was already using. I'm not including a style guide in this commit, but one will likely come. You can check out the `.eslintrc` in the root and in `test/` for the two configs. The `.eslintignore` includes a TODO for `mithril.js` itself targeted at me, in the root. Other info: - As a drive-by fix, I fixed line endings on a few of the files. - I also took care of a few other files and linted them as I went: - `Gruntfile.js` - `test/input-cursor.html` (was in `tests/`) - `test/svg.html` (was in `tests/`) - `docs/layout/tools/template-converter.html` - `docs/layout/tools/template-converter.js` I didn't test the template converter after linting it, because it needs further scrutiny to ensure it works with the latest version of Mithril. I know the API has changed a little, which is why I want to be sure. - I simplified the `.travis.yml` file because none of the tests are run directly through Node anymore. They are always run in a browser of some kind. Hopefully, this turned out all right...
1240 lines
26 KiB
JavaScript
1240 lines
26 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")
|
|
})
|
|
|
|
// FIXME: this causes others to fail
|
|
xdit("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
|
|
})
|
|
|
|
// FIXME: this causes others to fail
|
|
xdit("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
|
|
})
|
|
|
|
// FIXME: this causes others to fail
|
|
xdit("skips route change if non-curried 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
|
|
})
|
|
|
|
// FIXME: this causes others to fail
|
|
xdit("skips route change if non-curried 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")
|
|
})
|
|
})
|