clean slate

This commit is contained in:
Leo Horie 2016-04-20 20:01:04 -04:00
parent 5d2b6ea2b7
commit 13fdb60f66
377 changed files with 0 additions and 71900 deletions

View file

@ -1,18 +0,0 @@
{
"extends": "../.eslintrc",
"env": {
"mocha": true
},
"globals": {
"sinon": false,
"expect": false,
"mock": false,
"dom": false,
"m": false
},
"rules": {
"max-statements": 0,
"no-unused-expressions": 0,
"no-warning-comments": 0
}
}

View file

@ -1,73 +0,0 @@
<!doctype html>
<meta charset="utf-8">
<title>Mithril test suite</title>
<div id="mocha"></div>
<!-- Dependencies -->
<script src="../node_modules/chai/chai.js"></script>
<script src="../node_modules/sinon/pkg/sinon.js" charset="utf-8"></script>
<script src="../node_modules/sinon-chai/lib/sinon-chai.js" charset="utf-8"></script>
<link rel="stylesheet" href="../node_modules/mocha/mocha.css">
<script src="../node_modules/mocha/mocha.js"></script>
<script src="../test-deps/dom.js"></script>
<script src="../test-deps/mock.js"></script>
<script src="../mithril.js"></script>
<script>
mocha.setup("bdd")
var expect = chai.expect
// Temporary workaround for https://github.com/mochajs/mocha/issues/1348
chai.config.truncateThreshold = 0
</script>
<!-- Tests go here -->
<script src="./mithril.js"></script>
<script src="./mithril.mount.js"></script>
<script src="./mithril.render.js"></script>
<script src="./mithril.withAttr.js"></script>
<script src="./mithril.trust.js"></script>
<script src="./mithril.redraw.js"></script>
<script src="./mithril.route.js"></script>
<script src="./mithril.route.parseQueryString.js"></script>
<script src="./mithril.route.buildQueryString.js"></script>
<script src="./mithril.prop.js"></script>
<script src="./mithril.request.js"></script>
<script src="./mithril.deferred.js"></script>
<script src="./mithril.sync.js"></script>
<script src="./mithril.startComputation.js"></script>
<!-- Set options and run this thing -->
<script>
m.deps(mock)
mocha.checkLeaks()
mocha.globals(["m", "mochaResults"])
// For Saucelabs reporting
var runner = mocha.run()
var failedTests = []
runner.on("end", function() {
window.mochaResults = runner.stats
window.mochaResults.reports = failedTests
})
runner.on("fail", function (test, err) {
function flattenTitles(test) {
var titles = []
while (test.parent.title) {
titles.push(test.parent.title)
test = test.parent
}
return titles.reverse()
}
failedTests.push({
name: test.title,
result: false,
message: err.message,
stack: err.stack,
titles: flattenTitles(test),
})
})
</script>

View file

@ -1,73 +0,0 @@
<!doctype html>
<title>Input cursor test</title>
<h2>Things to check:</h2>
<ul>
<li>
Typing in the fields below should not move the cursor to the end of the
input. Especially in Chrome.
</li>
<li>All inputs should update with the same value.</li>
<li>
Typing in an input should not prevent it from being updated by other
inputs.
</li>
</ul>
<div id="test"></div>
<script src="../mithril.js"></script>
<script>
function test(sel) {
return m("li", [
m("p", m("code", "m(" + JSON.stringify(sel) + ")")),
m.apply(null, arguments)
])
}
m.module(document.getElementById("test"), {
controller: function() {
this.title = m.prop("hello world");
},
view: function (ctrl) {
return m("body", [
m("h1", ["Title: ", ctrl.title()]),
m("ul", [
test("input[list=data]", {
onkeyup: m.withAttr("value", ctrl.title),
value: ctrl.title()
}),
test("datalist#data", [
m("option", "John"),
m("option", "Bob"),
m("option", "Mary")
]),
test("textarea", {
onkeyup: m.withAttr("value", ctrl.title),
value: ctrl.title()
}),
m("li", [
m("p", "untrusted ",
m("code", "m(\"div[contenteditable]\")")),
m("div[contenteditable]", {
style: {border: "1px solid #888"},
onkeyup: m.withAttr("innerHTML", ctrl.title)
}, ctrl.title()),
]),
m("li", [
m("p", "trusted ",
m("code", "m(\"div[contenteditable]\")")),
m("div[contenteditable]", {
style: {border: "1px solid #888"},
onkeyup: m.withAttr("innerHTML", ctrl.title)
}, m.trust(ctrl.title())),
])
])
]);
}
});
</script>

View file

@ -1,8 +0,0 @@
<!doctype html>
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.0.3/es5-shim.min.js"></script>-->
<script src="test.js"></script>
<script src="mock.js"></script>
<script src="../mithril.js"></script>
<p>Open the console to see the test report</p>
<script src="./isolation-test.js"></script>

View file

@ -1,159 +0,0 @@
/* global m, test, mock */
(function () {
"use strict"
m.deps(mock.window)
test(function () { // eslint-disable-line max-statements
var root = mock.document.createElement("div")
var retain = false
var flag = true
var loaded1 = null
var loaded2 = null
var loaded1a = null
var loaded2a = null
var Comp1 = {
controller: function () {
loaded1 = true
this.onunload = function () {
loaded1 = false
}
},
view: function () {
if (retain) {
return {subtree: "retain"}
} else {
return m("a", {
config: function (el, init, ctx) {
if (!init) {
loaded1a = true
ctx.onunload = function () {
loaded1a = false
}
}
}
})
}
}
}
var Comp2 = {
controller: function () {
loaded2 = true
this.onunload = function () {
loaded2 = false
}
},
view: function () {
if (retain) {
return {subtree: "retain"}
} else {
return m("b", {
config: function (el, init, ctx) {
if (!init) {
loaded2a = true
ctx.onunload = function () {
loaded2a = false
}
}
}
})
}
}
}
var Root = {
view: function () {
return flag ? Comp1 : Comp2
}
}
m.mount(root, Root)
mock.requestAnimationFrame.$resolve()
// loaded 1
var result1 = loaded1 === true &&
loaded2 === null &&
loaded1a === true &&
loaded2a === null
retain = true
m.redraw(true)
mock.requestAnimationFrame.$resolve()
// retained
var result2 = loaded1 === true &&
loaded2 === null &&
loaded1a === true &&
loaded2a === null
flag = false
m.redraw(true)
mock.requestAnimationFrame.$resolve()
// loaded 2 while retained: both controllers are alive at the same time
// because dom element is retained
var result3 = loaded1 === true &&
loaded2 === true &&
loaded1a === true &&
loaded2a === null
retain = false
m.redraw(true)
mock.requestAnimationFrame.$resolve()
// unretained, i.e. 2 is now dynamic
var result4 = loaded1 === false &&
loaded2 === true &&
loaded1a === false &&
loaded2a === true
flag = true
m.redraw(true)
mock.requestAnimationFrame.$resolve()
// loaded 1 while dynamic
var result5 = loaded1 === true &&
loaded2 === false &&
loaded1a === true &&
loaded2a === false
return result1 && result2 && result3 && result4 && result5
})
/*
test(function() {
var root = mock.document.createElement("div")
var redraws = 0, data
var Root = {
view: function() {
return Comp
}
}
var Comp = {
controller: function() {
this.foo = m.request({method: "GET", url: "/foo"})
},
view: function(ctrl) {
redraws++
data = ctrl.foo()
return m("div")
}
}
m.mount(root, Root)
mock.requestAnimationFrame.$resolve()
mock.XMLHttpRequest.$instances.pop().onreadystatechange()
return redraws == 1 && data.url == "/foo"
})
*/
test.print(function (value) {
console.log(value) // eslint-disable-line no-console
})
})()

View file

@ -1,311 +0,0 @@
describe("m.deferred()", function () {
"use strict"
// Let unchecked exceptions bubble up in order to allow meaningful error
// messages in common cases like null reference exceptions due to typos.
// An unchecked exception is defined as an object that is a subclass of
// Error (but not a direct instance of Error itself) - basically anything
// that can be thrown without an explicit `throw` keyword and that we'd
// never want to programmatically manipulate. In other words, an unchecked
// error is one where we only care about its line number and where the only
// reasonable way to deal with it is to change the buggy source code that
// caused the error to be thrown in the first place.
//
// By contrast, a checked exception is defined as anything that is
// explicitly thrown via the `throw` keyword and that can be
// programmatically handled, for example to display a validation error
// message on the UI. If an exception is a subclass of Error for whatever
// reason, but it is meant to be handled as a checked exception (i.e.
// follow the rejection rules for A+), it can be rethrown as an instance
// of Error.
//
// This implementation deviates from the Promises/A+ spec in two ways:
//
// 1) A+ requires the `then` callback to be called asynchronously (this
// requires a setImmediate polyfill, which cannot be implemented in a
// reasonable way for Mithril's purpose - the possible polyfills are
// either too big or too slow). This implementation calls the `then`
// callback synchronously.
// 2) A+ swallows exceptions in a unrethrowable way, i.e. it's not possible
// to see default error messages on the console for runtime errors thrown
// from within a promise chain. This throws such checked exceptions.
it("exists", function () {
expect(m.deferred).to.be.a("function")
})
it("resolves values", function () {
var value = m.prop()
var deferred = m.deferred()
deferred.promise.then(value)
deferred.resolve("test")
expect(value()).to.equal("test")
})
it("resolves values returned in `then` method", function () {
var value = m.prop()
var deferred = m.deferred()
deferred.promise
.then(function () { return "foo" })
.then(value)
deferred.resolve("test")
expect(value()).to.equal("foo")
})
it("passes rejections through second `then` handler", function () {
var obj = {}
var value1 = m.prop(obj)
var value2 = m.prop(obj)
var deferred = m.deferred()
deferred.promise.then(value1, value2)
deferred.reject("test")
expect(value1()).to.equal(obj)
expect(value2()).to.equal("test")
})
it("passes rejections through `catch`", function () {
var value = m.prop()
var deferred = m.deferred()
deferred.promise.catch(value)
deferred.reject("test")
expect(value()).to.equal("test")
})
it("can resolve from a `then` rejection handler", function () {
var value = m.prop()
var deferred = m.deferred()
deferred.promise
.then(null, function () { return "foo" })
.then(value)
deferred.reject("test")
expect(value()).to.equal("foo")
})
it("can resolve from a `catch`", function () {
var value = m.prop()
var deferred = m.deferred()
deferred.promise
.catch(function () { return "foo" })
.then(value)
deferred.reject("test")
expect(value()).to.equal("foo")
})
it("can reject by throwing an `Error`", function () {
var value1 = m.prop()
var value2 = m.prop()
var deferred = m.deferred()
deferred.promise
.then(function () { throw new Error() })
.then(value1, value2)
deferred.resolve("test")
expect(value1()).to.not.exist
expect(value2()).to.be.an("error")
})
it("synchronously throws subclasses of Errors on creation", function () {
expect(function () {
m.deferred().reject(new TypeError())
}).to.throw()
})
it("synchronously throws subclasses of Errors thrown from its `then` fufill handler", function () { // eslint-disable-line
expect(function () {
var deferred = m.deferred()
deferred.promise.then(function () { throw new TypeError() })
deferred.resolve()
}).to.throw()
})
it("synchronously throws subclasses of Errors thrown from its `then` rejection handler", function () { // eslint-disable-line
expect(function () {
var deferred = m.deferred()
deferred.promise.then(null, function () { throw new TypeError() })
deferred.reject("test")
}).to.throw()
})
it("synchronously throws subclasses of Errors thrown from its `catch` method", function () { // eslint-disable-line
expect(function () {
var deferred = m.deferred()
deferred.promise.catch(function () { throw new TypeError() })
deferred.reject("test")
}).to.throw()
})
it("unwraps other thenables, and returns the correct values in the chain", function () { // eslint-disable-line
var deferred1 = m.deferred()
var deferred2 = m.deferred()
var value1, value2
deferred1.promise.then(function (data) {
value1 = data
return deferred2.promise
}).then(function (data) {
value2 = data
})
deferred1.resolve(1)
deferred2.resolve(2)
expect(value1).to.equal(1)
expect(value2).to.equal(2)
})
// https://github.com/lhorie/mithril.js/issues/80
it("propogates returns with `then` after being resolved", function () {
var deferred = m.deferred()
var value = m.prop()
deferred.resolve(1)
deferred.promise.then(value)
expect(value()).to.equal(1)
})
// https://github.com/lhorie/mithril.js/issues/80
it("propogates errors with `then` after being rejected", function () {
var deferred = m.deferred()
var value = m.prop()
deferred.reject(1)
deferred.promise.then(null, value)
expect(value()).to.equal(1)
})
// https://github.com/lhorie/mithril.js/issues/80
it("can only be resolved once before being chained", function () {
var deferred = m.deferred()
var value = m.prop()
deferred.resolve(1)
deferred.resolve(2)
deferred.promise.then(value)
expect(value()).to.equal(1)
})
// https://github.com/lhorie/mithril.js/issues/80
it("can only be resolved once after being chained", function () {
var deferred = m.deferred()
var value = m.prop()
deferred.promise.then(value)
deferred.resolve(1)
deferred.resolve(2)
expect(value()).to.equal(1)
})
// https://github.com/lhorie/mithril.js/issues/80
it("can't be rejected after being resolved", function () {
var deferred = m.deferred()
var value1 = m.prop()
var value2 = m.prop()
deferred.promise.then(value1, value2)
deferred.resolve(1)
deferred.reject(2)
expect(value1()).to.equal(1)
expect(value2()).to.not.exist
})
// https://github.com/lhorie/mithril.js/issues/80
it("can't be resolved after being rejected", function () {
var deferred = m.deferred()
var value1 = m.prop()
var value2 = m.prop()
deferred.promise.then(value1, value2)
deferred.reject(1)
deferred.resolve(2)
expect(value1()).to.not.exist
expect(value2()).to.equal(1)
})
// https://github.com/lhorie/mithril.js/issues/80
it("can only be rejected once before being chained", function () {
var deferred = m.deferred()
var value = m.prop()
deferred.reject(1)
deferred.reject(2)
deferred.promise.then(null, value)
expect(value()).to.equal(1)
})
// https://github.com/lhorie/mithril.js/issues/80
it("can only be rejected once after being chained", function () {
var deferred = m.deferred()
var value = m.prop()
deferred.promise.then(null, value)
deferred.reject(1)
deferred.reject(2)
expect(value()).to.equal(1)
})
// https://github.com/lhorie/mithril.js/issues/85
it("calls resolution handler when resolved with `undefined`", function () {
var deferred = m.deferred()
var value
deferred.resolve()
deferred.promise.then(function () {
value = 1
})
expect(value).to.equal(1)
})
// https://github.com/lhorie/mithril.js/issues/85
it("calls rejection handler when rejected with `undefined`", function () {
var deferred = m.deferred()
var value
deferred.reject()
deferred.promise.then(null, function () {
value = 1
})
expect(value).to.equal(1)
})
it("immediately resolves promise with `resolve` method", function () {
var deferred = m.deferred()
deferred.resolve(1)
expect(deferred.promise()).to.equal(1)
})
it("gets chained promise value when called", function () {
var deferred = m.deferred()
var promise = deferred.promise.then(function (data) { return data + 1 })
deferred.resolve(1)
expect(promise()).to.equal(2)
})
it("returns `undefined` from call if it's rejected", function () {
var deferred = m.deferred()
deferred.reject(1)
expect(deferred.promise()).to.be.undefined
})
it("resolves to value of returned promise", function () {
var prmA = m.deferred()
var prmB = m.deferred()
prmA.resolve("A")
prmB.resolve("B")
prmA.promise.then(function () {
return prmB.promise
}).then(function (B) {
expect(B).to.equal("B")
})
})
it("yields immutable promises", function () {
var d = m.deferred()
d.resolve(5)
d.resolve(6)
d.promise.then(function (v) {
expect(v).to.equal(5)
})
})
})

View file

@ -1,220 +0,0 @@
describe("m.version()", function () {
"use strict"
it("exists", function () {
expect(m.version).to.be.a("function")
})
it("is a string", function () {
expect(m.version()).to.be.a("string")
})
})
describe("m()", function () {
"use strict"
it("exists", function () {
expect(m).to.be.a("function")
})
it("sets correct tag name", function () {
expect(m("div")).to.have.property("tag", "div")
})
it("sets correct tag name with only a class", function () {
expect(m(".foo")).to.have.property("tag", "div")
})
it("sets correct class name", function () {
expect(m(".foo")).to.have.deep.property("attrs.className", "foo")
})
it("sets correct tag name with only an attr", function () {
expect(m("[title=bar]")).to.have.property("tag", "div")
})
it("sets correct unquoted attr", function () {
expect(m("[title=bar]")).to.have.deep.property("attrs.title", "bar")
})
it("sets attr without a value as an empty string", function () {
expect(m("[empty]")).to.have.deep.property("attrs.empty", "")
})
it("sets correct single quoted attr", function () {
expect(m("[title=\'bar\']")).to.have.deep.property("attrs.title", "bar")
})
it("sets correct double quoted attr", function () {
expect(m("[title=\"bar\"]")).to.have.deep.property("attrs.title", "bar")
})
it("sets correct children with 1 string arg", function () {
expect(m("div", "test"))
.to.have.property("children")
.that.eqls(["test"])
})
it("sets correct children with multiple string args", function () {
expect(m("div", "test", "test2"))
.to.have.property("children")
.that.eqls(["test", "test2"])
})
it("sets correct children with string array", function () {
expect(m("div", ["test"]))
.to.have.property("children")
.that.eqls(["test"])
})
it("sets correct attrs with object", function () {
expect(m("div", {title: "bar"}, "test"))
.to.have.deep.property("attrs.title", "bar")
})
it("sets correct children with attrs object", function () {
expect(m("div", {title: "bar"}, "test"))
.to.have.property("children")
.that.eqls(["test"])
})
it("sets correct children with nested node", function () {
expect(m("div", {title: "bar"}, m("div")))
.to.have.property("children")
.that.eqls([m("div")])
})
it("sets correct children with string rest arg", function () {
expect(m("div", {title: "bar"}, "test0", "test1", "test2", "test3"))
.to.have.property("children")
.that.eqls(["test0", "test1", "test2", "test3"])
})
it("sets correct children with node rest arg", function () {
expect(m("div", {title: "bar"}, m("div"), m("i"), m("span")))
.to.have.property("children")
.that.eqls([m("div"), m("i"), m("span")])
})
it("sets correct children with string array & no attrs", function () {
expect(m("div", ["a", "b"]))
.to.have.property("children")
.that.eqls(["a", "b"])
})
it("sets correct children with node array & no attrs", function () {
expect(m("div", [m("div"), m("i")]))
.to.have.property("children")
.that.eqls([m("div"), m("i")])
})
it("sets correct children with 2nd arg as node", function () {
expect(m("div", m("div"))).to.have.property("children")
.that.eqls([m("div")])
})
it("sets correct tag with undefined array entry", function () {
expect(m("div", [undefined])).to.have.property("tag", "div")
})
it("loosely accepts invalid objects", function () {
expect(function () { m("div", [{foo: "bar"}]) }).to.not.throw()
})
it("accepts svg nodes", function () {
expect(m("svg", [m("g")]))
.to.have.property("children")
.that.eqls([m("g")])
})
it("accepts HTML children in svg element", function () {
expect(m("svg", [m("a[href='http://google.com']")]))
.to.have.property("children")
.that.eqls([m("a[href='http://google.com']")])
})
it("uses className if given", function () {
expect(m(".foo", {className: ""}))
.to.have.deep.property("attrs.className", "foo")
})
it("accepts a class and class attr", function () {
var node = m(".foo", {class: "bar"})
expect(node).to.have.deep.property("attrs.class")
expect(node.attrs.class).to.include("foo").and.include("bar")
})
it("accepts a class and className attr", function () {
var node = m(".foo", {className: "bar"})
expect(node).to.have.deep.property("attrs.className")
expect(node.attrs.className).to.include("foo").and.include("bar")
})
// https://github.com/lhorie/mithril.js/issues/382 and 512
it("sets an empty className attr if it's an empty string", function () {
expect(m("div", {className: ""}))
.to.have.deep.property("attrs.className", "")
})
it("does not set className attr if class is given", function () {
expect(m("div", {class: ""})).to.not.have.property("attrs.className")
})
it("does not set class attr if className is given", function () {
expect(m("div", {className: ""})).to.not.have.property("attrs.class")
})
it("sets an empty class attr if it's an empty string", function () {
expect(m("div", {class: ""})).to.have.deep.property("attrs.class", "")
})
it("does not flatten 1 nested array", function () {
expect(m("div", [1, 2, 3], 4))
.to.have.property("children")
.that.eqls([[1, 2, 3], 4])
})
it("does not flatten 2 nested arrays", function () {
expect(m("div", [1, 2, 3], [4, 5, 6, 7]))
.to.have.property("children")
.that.eqls([[1, 2, 3], [4, 5, 6, 7]])
})
it("does not flatten 3 nested arrays", function () {
expect(m("div", [1], [2], [3]))
.to.have.property("children")
.that.eqls([[1], [2], [3]])
})
it("doesn't recreate the DOM when classes are different", function () {
var v1 = m(".foo", {class: "", onclick: function () {}})
var v2 = m(".foo", {class: "bar", onclick: function () {}})
expect(v1)
.to.have.property("attrs")
.that.contains.all.keys("class", "onclick")
expect(v2)
.to.have.property("attrs")
.that.contains.all.keys("class", "onclick")
})
it("proxies an object first arg to m.component()", function () {
var spy = sinon.spy()
var component = {
controller: spy,
view: function () {
return m("div", "testing")
}
}
var args = {age: 12}
m(component, args).controller()
expect(spy.firstCall).to.have.been.calledWith(args)
m.component(component, args).controller()
expect(spy.secondCall).to.have.been.calledWith(args)
})
})

View file

@ -1,798 +0,0 @@
describe("m.mount()", function () {
"use strict"
// This is a frequent idiom
function refresh(force) {
m.redraw(!!force)
mock.requestAnimationFrame.$resolve()
}
function clear(root) {
m.mount(root, null)
mock.requestAnimationFrame.$resolve()
}
function mount(root, mod) {
var res = m.mount(root, mod)
mock.requestAnimationFrame.$resolve()
return res
}
// This is extremely frequent in the tests
function pure(view) {
return {
controller: function () {},
view: view
}
}
it("exists", function () {
expect(m.mount).to.be.a("function")
})
it("mounts onto the root", function () {
var root = mock.document.createElement("div")
var whatever = 1
var app = pure(function () {
return [
whatever % 2 ? m("span", "% 2") : undefined,
m("div", "bugs"),
m("a")
]
})
mount(root, app)
whatever++
refresh()
whatever++
refresh()
expect(root.childNodes).to.have.length.above(0)
})
it("reloads components correctly", function () {
mock.requestAnimationFrame.$resolve()
var root1 = mock.document.createElement("div")
var controller1 = sinon.spy(function () { this.value = "test1" }) // eslint-disable-line
var view1 = sinon.stub().returns("test1")
var mod1 = m.mount(root1, {
controller: controller1,
view: view1
})
var controller2 = sinon.spy(function () { this.value = "test2" }) // eslint-disable-line
var view2 = sinon.stub().returns("test2")
var root2 = mock.document.createElement("div")
var mod2 = mount(root2, {
controller: controller2,
view: view2
})
expect(controller1).to.have.been.called
expect(view1).to.have.been.called
expect(controller2).to.have.been.called
expect(view2).to.have.been.called
expect(root1.childNodes[0].nodeValue).to.equal("test1")
expect(root2.childNodes[0].nodeValue).to.equal("test2")
expect(mod1).to.have.property("value", "test1")
expect(mod2).to.have.property("value", "test2")
})
it("triggers an unload when the element is removed", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var spy = sinon.spy()
mount(root, {
controller: function () {
this.onunload = spy
},
view: function () {}
})
clear(root)
expect(spy).to.have.been.called
})
it("passes the args to both component & view", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var ctrlSpy = sinon.spy()
var viewSpy = sinon.stub().returns(m("div"))
var component = {
controller: ctrlSpy,
view: viewSpy
}
var arg = {}
mount(root, m.component(component, arg))
expect(ctrlSpy).to.have.been.calledWith(arg)
expect(viewSpy.firstCall.args[1]).to.equal(arg)
})
it("mounts a component without a controller", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var arg = {}
var spy = sinon.spy()
var component = pure(spy)
mount(root, m.component(component, arg))
expect(spy.firstCall.args[1]).to.equal(arg)
})
it("only runs a component controller once", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var ctrlSpy = sinon.spy()
var viewSpy = sinon.stub().returns(m("div"))
var sub = {
controller: ctrlSpy,
view: viewSpy
}
mount(root, pure(function () { return sub }))
refresh(true)
expect(ctrlSpy).to.have.been.calledOnce
expect(viewSpy).to.have.been.calledTwice
})
it("only runs a subcomponent controller once", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var ctrl1 = sinon.spy()
var view1 = sinon.stub().returns(m("div"))
var subsub = {
controller: ctrl1,
view: view1
}
var ctrl2 = sinon.spy()
var view2 = sinon.stub().returns(subsub)
var sub = {
controller: ctrl2,
view: view2
}
mount(root, pure(function () { return sub }))
refresh(true)
expect(ctrl1).to.have.been.calledOnce
expect(ctrl2).to.have.been.calledOnce
expect(view1).to.have.been.calledTwice
expect(view2).to.have.been.calledTwice
})
it("addresses keys in components", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var list = [1, 2, 3]
var sub = pure(function () { return m("div") })
m.mount(root, pure(function () {
return list.map(function (i) {
return m.component(sub, {key: i})
})
}))
var firstBefore = root.childNodes[0]
mock.requestAnimationFrame.$resolve()
list.reverse()
refresh(true)
expect(root.childNodes[2]).to.equal(firstBefore)
})
it("addresses keys in subcomponents correctly", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var list = [1, 2, 3]
var subsub = pure(function () { return m("div") })
var sub = pure(function () { return subsub })
m.mount(root, pure(function () {
return list.map(function (i) {
return m.component(sub, {key: i})
})
}))
var firstBefore = root.childNodes[0]
mock.requestAnimationFrame.$resolve()
list.reverse()
refresh(true)
expect(root.childNodes[2]).to.equal(firstBefore)
})
it("is error resistant with keys in components", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var list = [1, 2, 3]
var sub = pure(function () { return m("div", {key: 1}) })
m.mount(root, pure(function () {
return list.map(function (i) {
return m.component(sub, {key: i})
})
}))
var firstBefore = root.childNodes[0]
mock.requestAnimationFrame.$resolve()
list.reverse()
refresh(true)
expect(root.childNodes[2]).to.equal(firstBefore)
})
it("is error resistant with keys in subcomponents", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var list = [1, 2, 3]
var subsub = pure(function () { return m("div", {key: 1}) })
var sub = pure(function () { return subsub })
m.mount(root, pure(function () {
return list.map(function (i) {
return m.component(sub, {key: i})
})
}))
var firstBefore = root.childNodes[0]
mock.requestAnimationFrame.$resolve()
list.reverse()
refresh(true)
expect(root.childNodes[2]).to.equal(firstBefore)
})
it("retains subcomponent identity if child of keyed element", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var list = [1, 2, 3]
var sub = pure(function () { return m("div") })
m.mount(root, pure(function () {
return list.map(function (i) {
return m("div", {key: i}, sub)
})
}))
var firstBefore = root.childNodes[0].childNodes[0]
mock.requestAnimationFrame.$resolve()
list.reverse()
refresh(true)
expect(root.childNodes[2].childNodes[0]).to.equal(firstBefore)
})
it("calls component onunload when removed from template", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var list = [1, 2, 3]
var spies = []
var sub = {
controller: function (opts) {
this.onunload = spies[opts.key] = sinon.spy()
},
view: function () {
return m("div")
}
}
mount(root, pure(function () {
return list.map(function (i) {
return m.component(sub, {key: i})
})
}))
list = []
refresh(true)
expect(spies[1]).to.have.been.called
expect(spies[2]).to.have.been.called
expect(spies[3]).to.have.been.called
})
it("calls subcomponent onunload when removed from template", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var list = [1, 2, 3]
var spies1 = []
var spies2 = []
var subsub = {
controller: function (opts) {
this.onunload = spies1[opts.key] = sinon.spy()
},
view: function () {
return m("div")
}
}
var sub = {
controller: function (opts) {
this.onunload = spies2[opts.key] = sinon.spy()
},
view: function (ctrl, opts) {
return m.component(subsub, {key: opts.key})
}
}
mount(root, pure(function () {
return list.map(function (i) {
return m.component(sub, {key: i})
})
}))
list = []
refresh(true)
expect(spies1[1]).to.have.been.called
expect(spies1[2]).to.have.been.called
expect(spies1[3]).to.have.been.called
expect(spies2[1]).to.have.been.called
expect(spies2[2]).to.have.been.called
expect(spies2[3]).to.have.been.called
})
it("doesn't redraw if m.render() is called by controller constructor", function () { // eslint-disable-line
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var spy = sinon.stub().returns(m("div"))
var sub = {
controller: function () {
m.redraw()
},
view: spy
}
mount(root, pure(function () { return sub }))
expect(spy).to.have.been.called
})
it("doesn't redraw if m.render() is called by subcomponent controller constructor", function () { // eslint-disable-line
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var spy = sinon.stub().returns(m("div"))
var subsub = {
controller: function () {
m.redraw()
},
view: spy
}
var sub = pure(function () { return subsub })
mount(root, pure(function () { return sub }))
expect(spy).to.have.been.called
})
it("renders nested components under keyed components", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var spy = sinon.stub().returns(m(".reply"))
var Reply = pure(spy)
var CommentList = pure(function (ctrl, props) {
return m(".list", props.list.map(function (i) {
return m(".comment", [
m.component(Reply, {key: i})
])
}))
})
mount(root, pure(function () {
return m(".outer", [
m(".inner", m.component(CommentList, {list: [1, 2, 3]}))
])
}))
expect(spy).to.have.been.calledThrice
})
it("calls unload when the component is replaced with another component", function () { // eslint-disable-line
var root = mock.document.createElement("div")
var spy = sinon.spy()
m.mount(root, {
controller: function () {
this.onunload = spy
},
view: function () {}
})
m.mount(root, pure(function () {}))
expect(spy).to.have.been.called
})
it("calls config with truthy init only once", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var count = 0
mount(root, pure(function () {
return m("div", {
config: function (el, init) {
if (init) count += 1
}
})
}))
refresh()
expect(count).to.equal(1)
})
it("doesn't recreate node that modifies DOM in config, but stays same between redraws", function () { // eslint-disable-line
var root = mock.document.createElement("div")
var child = mock.document.createElement("div")
var show = true
function test(el, init) {
if (!init) {
root.appendChild(child)
}
}
mount(root, pure(function () {
return [
m(".foo", {
key: 1,
config: test,
onclick: function () { show = !show }
}),
show ? m(".bar", {key: 2}) : null
]
}))
show = false
refresh()
show = true
refresh()
expect(root.childNodes).to.have.length(3)
})
it("correctly replaces nodes", function () {
var root = mock.document.createElement("div")
var show = true
var sub = pure(function () { return m("div", "component") })
mount(root, pure(function () {
return show ? [
m("h1", "1"),
sub
] : [
m("h1", "2")
]
}))
show = false
refresh()
show = true
refresh()
expect(root.childNodes).to.have.length(2)
})
// https://github.com/lhorie/mithril.js/issues/551
it("only redraws a component when clicked", function () {
var root = mock.document.createElement("div")
var a = false
var found = {}
var onunload = sinon.spy()
var view = sinon.spy(function () {
return m("div", {config: Comp.config}, [ // eslint-disable-line
m("div", {
onclick: function () {
a = !a
m.redraw(true)
found = root.childNodes[0].childNodes[1]
}
}, "asd"),
a ? m("#a", "aaa") : null,
"test"
])
})
var Comp = {
view: view,
config: function (el, init, ctx) {
if (!init) ctx.onunload = onunload
}
}
m.mount(root, pure(function () { return Comp }))
var target = root.childNodes[0].childNodes[0]
target.onclick({currentTarget: target})
mock.requestAnimationFrame.$resolve()
expect(onunload).to.not.be.called
expect(found).to.have.property("id", "a")
expect(view).to.have.been.calledThrice
})
// https://github.com/lhorie/mithril.js/issues/551
it("only redraws a component when clicked if the strategy is `none`", function () { // eslint-disable-line
var root = mock.document.createElement("div")
var a = false
var found = {}
var onunload = sinon.spy()
var view = sinon.spy(function () {
return m("div", {config: Comp.config}, [ // eslint-disable-line
m("div", {
onclick: function () {
a = !a
m.redraw(true)
found = root.childNodes[0].childNodes[1]
m.redraw.strategy("none")
}
}, "asd"),
a ? m("#a", "aaa") : null,
"test"
])
})
var Comp = {
view: view,
config: function (el, init, ctx) {
if (!init) ctx.onunload = onunload
}
}
m.mount(root, pure(function () { return Comp }))
var target = root.childNodes[0].childNodes[0]
target.onclick({currentTarget: target})
mock.requestAnimationFrame.$resolve()
expect(onunload).to.not.be.called
expect(found).to.have.property("id", "a")
expect(view).to.have.been.calledTwice
})
it("redraws when clicked and click handler forces redraw", function () {
var root = mock.document.createElement("div")
var view = sinon.stub().returns(m("div", {
onclick: function () { m.redraw(true) }
}))
m.mount(root, pure(view))
var target = root.childNodes[0]
target.onclick({currentTarget: target})
mock.requestAnimationFrame.$resolve()
expect(view).to.be.calledThrice
})
function resolveXhr() {
mock.XMLHttpRequest.$instances.pop().$resolve().onreadystatechange()
mock.requestAnimationFrame.$resolve()
}
it("doesn't redraw on a single synchronous request", function () {
var root = mock.document.createElement("div")
var data
var view = sinon.spy(function (ctrl) {
data = ctrl.foo()
return m("div")
})
var Comp = {
controller: function () {
this.foo = m.request({method: "GET", url: "/foo"})
},
view: view
}
mount(root, pure(function () { return Comp }))
resolveXhr()
clear(root)
expect(view).to.be.calledOnce
expect(data).to.have.property("url", "/foo")
})
it("doesn't redraw on multiple synchronous requests", function () {
mock.requestAnimationFrame.$resolve()
mock.location.search = "?"
var root = mock.document.createElement("div")
var view1 = sinon.stub().returns(m("div"))
var view2 = sinon.stub().returns(m("div"))
var Comp1 = {
controller: function () {
this.foo = m.request({method: "GET", url: "/foo"})
},
view: view1
}
var Comp2 = {
controller: function () {
this.bar = m.request({method: "GET", url: "/bar"})
},
view: view2
}
mount(root, pure(function () {
return m("div", [
Comp1,
Comp2
])
}))
resolveXhr()
resolveXhr()
clear(root)
expect(view1).to.be.calledOnce
expect(view2).to.be.calledOnce
})
it("instantiates different controllers for components without controller constructors", function () { // eslint-disable-line
var root = mock.document.createElement("div")
var cond = true
var controller1, controller2
var Comp1 = pure(function (ctrl) {
controller1 = ctrl
return m("div")
})
var Comp2 = pure(function (ctrl) {
controller2 = ctrl
return m("div")
})
mount(root, pure(function () { return cond ? Comp1 : Comp2 }))
cond = false
refresh(true)
expect(controller1).to.not.equal(controller2)
})
it("unloads removed components", function () {
var root = mock.document.createElement("div")
var onunload = sinon.spy()
var cond = true
var Comp1 = pure(function () {
return m("div", {
config: function (el, init, ctx) {
ctx.onunload = onunload
}
})
})
var Comp2 = pure(function () { return m("div") })
mount(root, pure(function () { return cond ? Comp1 : Comp2 }))
cond = false
refresh(true)
expect(onunload).to.be.called
})
it("calls config with its second argument false first", function () {
var root = mock.document.createElement("div")
var cond = true
var config = sinon.spy()
var Comp1 = pure(function () { return m("div") })
var Comp2 = pure(function () { return m("div", {config: config}) })
mount(root, pure(function () { return cond ? Comp1 : Comp2 }))
cond = false
refresh(true)
expect(config.firstCall.args[1]).to.be.false
})
it("refreshes the component when it's redrawn in a handler", function () {
var root = mock.document.createElement("div")
var sub = pure(function () { return m("#bar", "test") })
var el
m.mount(root, pure(function (ctrl) {
return m("div", [
m("button", {
onclick: function () {
ctrl.bar = true
m.redraw(true)
el = root.childNodes[0].childNodes[1]
}
}, "click me"),
ctrl.bar ? m.component(sub) : ""
])
}))
root.childNodes[0].childNodes[0].onclick({})
expect(el).to.have.property("id", "bar")
})
})

View file

@ -1,64 +0,0 @@
describe("m.prop()", function () {
"use strict"
it("reads correct value", function () {
var prop = m.prop("test")
expect(prop()).to.equal("test")
})
it("defaults to `undefined`", function () {
var prop = m.prop()
expect(prop()).to.be.undefined
})
it("sets the correct value", function () {
var prop = m.prop("test")
prop("foo")
expect(prop()).to.equal("foo")
})
it("sets `null`", function () {
var prop = m.prop(null)
expect(prop()).to.be.null
})
it("sets `undefined`", function () {
var prop = m.prop(undefined)
expect(prop()).to.be.undefined
})
it("returns the new value when set", function () {
var prop = m.prop()
expect(prop("foo")).to.equal("foo")
})
it("correctly stringifies to the correct value", function () {
var prop = m.prop("test")
expect(JSON.stringify(prop)).to.equal('"test"')
})
it("correctly stringifies to the correct value as a child", function () {
var obj = {prop: m.prop("test")}
expect(JSON.stringify(obj)).to.equal('{"prop":"test"}')
})
it("correctly wraps Mithril promises", function () {
var defer = m.deferred()
var prop = m.prop(defer.promise)
defer.resolve("test")
expect(prop()).to.equal("test")
})
it("returns a thenable when wrapping a Mithril promise", function () {
var defer = m.deferred()
var prop = m.prop(defer.promise).then(function () {
return "test2"
})
defer.resolve("test")
expect(prop()).to.equal("test2")
})
})

View file

@ -1,220 +0,0 @@
describe("m.redraw()", function () {
"use strict"
beforeEach(function () {
mock.requestAnimationFrame.$resolve()
})
it("exists", function () {
expect(m.redraw).to.be.a("function")
})
it("correctly renders a property if the controller value changes", function () { // eslint-disable-line
var ctx
var root = mock.document.createElement("div")
m.mount(root, {
controller: function () { ctx = this }, // eslint-disable-line
view: function (ctrl) { return ctrl.value }
})
mock.requestAnimationFrame.$resolve()
var valueBefore = root.childNodes[0].nodeValue
ctx.value = "foo"
m.redraw()
mock.requestAnimationFrame.$resolve()
expect(valueBefore).to.equal("")
expect(root.childNodes[0].nodeValue).to.equal("foo")
})
it("runs unnecessary redraws asynchronously", function () {
var root = mock.document.createElement("div")
var view = sinon.spy()
m.mount(root, {
controller: function () {},
view: view
})
mock.requestAnimationFrame.$resolve() // teardown
m.redraw()
// These should run asynchronously
m.redraw()
m.redraw()
m.redraw()
mock.requestAnimationFrame.$resolve() // teardown
expect(view).to.be.calledThrice
})
it("runs unnecessary forced redraws asynchronously", function () {
var root = mock.document.createElement("div")
var view = sinon.spy()
m.mount(root, {
controller: function () {},
view: view
})
mock.requestAnimationFrame.$resolve() // teardown
m.redraw(true)
// These should run asynchronously
m.redraw(true)
m.redraw(true)
m.redraw(true)
mock.requestAnimationFrame.$resolve() // teardown
expect(view).to.have.callCount(5)
})
context("m.redraw.strategy()", function () {
// 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
}
// 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)
// 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()
mock.location.search = "?"
m.route.mode = "search"
this.root = mock.document.createElement("div")
})
afterEach(function () {
m.mount(this.root, null)
})
/* eslint-enable no-invalid-this */
it("exists", function () {
expect(m.redraw.strategy).to.be.a("function")
})
dit("works with \"all\"", function (root) {
var strategy
route(root, "/foo1", {
"/foo1": {
controller: function () {
strategy = m.redraw.strategy()
m.redraw.strategy("none")
},
view: function () {
return m("div")
}
}
})
expect(strategy).to.equal("all")
expect(root.childNodes).to.be.empty
})
dit("works with \"redraw\"", function (root) {
var count = 0
var strategy
function config(el, init) {
if (!init) count++
}
route(root, "/foo1", {
"/foo1": pure(function () {
return m("div", {config: config})
}),
"/bar1": {
controller: function () {
strategy = m.redraw.strategy()
m.redraw.strategy("redraw")
},
view: function () {
return m("div", {config: config})
}
}
})
route("/bar1")
expect(strategy).to.equal("all")
expect(count).to.equal(1)
})
dit("works with \"diff\"", function (root) {
var strategy
m.route(root, "/foo1", {
"/foo1": {
controller: function () { this.number = 1 },
view: function (ctrl) {
return m("div", {
onclick: function () {
strategy = m.redraw.strategy()
ctrl.number++
m.redraw.strategy("none")
}
}, ctrl.number)
}
}
})
root.childNodes[0].onclick({})
mock.requestAnimationFrame.$resolve()
expect(strategy).to.equal("diff")
expect(root.childNodes[0].childNodes[0].nodeValue).to.equal("1")
})
dit("recreates the component when \"all\"", function (root) {
var count = 0
function config(el, init) {
if (!init) count++
}
m.route(root, "/foo1", {
"/foo1": pure(function () {
return m("div", {
config: config,
onclick: function () {
m.redraw.strategy("all")
}
})
})
})
root.childNodes[0].onclick({})
mock.requestAnimationFrame.$resolve()
expect(count).to.equal(2)
})
})
})

File diff suppressed because it is too large Load diff

View file

@ -1,344 +0,0 @@
describe("m.request()", function () {
"use strict"
// Much easier to read
function resolve() {
var xhr = mock.XMLHttpRequest.$instances.pop()
xhr.$resolve.apply(xhr, arguments)
xhr.onreadystatechange()
return xhr
}
// Common abstraction: request(opts, ...callbacks)
function request(opts) {
var ret = m.request(opts)
for (var i = 0; i < arguments.length; i++) {
ret = ret.then(arguments[i])
}
resolve()
return ret
}
it("sets the correct properties on `GET`", function () {
var prop = request({
method: "GET",
url: "test"
})
expect(prop()).to.contain.keys({
method: "GET",
url: "test"
})
})
it("returns a Mithril promise (1)", function () {
var prop = request(
{method: "GET", url: "test"},
function () { return "foo" })
expect(prop()).to.equal("foo")
})
it("returns a Mithril promise (2)", function () {
var prop = request({method: "GET", url: "test"})
var result = prop()
expect(prop.then(function (value) { return value })()).to.equal(result)
})
it("sets the correct properties on `POST`", function () {
var prop = request({
method: "POST",
url: "http://domain.com:80",
data: {}
})
expect(prop()).to.contain.keys({
method: "POST",
url: "http://domain.com:80"
})
})
it("sets the correct arguments", function () {
expect(request({
method: "POST",
url: "http://domain.com:80/:test1",
data: {test1: "foo"}
})().url).to.equal("http://domain.com:80/foo")
})
it("propagates errors through the promise (1)", function () {
var error = m.prop()
var prop = m.request({
method: "GET",
url: "test",
deserialize: function () { throw new Error("error occurred") }
}).then(null, error)
resolve()
expect(prop().message).to.equal("error occurred")
expect(error().message).to.equal("error occurred")
})
it("propagates errors through the promise (2)", function () {
var error = m.prop()
var prop = m.request({
method: "GET",
url: "test",
deserialize: function () { throw new Error("error occurred") }
}).catch(error)
resolve()
expect(prop().message).to.equal("error occurred")
expect(error().message).to.equal("error occurred")
})
it("synchronously throws TypeErrors", function () {
var error = m.prop()
var exception
var prop = m.request({
method: "GET",
url: "test",
deserialize: function () { throw new TypeError("error occurred") }
}).then(null, error)
try {
resolve()
} catch (e) {
exception = e
}
expect(prop()).to.not.exist
expect(error()).to.not.exist
expect(exception.message).to.equal("error occurred")
})
it("sets correct Content-Type when given data", function () {
var error = m.prop()
m.request({
method: "POST",
url: "test",
data: {foo: 1}
}).then(null, error)
var xhr = mock.XMLHttpRequest.$instances.pop()
xhr.onreadystatechange()
expect(xhr.$headers).to.have.property(
"Content-Type",
"application/json; charset=utf-8")
})
it("doesn't set Content-Type when it doesn't have data", function () {
var error = m.prop()
m.request({
method: "POST",
url: "test"
}).then(null, error)
var xhr = mock.XMLHttpRequest.$instances.pop()
xhr.onreadystatechange()
expect(xhr.$headers).to.not.have.property("Content-Type")
})
it("correctly sets initial value", function () {
var prop = m.request({
method: "POST",
url: "test",
initialValue: "foo"
})
var initialValue = prop()
resolve()
expect(initialValue).to.equal("foo")
})
it("correctly propagates initial value when not completed", function () {
var prop = m.request({
method: "POST",
url: "test",
initialValue: "foo"
}).then(function (value) { return value })
var initialValue = prop()
resolve()
expect(initialValue).to.equal("foo")
})
it("resolves `then` correctly with an initialValue", function () {
var prop = m.request({
method: "POST",
url: "test",
initialValue: "foo"
}).then(function () { return "bar" })
resolve()
expect(prop()).to.equal("bar")
})
it("appends query strings to `url` from `data` for `GET`", function () {
var prop = m.request({method: "GET", url: "/test", data: {foo: 1}})
resolve()
expect(prop().url).to.equal("/test?foo=1")
})
it("doesn't append query strings to `url` from `data` for `POST`", function () { // eslint-disable-line
var prop = m.request({method: "POST", url: "/test", data: {foo: 1}})
resolve()
expect(prop().url).to.equal("/test")
})
it("appends children in query strings to `url` from `data` for `GET`", function () { // eslint-disable-line
var prop = m.request({method: "GET", url: "test", data: {foo: [1, 2]}})
resolve()
expect(prop().url).to.equal("test?foo=1&foo=2")
})
it("propagates initial value in call before request is completed", function () { // eslint-disable-line
var value
var prop1 = m.request({method: "GET", url: "test", initialValue: 123})
expect(prop1()).to.equal(123)
var prop2 = prop1.then(function () { return 1 })
expect(prop2()).to.equal(123)
var prop3 = prop1.then(function (v) { value = v })
expect(prop3()).to.equal(123)
resolve()
expect(value.method).to.equal("GET")
expect(value.url).to.equal("test")
})
context("over jsonp", function () {
/* eslint-disable no-invalid-this */
beforeEach(function () {
var body = this.body = mock.document.createElement("body")
mock.document.body = body
mock.document.appendChild(body)
})
afterEach(function () {
mock.document.removeChild(this.body)
})
/* eslint-enable no-invalid-this */
function request(data, callbackKey) {
return m.request({
url: "/test",
dataType: "jsonp",
data: data,
callbackKey: callbackKey
})
}
function find(list, item, prop) {
var res
for (var i = 0; i < list.length; i++) {
var entry = list[i]
if (prop != null) entry = entry[prop]
if (entry.indexOf(item) >= 0) res = entry
}
return res
}
function resolve(data) {
var callback = find(Object.keys(mock), "mithril_callback")
var url = find(mock.document.getElementsByTagName("script"),
callback, "src")
mock[callback](data)
return url
}
it("sets the `GET` url with the correct query parameters", function () {
request({foo: "bar"})
expect(resolve({foo: "bar"})).to.contain("foo=bar")
})
it("correctly gets the value, without appending the script on the document", function () { // eslint-disable-line
var data = m.prop()
request().then(data)
var url = resolve({foo: "bar"})
expect(url).to.contain("/test?callback=mithril_callback")
expect(data()).to.eql({foo: "bar"})
})
it("correctly gets the value with a custom `callbackKey`, without appending the script on the document", function () { // eslint-disable-line
var data = m.prop()
request(null, "jsonpCallback").then(data)
var url = resolve({foo: "bar1"})
expect(url).to.contain("/test?jsonpCallback=mithril_callback")
expect(data()).to.eql({foo: "bar1"})
})
it("correctly gets the value on calling the function", function () {
var req = request()
resolve({foo: "bar1"})
expect(req()).to.eql({foo: "bar1"})
})
})
it("ends the computation when a SyntaxError is thrown from `options.extract`", function () { // eslint-disable-line max-len
var root = mock.document.createElement("div")
var viewSpy = sinon.spy(function () { return m("div") })
var resolved = sinon.spy()
var rejected = sinon.spy()
m.mount(root, {
controller: function () {
m.request({
url: "/test",
extract: function () {
throw new SyntaxError()
}
}).then(resolved, rejected)
},
view: viewSpy
})
// For good measure
mock.requestAnimationFrame.$resolve()
expect(function () {
resolve()
}).to.throw()
expect(resolved).to.not.have.been.called
expect(rejected).to.not.have.been.called
// The controller should throw, but the view should still render.
expect(viewSpy).to.have.been.called
// For good measure
mock.requestAnimationFrame.$resolve()
})
it("can use a config correctly", function () {
var config = sinon.spy()
var result = m.prop()
var error = sinon.spy
var opts = {
method: "GET",
url: "/test",
config: config
}
m.request(opts).then(result, error)
var xhr = resolve({foo: "bar"})
expect(config).to.be.calledWithExactly(xhr, opts)
expect(result()).to.eql({foo: "bar"})
expect(error).to.not.be.called
})
})

View file

@ -1,26 +0,0 @@
describe("m.route.buildQueryString()", function () {
"use strict"
it("exists", function () {
expect(m.route.buildQueryString).to.be.a("function")
})
it("converts an empty object to an empty string", function () {
expect(m.route.buildQueryString({})).to.equal("")
})
it("converts an object into a correct query string", function () {
expect(
m.route.buildQueryString({
foo: "bar",
hello: ["world", "mars", "mars"],
world: {
test: 3
},
bam: "",
yup: null,
removed: undefined
})
).to.equal("foo=bar&hello=world&hello=mars&world%5Btest%5D=3&bam=&yup")
})
})

File diff suppressed because it is too large Load diff

View file

@ -1,34 +0,0 @@
describe("m.route.parseQueryString()", function () {
"use strict"
it("exists", function () {
expect(m.route.parseQueryString).to.be.a("function")
})
it("parses an empty string as an empty object", function () {
var args = m.route.parseQueryString("")
expect(args).to.eql({})
})
it("parses multiple parameters correctly", function () {
var args = m.route.parseQueryString("foo=bar&hello=world&hello=mars" +
"&bam=&yup")
expect(args).to.eql({
foo: "bar",
hello: ["world", "mars"],
bam: "",
yup: null
})
})
it("parses escapes correctly", function () {
var args = m.route.parseQueryString("foo=bar&hello%5B%5D=world&" +
"hello%5B%5D=mars&hello%5B%5D=pluto")
expect(args).to.eql({
foo: "bar",
"hello[]": ["world", "mars", "pluto"]
})
})
})

View file

@ -1,28 +0,0 @@
describe("m.startComputation(), m.endComputation()", function () {
"use strict"
it("exists", function () {
expect(m.startComputation).to.be.a("function")
expect(m.endComputation).to.be.a("function")
})
it("blocks automatic rendering", function () {
mock.requestAnimationFrame.$resolve()
var root = mock.document.createElement("div")
var controller = m.mount(root, {
controller: function () {},
view: function (ctrl) { return ctrl.value }
})
mock.requestAnimationFrame.$resolve()
m.startComputation()
controller.value = "foo"
m.endComputation()
mock.requestAnimationFrame.$resolve()
expect(root.childNodes[0].nodeValue).to.equal("foo")
})
// FIXME: this needs to be better tested
})

View file

@ -1,56 +0,0 @@
describe("m.sync()", function () {
"use strict"
it("exists", function () {
expect(m.sync).to.be.a("function")
})
it("joins multiple promises in order to an array", function () {
var value
var deferred1 = m.deferred()
var deferred2 = m.deferred()
m.sync([deferred1.promise, deferred2.promise])
.then(function (data) { value = data })
deferred1.resolve("test")
deferred2.resolve("foo")
expect(value).to.eql(["test", "foo"])
})
it("joins multiple promises out of order to an array", function () {
var value
var deferred1 = m.deferred()
var deferred2 = m.deferred()
m.sync([deferred1.promise, deferred2.promise])
.then(function (data) { value = data })
deferred2.resolve("foo")
deferred1.resolve("test")
expect(value).to.eql(["test", "foo"])
})
// FIXME: bad behavior?
it("rejects to an array if one promise rejects", function () {
var value
var deferred = m.deferred()
m.sync([deferred.promise]).catch(function (data) { value = data })
deferred.reject("fail")
expect(value).to.eql(["fail"])
})
it("resolves immediately if given an empty array", function () {
var value = 1
m.sync([]).then(function () { value = 2 })
expect(value).to.equal(2)
})
it("resolves to an empty array if given an empty array", function () {
var value
m.sync([]).then(function (data) { value = data })
expect(value).to.eql([])
})
})

View file

@ -1,69 +0,0 @@
describe("m.trust()", function () {
"use strict"
it("exists", function () {
expect(m.trust).to.be.a("function")
})
it("returns an instance of String", function () {
expect(m.trust("foo")).to.be.an.instanceof(String)
})
it("does not modify the string", function () {
expect(m.trust("foo").valueOf()).to.equal("foo")
})
it("is not identical to the string", function () {
expect(m.trust("foo")).to.not.equal("foo")
})
// FIXME: implement document.createRange().createContextualFragment() in the
// mock window for these tests
dom(function () {
it("isn't escaped in m.render()", function () {
var root = document.createElement("div")
m.render(root, m("div", "a", m.trust("&amp;"), "b"))
expect(root.childNodes[0].innerHTML).to.equal("a&amp;b")
})
it("works with mixed trusted content in div", function () {
var root = document.createElement("div")
m.render(root, [m.trust("<p>1</p><p>2</p>"), m("i", "foo")])
expect(root.childNodes[2].tagName).to.equal("I")
})
it("works with mixed trusted content in text nodes", function () {
var root = document.createElement("div")
m.render(root, [
m.trust("<p>1</p>123<p>2</p>"),
m("i", "foo")
])
expect(root.childNodes[3].tagName).to.equal("I")
})
// TODO: m.trust's contents are having their tags stripped.
xit("works with mixed trusted content in td", function () {
var root = document.createElement("table")
root.appendChild(root = document.createElement("tr"))
m.render(root, [
m.trust("<td>1</td><td>2</td>"),
m("td", "foo")
])
expect(root.childNodes[2].tagName).to.equal("TD")
})
it("works with trusted content in div", function () {
var root = document.createElement("div")
m.render(root, m("div", [
m("p", "&copy;"),
m("p", m.trust("&copy;")),
m.trust("&copy;")
]))
expect(root.innerHTML)
.to.equal("<div><p>&amp;copy;</p><p>©</p>©</div>")
})
})
})

View file

@ -1,17 +0,0 @@
describe("m.withAttr()", function () {
"use strict"
it("calls the handler with the right value/context without callbackThis", function () { // eslint-disable-line
var spy = sinon.spy()
var object = {}
m.withAttr("test", spy).call(object, {currentTarget: {test: "foo"}})
expect(spy).to.be.calledOn(object).and.calledWith("foo")
})
it("calls the handler with the right value/context with callbackThis", function () { // eslint-disable-line
var spy = sinon.spy()
var object = {}
m.withAttr("test", spy, object)({currentTarget: {test: "foo"}})
expect(spy).to.be.calledOn(object).and.calledWith("foo")
})
})

View file

@ -1,214 +0,0 @@
<!doctype html>
<title>SVG test</title>
<style>
.path {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: dash 5s linear alternate infinite;
-webkit-animation: dash 5s linear alternate infinite;
}
@keyframes dash {
from {
stroke-dashoffset: 1000;
}
to {
stroke-dashoffset: 0;
}
}
@-webkit-keyframes dash {
from {
stroke-dashoffset: 1000;
}
to {
stroke-dashoffset: 0;
}
}
</style>
<p>
This page is for testing SVG support in Mithril. This page should contain:
</p>
<ul>
<li>an HTML link labeled "HTML link"</li>
<li>an SVG link labeled "SVG link"</li>
<li>a tilted blue square</li>
<li>a cat picture</li>
<li>an animated line drawing</li>
<li>a clock with the current time</li>
</ul>
<p>
The links should open in a new tab. All items should display title tooltips
when hovered over.
</p>
<div id="test"></div>
<script src="../mithril.js"></script>
<script>
m.render(document.getElementById("test"), [
m("a[href='http://google.com'][target=_blank][title='HTML link']",
"HTML link"),
m("br"),
m("svg[width=180][height=200]", [
m("rect[title=Square]", {
x: 50,
y: 50,
height: 100,
width: 100,
transform: "translate(30) rotate(45 50 50)",
style: {stroke: "#000", fill: "#0086b2"}
}),
m("a[href='http://google.com'][title='SVG link'][target=_new]", {
style: {textDecoration: "underline"}
}, [
m("text[x=0][y=20]", "SVG Link")
])
]),
m("svg[height=201px][width=201px]", [
m("image[href='http://placekitten.com/201/201']", {
height: "200px",
width: "200px",
title: "Cat picture"
})
]),
m("svg[title='Line drawings']", {
"enable-background": "new 0 0 340 333",
height: "333px",
viewBox: "0 0 340 333",
width: "340px",
x: "0px",
y: "0px"
}, [
m("path.path", {
d: [
"M 66.039,133.545",
"c 0,0 -21 -57,18 -67",
"s 49 -4,65,8",
"s 30,41,53,27",
"s 66,4,58,32",
"s -5,44,18,57",
"s 22,46,0,45",
"s -54 -40 -68 -16",
"s -40,88 -83,48",
"s 11 -61 -11 -80",
"s -79 -7 -70 -41",
"C 46.039,146.545,53.039,128.545,66.039,133.545",
"z",
].join(" "),
fill: "#FFFFFF",
stroke: "#000000",
"stroke-miterlimit": 10,
"stroke-width": 4,
}),
]),
m("svg[height=270px][width=270px][viewBox='0 0 270 270']", [
m("g[transform='translate(150,150)'][title=Clock]", [
m("g", [
m("circle", {
r: 108,
fill: "none",
"stroke-width": 4,
stroke: "gray",
}),
m("circle", {
r: 97,
fill: "none",
"stroke-width": 11,
stroke: "black",
"stroke-dasharray": "4,46.789082",
transform: "rotate(-1.5)",
}),
m("circle", {
r: 100,
fill: "none",
"stroke-width": 5,
stroke: "black",
"stroke-dasharray": "2,8.471976",
transform: "rotate(-.873)",
}),
]),
m("g[transform='rotate(180)']", [
m("g#hour", [
m("line", {
"stroke-width": 5,
y2: 75,
"stroke-linecap": "round",
stroke: "blue",
opacity: 0.5,
}),
m("animateTransform[attributeName=transform]", {
type: "rotate",
repeatCount: "indefinite",
dur: "12h",
by: 360
}),
m("circle[r=7]"),
]),
m("g#minute", [
m("line", {
"stroke-width": 4,
y2: 93,
"stroke-linecap": "round",
stroke: "green",
opacity: 0.9
}),
m("animateTransform[attributeName=transform]", {
type: "rotate",
repeatCount: "indefinite",
dur: "60min",
by: 360,
}),
m("circle[r=6][fill=red]"),
]),
m("g#second", [
m("line", {
"stroke-width": 2,
y1: -20,
y2: 102,
"stroke-linecap": "round",
stroke: "red"
}),
m("animateTransform[attributeName=transform]", {
type: "rotate",
repeatCount: "indefinite",
dur: "60s",
by: 360
}),
m("circle[r=4][fill=blue]"),
]),
]),
]),
m("script", "(" + function () {
"use strict"
function rotate(id, num) {
document.getElementById(id)
.setAttribute("transform", "rotate(" + num + ")")
}
var date = new Date()
var hours = date.getHours()
if (hours > 12) hours -= 12
var minutes = date.getMinutes()
var seconds = date.getSeconds()
rotate("hour", 30 * (hours + minutes / 60 + seconds / 3600))
rotate("minute", 6 * (minutes + seconds / 60))
rotate("second", 6 * seconds)
}.toString() + ")()"),
]),
m("svg[height=200px][width=200px]", [
m("foreignObject", {
x: 0,
y: 0,
width: "100px",
height: "100px",
transform: "translate(0,0)",
}, m("div", {xmlns: "http://www.w3.org/1999/xhtml"}, [
m.trust("this is a piece of html rendered as " +
"<a href=\"http://www.w3.org/TR/SVG11/extend.html\">" +
"SVG foreignObject</a>")
]))
])
])
</script>