350 lines
8.5 KiB
JavaScript
350 lines
8.5 KiB
JavaScript
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("ignores interpolations without data", function () { // eslint-disable-line
|
|
var prop = m.request({method: "GET", url: "/test:notfound", data: {foo: 1}})
|
|
resolve()
|
|
expect(prop().url).to.equal("/test:notfound?foo=1")
|
|
})
|
|
|
|
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
|
|
})
|
|
})
|