mithril-vndb/test/mithril.request.js
impinball 12b8f044f1 Convert tests to Mocha/Chai/Sinon and lint them.
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...
2015-10-31 11:07:22 -04:00

348 lines
8.4 KiB
JavaScript

describe("m.request()", function () {
"use strict"
// Much easier to read
function resolve() {
var xhr = mock.XMLHttpRequest.$instances.pop()
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("propogates 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("propogates 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("does not propogate results to `finally`", function () {
// Data returned by then() functions do *not* propagate to finally().
var data = m.prop()
var prop = m.request({
method: "GET",
url: "test"
})
.then(function () { return "foo" })
.finally(data)
resolve()
expect(prop()).to.equal("foo")
expect(data()).to.not.exist
})
it("does not propogate `finally` results to the next promise", function () {
var data = m.prop()
var prop = m.request({method: "GET", url: "test"})
.then(function () { return "foo" })
.finally(function () { return "bar" })
.then(data)
resolve()
expect(prop()).to.equal("foo")
expect(data()).to.equal("foo")
})
it("propogates `finally` errors", function () {
var error = m.prop()
var prop = m.request({method: "GET", url: "test"})
.then(function () { return "foo" })
.finally(function () { throw new Error("error occurred") })
.catch(error)
resolve()
expect(prop().message).to.equal("error occurred")
expect(error().message).to.equal("error occurred")
})
it("runs successive `finally` after `catch`", function () {
var error = m.prop()
var prop = m.request({
method: "GET",
url: "test",
deserialize: function () { throw new Error("error occurred") }
})
.catch(error)
.finally(function () { error("finally") })
resolve()
expect(prop().message).to.equal("error occurred")
expect(error()).to.equal("finally")
})
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 propogates 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("propogates 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"})
})
})
})