Kick jsonp - fixes #2682
This commit is contained in:
parent
efdb563fa5
commit
716d1e1804
16 changed files with 8 additions and 362 deletions
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
## What is Mithril.js?
|
## What is Mithril.js?
|
||||||
|
|
||||||
A modern client-side JavaScript framework for building Single Page Applications. It's small (<!-- size -->9.34 KB<!-- /size --> gzipped), fast and provides routing and XHR utilities out of the box.
|
A modern client-side JavaScript framework for building Single Page Applications. It's small (<!-- size -->9.18 KB<!-- /size --> gzipped), fast and provides routing and XHR utilities out of the box.
|
||||||
|
|
||||||
Mithril.js is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍.
|
Mithril.js is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍.
|
||||||
|
|
||||||
|
|
|
||||||
15
docs/api.md
15
docs/api.md
|
|
@ -92,21 +92,6 @@ m.request({
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### m.jsonp(options) - [docs](jsonp.md)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
m.jsonp({
|
|
||||||
url: "/api/v1/users/:id",
|
|
||||||
params: {id: 1},
|
|
||||||
callbackKey: "callback",
|
|
||||||
})
|
|
||||||
.then(function(result) {
|
|
||||||
console.log(result)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### m.parseQueryString(querystring) - [docs](parseQueryString.md)
|
#### m.parseQueryString(querystring) - [docs](parseQueryString.md)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ Argument | Type | Required | Descripti
|
||||||
|
|
||||||
### How it works
|
### How it works
|
||||||
|
|
||||||
The `m.buildPathname` creates a [path name](paths.md) from a path template and a parameters object. It's useful for building URLs, and it's what [`m.route`](route.md), [`m.request`](request.md), and [`m.jsonp`](jsonp.md) all use internally to interpolate paths. It uses [`m.buildQueryString`](buildQueryString.md) to generate the query parameters to append to the path name.
|
The `m.buildPathname` creates a [path name](paths.md) from a path template and a parameters object. It's useful for building URLs, and it's what [`m.route`](route.md) and [`m.request`](request.md) use internally to interpolate paths. It uses [`m.buildQueryString`](buildQueryString.md) to generate the query parameters to append to the path name.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var pathname = m.buildPathname("/path/:id", {id: "user", a: 1, b: 2})
|
var pathname = m.buildPathname("/path/:id", {id: "user", a: 1, b: 2})
|
||||||
|
|
|
||||||
103
docs/jsonp.md
103
docs/jsonp.md
|
|
@ -1,103 +0,0 @@
|
||||||
<!--meta-description
|
|
||||||
Documentation on m.jsonp(), a utility to fetch data from JSONP APIs
|
|
||||||
-->
|
|
||||||
|
|
||||||
# jsonp(options)
|
|
||||||
|
|
||||||
- [Description](#description)
|
|
||||||
- [Signature](#signature)
|
|
||||||
- [How it works](#how-it-works)
|
|
||||||
- [Typical usage](#typical-usage)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Description
|
|
||||||
|
|
||||||
Makes JSON-P requests. Typically, it's useful to interact with servers that allow JSON-P but that don't have CORS enabled.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
m.jsonp({
|
|
||||||
url: "/api/v1/users/:id",
|
|
||||||
params: {id: 1},
|
|
||||||
callbackKey: "callback",
|
|
||||||
})
|
|
||||||
.then(function(result) {
|
|
||||||
console.log(result)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Signature
|
|
||||||
|
|
||||||
`promise = m.jsonp(options)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
---------------------- | --------------------------------- | -------- | ---
|
|
||||||
`options` | `Object` | Yes | The request options to pass.
|
|
||||||
`options.url` | `String` | Yes | The [path name](paths.md) to send the request to, optionally interpolated with values from `options.params`.
|
|
||||||
`options.params` | `Object` | No | The data to be interpolated into the URL and serialized into the querystring.
|
|
||||||
`options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
|
||||||
`options.callbackName` | `String` | No | The name of the function that will be called as the callback. Defaults to a randomized string (e.g. `_mithril_6888197422121285_0({a: 1})`
|
|
||||||
`options.callbackKey` | `String` | No | The name of the querystring parameter name that specifies the callback name. Defaults to `callback` (e.g. `/someapi?callback=_mithril_6888197422121285_0`)
|
|
||||||
`options.background` | `Boolean` | No | If `false`, redraws mounted components upon completion of the request. If `true`, it does not. Defaults to `false`.
|
|
||||||
**returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through `type` method
|
|
||||||
|
|
||||||
`promise = m.jsonp(url, options)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
----------- | --------- | -------- | ---
|
|
||||||
`url` | `String` | Yes | The [path name](paths.md) to send the request to. `options.url` overrides this when present.
|
|
||||||
`options` | `Object` | No | The request options to pass.
|
|
||||||
**returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through the `type` method
|
|
||||||
|
|
||||||
This second form is mostly equivalent to `m.jsonp(Object.assign({url: url}, options))`, just it does not depend on the ES6 global `Object.assign` internally.
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### How it works
|
|
||||||
|
|
||||||
The `m.jsonp` utility is useful for third party APIs that can return data in [JSON-P](https://en.wikipedia.org/wiki/JSONP) format.
|
|
||||||
|
|
||||||
In a nutshell, JSON-P consists of creating a `script` tag whose `src` attribute points to a script that lives in the server outside of your control. Typically, you are required to define a global function and specify its name in the querystring of the script's URL. The response will return code that calls your global function, passing the server's data as the first parameter.
|
|
||||||
|
|
||||||
JSON-P has several limitations: it can only use GET requests, it implicitly trusts that the third party server won't serve malicious code and it requires polluting the global JavaScript scope. Nonetheless, it is sometimes the only available way to retrieve data from a service (for example, if the service doesn't support [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Typical usage
|
|
||||||
|
|
||||||
Some services follow the de-facto convention of responding with JSON-P if a `callback` querystring key is provided, thus making `m.jsonp` automatically work without any effort:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
m.jsonp({url: "https://api.github.com/users/lhorie"}).then(function(response) {
|
|
||||||
console.log(response.data.login) // logs "lhorie"
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Some services do not follow conventions and therefore you must specify the callback key that the service expects:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
m.jsonp({
|
|
||||||
url: "https://api.flickr.com/services/feeds/photos_public.gne?tags=kitten&format=json",
|
|
||||||
callbackKey: "jsoncallback",
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
console.log(response.link) // logs "https://www.flickr.com/photos/tags/kitten/"
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
And sometimes, you just want to take advantage of HTTP caching for GET requests for rarely-modified data:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// this request is always called with the same querystring, and therefore it is cached
|
|
||||||
m.jsonp({
|
|
||||||
url: "https://api.github.com/users/lhorie",
|
|
||||||
callbackName: "__callback",
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
console.log(response.data.login) // logs "lhorie"
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
- [m.mount](mount.md)
|
- [m.mount](mount.md)
|
||||||
- [m.route](route.md)
|
- [m.route](route.md)
|
||||||
- [m.request](request.md)
|
- [m.request](request.md)
|
||||||
- [m.jsonp](jsonp.md)
|
|
||||||
- [m.parseQueryString](parseQueryString.md)
|
- [m.parseQueryString](parseQueryString.md)
|
||||||
- [m.buildQueryString](buildQueryString.md)
|
- [m.buildQueryString](buildQueryString.md)
|
||||||
- [m.buildPathname](buildPathname.md)
|
- [m.buildPathname](buildPathname.md)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ Documentation on how to work with paths in Mithril.js
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
||||||
[`m.route`](route.md), [`m.request`](request.md), and [`m.jsonp`](jsonp.md) each have a concept called a path. This is used to generate the URL you route to or fetch from.
|
[`m.route`](route.md) and [`m.request`](request.md) each have a concept called a path. This is used to generate the URL you route to or fetch from.
|
||||||
|
|
||||||
### Path types
|
### Path types
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ There are two general types of paths: raw paths and parameterized paths.
|
||||||
- Raw paths are simply strings used directly as URLs. Nothing is substituted or even split. It's just normalized with all the parameters appended to the end.
|
- Raw paths are simply strings used directly as URLs. Nothing is substituted or even split. It's just normalized with all the parameters appended to the end.
|
||||||
- Parameterized paths let you insert values into paths, escaped by default for convenience and safety against URL injection.
|
- Parameterized paths let you insert values into paths, escaped by default for convenience and safety against URL injection.
|
||||||
|
|
||||||
For [`m.request`](request.md) and [`m.jsonp`](jsonp.md), these can be pretty much any URL, but for [routes](route.md), these can only be absolute URL path names without schemes or domains.
|
For [`m.request`](request.md) these can be pretty much any URL, but for [routes](route.md), these can only be absolute URL path names without schemes or domains.
|
||||||
|
|
||||||
### Path parameters
|
### Path parameters
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ Path parameters are themselves pretty simple. They come in two forms:
|
||||||
- `:foo` - This injects a simple `params.foo` into the URL, escaping its value first.
|
- `:foo` - This injects a simple `params.foo` into the URL, escaping its value first.
|
||||||
- `:foo...` - This injects a raw `params.foo` path into the URL without escaping anything.
|
- `:foo...` - This injects a raw `params.foo` path into the URL without escaping anything.
|
||||||
|
|
||||||
You're probably wondering what that `params` object is supposed to be. It's pretty simple: it's the `params` in either [`m.route.set(path, params)`](route.md#mrouteset), [`m.request({url, params})`](request.md#signature), or [`m.jsonp({url, params})`](jsonp.md#signature).
|
You're probably wondering what that `params` object is supposed to be. It's pretty simple: it's the `params` in either [`m.route.set(path, params)`](route.md#mrouteset), [`m.request({url, params})`](request.md#signature).
|
||||||
|
|
||||||
When receiving routes via [`m.route(root, defaultRoute, routes)`](route.md#signature), you can use these parameters to *extract* values from routes. They work basically the same way as generating the paths, just in the opposite direction.
|
When receiving routes via [`m.route(root, defaultRoute, routes)`](route.md#signature), you can use these parameters to *extract* values from routes. They work basically the same way as generating the paths, just in the opposite direction.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ Documentation on m.redraw(), which schedules an update of all components mounted
|
||||||
|
|
||||||
Updates the DOM after a change in the application data layer.
|
Updates the DOM after a change in the application data layer.
|
||||||
|
|
||||||
You DON'T need to call it if data is modified within the execution context of an event handler defined in a Mithril.js view, or after request completion when using `m.request`/`m.jsonp`. The [autoredraw](autoredraw.md) system, which is built on top of `m.redraw()` will take care of it.
|
You DON'T need to call it if data is modified within the execution context of an event handler defined in a Mithril.js view, or after request completion when using `m.request`. The [autoredraw](autoredraw.md) system, which is built on top of `m.redraw()` will take care of it.
|
||||||
|
|
||||||
You DO need to call it in `setTimeout`/`setInterval`/`requestAnimationFrame` callbacks, or callbacks from 3rd party libraries.
|
You DO need to call it in `setTimeout`/`setInterval`/`requestAnimationFrame` callbacks, or callbacks from 3rd party libraries.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,7 @@ Mithril.js' `m.request` uses `XMLHttpRequest` instead of `fetch()` for a number
|
||||||
|
|
||||||
Currently, due to lack of browser support, `fetch()` typically requires a [polyfill](https://github.com/github/fetch), which is over 11kb uncompressed - nearly three times larger than Mithril.js' XHR module.
|
Currently, due to lack of browser support, `fetch()` typically requires a [polyfill](https://github.com/github/fetch), which is over 11kb uncompressed - nearly three times larger than Mithril.js' XHR module.
|
||||||
|
|
||||||
Despite being much smaller, Mithril.js' XHR module supports many important and not-so-trivial-to-implement features like [URL interpolation](#dynamic-urls), querystring serialization and [JSON-P requests](jsonp.md), in addition to its ability to integrate seamlessly to Mithril.js' autoredrawing subsystem. The `fetch` polyfill does not support any of those, and requires extra libraries and boilerplates to achieve the same level of functionality.
|
Despite being much smaller, Mithril.js' XHR module supports many important and not-so-trivial-to-implement features like [URL interpolation](#dynamic-urls) and querystring serialization in addition to its ability to integrate seamlessly to Mithril.js' autoredrawing subsystem. The `fetch` polyfill does not support any of those, and requires extra libraries and boilerplates to achieve the same level of functionality.
|
||||||
|
|
||||||
In addition, Mithril.js' XHR module is optimized for JSON-based endpoints and makes that most common case appropriately terse - i.e. `m.request(url)` - whereas `fetch` requires an additional explicit step to parse the response data as JSON: `fetch(url).then(function(response) {return response.json()})`
|
In addition, Mithril.js' XHR module is optimized for JSON-based endpoints and makes that most common case appropriately terse - i.e. `m.request(url)` - whereas `fetch` requires an additional explicit step to parse the response data as JSON: `fetch(url).then(function(response) {return response.json()})`
|
||||||
|
|
||||||
|
|
|
||||||
1
index.js
1
index.js
|
|
@ -14,7 +14,6 @@ m.route = require("./route")
|
||||||
m.render = require("./render")
|
m.render = require("./render")
|
||||||
m.redraw = mountRedraw.redraw
|
m.redraw = mountRedraw.redraw
|
||||||
m.request = request.request
|
m.request = request.request
|
||||||
m.jsonp = request.jsonp
|
|
||||||
m.parseQueryString = require("./querystring/parse")
|
m.parseQueryString = require("./querystring/parse")
|
||||||
m.buildQueryString = require("./querystring/build")
|
m.buildQueryString = require("./querystring/build")
|
||||||
m.parsePathname = require("./pathname/parse")
|
m.parsePathname = require("./pathname/parse")
|
||||||
|
|
|
||||||
19
mithril.js
19
mithril.js
|
|
@ -1379,24 +1379,6 @@ var _17 = function($window, oncompletion) {
|
||||||
else if (body instanceof $window.FormData || body instanceof $window.URLSearchParams) xhr.send(body)
|
else if (body instanceof $window.FormData || body instanceof $window.URLSearchParams) xhr.send(body)
|
||||||
else xhr.send(JSON.stringify(body))
|
else xhr.send(JSON.stringify(body))
|
||||||
}),
|
}),
|
||||||
jsonp: makeRequest(function(url, args, resolve, reject) {
|
|
||||||
var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++
|
|
||||||
var script = $window.document.createElement("script")
|
|
||||||
$window[callbackName] = function(data) {
|
|
||||||
delete $window[callbackName]
|
|
||||||
script.parentNode.removeChild(script)
|
|
||||||
resolve(data)
|
|
||||||
}
|
|
||||||
script.onerror = function() {
|
|
||||||
delete $window[callbackName]
|
|
||||||
script.parentNode.removeChild(script)
|
|
||||||
reject(new Error("JSONP request failed"))
|
|
||||||
}
|
|
||||||
script.src = url + (url.indexOf("?") < 0 ? "?" : "&") +
|
|
||||||
encodeURIComponent(args.callbackKey || "callback") + "=" +
|
|
||||||
encodeURIComponent(callbackName)
|
|
||||||
$window.document.documentElement.appendChild(script)
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var request = _17(typeof window !== "undefined" ? window : null, mountRedraw0.redraw)
|
var request = _17(typeof window !== "undefined" ? window : null, mountRedraw0.redraw)
|
||||||
|
|
@ -1799,7 +1781,6 @@ m.route = _26(typeof window !== "undefined" ? window : null, mountRedraw)
|
||||||
m.render = render
|
m.render = render
|
||||||
m.redraw = mountRedraw.redraw
|
m.redraw = mountRedraw.redraw
|
||||||
m.request = request.request
|
m.request = request.request
|
||||||
m.jsonp = request.jsonp
|
|
||||||
m.parseQueryString = parseQueryString
|
m.parseQueryString = parseQueryString
|
||||||
m.buildQueryString = buildQueryString
|
m.buildQueryString = buildQueryString
|
||||||
m.parsePathname = parsePathname
|
m.parsePathname = parsePathname
|
||||||
|
|
|
||||||
2
mithril.min.js
vendored
2
mithril.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -4,8 +4,6 @@ var buildPathname = require("../pathname/build")
|
||||||
var hasOwn = require("../util/hasOwn")
|
var hasOwn = require("../util/hasOwn")
|
||||||
|
|
||||||
module.exports = function($window, oncompletion) {
|
module.exports = function($window, oncompletion) {
|
||||||
var callbackCount = 0
|
|
||||||
|
|
||||||
function PromiseProxy(executor) {
|
function PromiseProxy(executor) {
|
||||||
return new Promise(executor)
|
return new Promise(executor)
|
||||||
}
|
}
|
||||||
|
|
@ -197,23 +195,5 @@ module.exports = function($window, oncompletion) {
|
||||||
else if (body instanceof $window.FormData || body instanceof $window.URLSearchParams) xhr.send(body)
|
else if (body instanceof $window.FormData || body instanceof $window.URLSearchParams) xhr.send(body)
|
||||||
else xhr.send(JSON.stringify(body))
|
else xhr.send(JSON.stringify(body))
|
||||||
}),
|
}),
|
||||||
jsonp: makeRequest(function(url, args, resolve, reject) {
|
|
||||||
var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++
|
|
||||||
var script = $window.document.createElement("script")
|
|
||||||
$window[callbackName] = function(data) {
|
|
||||||
delete $window[callbackName]
|
|
||||||
script.parentNode.removeChild(script)
|
|
||||||
resolve(data)
|
|
||||||
}
|
|
||||||
script.onerror = function() {
|
|
||||||
delete $window[callbackName]
|
|
||||||
script.parentNode.removeChild(script)
|
|
||||||
reject(new Error("JSONP request failed"))
|
|
||||||
}
|
|
||||||
script.src = url + (url.indexOf("?") < 0 ? "?" : "&") +
|
|
||||||
encodeURIComponent(args.callbackKey || "callback") + "=" +
|
|
||||||
encodeURIComponent(callbackName)
|
|
||||||
$window.document.documentElement.appendChild(script)
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
"use strict"
|
|
||||||
|
|
||||||
var o = require("ospec")
|
|
||||||
var xhrMock = require("../../test-utils/xhrMock")
|
|
||||||
var Request = require("../../request/request")
|
|
||||||
var parseQueryString = require("../../querystring/parse")
|
|
||||||
|
|
||||||
o.spec("jsonp", function() {
|
|
||||||
var mock, jsonp, complete
|
|
||||||
o.beforeEach(function() {
|
|
||||||
mock = xhrMock()
|
|
||||||
complete = o.spy()
|
|
||||||
jsonp = Request(mock, complete).jsonp
|
|
||||||
})
|
|
||||||
|
|
||||||
o("works", function(done) {
|
|
||||||
mock.$defineRoutes({
|
|
||||||
"GET /item": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["callback"] + "(" + JSON.stringify({a: 1}) + ")"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
jsonp({url: "/item"}).then(function(data) {
|
|
||||||
o(data).deepEquals({a: 1})
|
|
||||||
}).then(done)
|
|
||||||
})
|
|
||||||
o("first argument can be a string aliasing url property", function(done){
|
|
||||||
mock.$defineRoutes({
|
|
||||||
"GET /item": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["callback"] + "(" + JSON.stringify({a: 1}) + ")"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
jsonp("/item").then(function(data) {
|
|
||||||
o(data).deepEquals({a: 1})
|
|
||||||
}).then(function() {
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("works w/ other querystring params", function(done) {
|
|
||||||
mock.$defineRoutes({
|
|
||||||
"GET /item": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["callback"] + "(" + JSON.stringify(queryData) + ")"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
jsonp({url: "/item", params: {a: "b", c: "d"}}).then(function(data) {
|
|
||||||
delete data["callback"]
|
|
||||||
o(data).deepEquals({a: "b", c: "d"})
|
|
||||||
}).then(done)
|
|
||||||
})
|
|
||||||
o("works w/ custom callbackKey", function(done) {
|
|
||||||
mock.$defineRoutes({
|
|
||||||
"GET /item": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["cb"] + "(" + JSON.stringify({a: 2}) + ")"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
jsonp({url: "/item", callbackKey: "cb"}).then(function(data) {
|
|
||||||
o(data).deepEquals({a: 2})
|
|
||||||
}).then(done)
|
|
||||||
})
|
|
||||||
o("requests don't block each other", function(done) {
|
|
||||||
mock.$defineRoutes({
|
|
||||||
"GET /item": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["callback"] + "([])"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
jsonp("/item").then(function() {
|
|
||||||
return jsonp("/item")
|
|
||||||
})
|
|
||||||
jsonp("/item").then(function() {
|
|
||||||
return jsonp("/item")
|
|
||||||
})
|
|
||||||
setTimeout(function() {
|
|
||||||
o(complete.callCount).equals(4)
|
|
||||||
done()
|
|
||||||
}, 20)
|
|
||||||
})
|
|
||||||
o("requests trigger finally once with a chained then", function(done) {
|
|
||||||
mock.$defineRoutes({
|
|
||||||
"GET /item": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["callback"] + "([])"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
var promise = jsonp("/item")
|
|
||||||
promise.then(function() {}).then(function() {})
|
|
||||||
promise.then(function() {}).then(function() {})
|
|
||||||
setTimeout(function() {
|
|
||||||
o(complete.callCount).equals(1)
|
|
||||||
done()
|
|
||||||
}, 20)
|
|
||||||
})
|
|
||||||
o("requests does not trigger finally when background: true", function(done) {
|
|
||||||
mock.$defineRoutes({
|
|
||||||
"GET /item": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["callback"] + "([])"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
jsonp("/item", {background: true}).then(function() {})
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
o(complete.callCount).equals(0)
|
|
||||||
done()
|
|
||||||
}, 20)
|
|
||||||
})
|
|
||||||
o("handles error", function(done) {
|
|
||||||
jsonp({url: "/item", callbackKey: "cb"}).catch(function(e) {
|
|
||||||
o(e.message).equals("JSONP request failed")
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
@ -98,75 +98,4 @@ o.spec("xhrMock", function() {
|
||||||
o(xhr.getRequestHeader("Content-Type")).equals("foo, bar")
|
o(xhr.getRequestHeader("Content-Type")).equals("foo, bar")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
o.spec("jsonp", function() {
|
|
||||||
o("works", function(done) {
|
|
||||||
$window.$defineRoutes({
|
|
||||||
"GET /test": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["callback"] + "(" + JSON.stringify({a: 1}) + ")"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$window["cb"] = finish
|
|
||||||
|
|
||||||
var script = $window.document.createElement("script")
|
|
||||||
script.src = "/test?callback=cb"
|
|
||||||
$window.document.documentElement.appendChild(script)
|
|
||||||
|
|
||||||
function finish(data) {
|
|
||||||
o(data).deepEquals({a: 1})
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
o("works w/ custom callback key", function(done) {
|
|
||||||
$window.$defineRoutes({
|
|
||||||
"GET /test": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["cb"] + "(" + JSON.stringify({a: 2}) + ")"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
$window.$defineJSONPCallbackKey("cb")
|
|
||||||
|
|
||||||
$window["customcb"] = finish2
|
|
||||||
|
|
||||||
var script = $window.document.createElement("script")
|
|
||||||
script.src = "/test?cb=customcb"
|
|
||||||
$window.document.documentElement.appendChild(script)
|
|
||||||
|
|
||||||
function finish2(data) {
|
|
||||||
o(data).deepEquals({a: 2})
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
o("works with other querystring params", function(done) {
|
|
||||||
$window.$defineRoutes({
|
|
||||||
"GET /test": function(request) {
|
|
||||||
var queryData = parseQueryString(request.query)
|
|
||||||
return {status: 200, responseText: queryData["callback"] + "(" + JSON.stringify({a: 3}) + ")"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$window["cbwithinparams"] = finish
|
|
||||||
|
|
||||||
var script = $window.document.createElement("script")
|
|
||||||
script.src = "/test?a=b&callback=cbwithinparams&c=d"
|
|
||||||
$window.document.documentElement.appendChild(script)
|
|
||||||
|
|
||||||
function finish(data) {
|
|
||||||
o(data).deepEquals({a: 3})
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
o("handles error", function(done) {
|
|
||||||
var script = $window.document.createElement("script")
|
|
||||||
script.onerror = finish
|
|
||||||
script.src = "/test?cb=nonexistent"
|
|
||||||
$window.document.documentElement.appendChild(script)
|
|
||||||
|
|
||||||
function finish(e) {
|
|
||||||
o(e.type).equals("error")
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -137,9 +137,6 @@ module.exports = function() {
|
||||||
$defineRoutes: function(rules) {
|
$defineRoutes: function(rules) {
|
||||||
routes = rules
|
routes = rules
|
||||||
},
|
},
|
||||||
$defineJSONPCallbackKey: function(/* key */) {
|
|
||||||
// callback = key
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
return $window
|
return $window
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,6 @@ o.spec("api", function() {
|
||||||
o(typeof m.request).equals("function") // TODO improve
|
o(typeof m.request).equals("function") // TODO improve
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
o.spec("m.jsonp", function() {
|
|
||||||
o("works", function() {
|
|
||||||
o(typeof m.jsonp).equals("function") // TODO improve
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o.spec("m.render", function() {
|
o.spec("m.render", function() {
|
||||||
o("works", function() {
|
o("works", function() {
|
||||||
root = window.document.createElement("div")
|
root = window.document.createElement("div")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue