import { DefaultProxy } from "../../Source/Cesium.js"; import { defaultValue } from "../../Source/Cesium.js"; import { queryToObject } from "../../Source/Cesium.js"; import { Request } from "../../Source/Cesium.js"; import { RequestErrorEvent } from "../../Source/Cesium.js"; import { RequestScheduler } from "../../Source/Cesium.js"; import { Resource } from "../../Source/Cesium.js"; import createCanvas from "../createCanvas.js"; import { Uri } from "../../Source/Cesium.js"; import { when } from "../../Source/Cesium.js"; import dataUriToBuffer from "../dataUriToBuffer.js"; describe("Core/Resource", function () { var dataUri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2Nk+M/wHwAEBgIA5agATwAAAABJRU5ErkJggg=="; var supportsImageBitmapOptions; beforeAll(function () { return Resource.supportsImageBitmapOptions().then(function (result) { supportsImageBitmapOptions = result; }); }); it("Constructor sets correct properties", function () { var proxy = new DefaultProxy("/proxy/"); var request = new Request(); function retryFunc() {} var resource = new Resource({ url: "http://test.com/tileset", queryParameters: { key1: "value1", key2: "value2", }, templateValues: { key3: "value3", key4: "value4", }, headers: { Accept: "application/test-type", }, proxy: proxy, retryCallback: retryFunc, retryAttempts: 4, request: request, }); expect(resource.getUrlComponent(false, false)).toEqual( "http://test.com/tileset" ); expect(resource.getUrlComponent(true, false)).toEqual( "http://test.com/tileset?key1=value1&key2=value2" ); expect(resource.getUrlComponent(false, true)).toEqual( proxy.getURL("http://test.com/tileset") ); expect(resource.getUrlComponent(true, true)).toEqual( proxy.getURL("http://test.com/tileset?key1=value1&key2=value2") ); expect(resource.url).toEqual( proxy.getURL("http://test.com/tileset?key1=value1&key2=value2") ); expect(String(resource)).toEqual( proxy.getURL("http://test.com/tileset?key1=value1&key2=value2") ); expect(resource.queryParameters).toEqual({ key1: "value1", key2: "value2", }); expect(resource.templateValues).toEqual({ key3: "value3", key4: "value4", }); expect(resource.headers).toEqual({ Accept: "application/test-type", }); expect(resource.proxy).toBe(proxy); expect(resource.retryCallback).toBe(retryFunc); expect(resource.retryAttempts).toEqual(4); expect(resource._retryCount).toEqual(0); expect(resource.request).toBe(request); }); it("Constructor sets correct properties", function () { var url = "http://invalid.domain.com/tileset"; var resource = new Resource(url); expect(resource.url).toEqual(url); expect(String(resource)).toEqual(url); expect(resource.queryParameters).toEqual({}); expect(resource.templateValues).toEqual({}); expect(resource.headers).toEqual({}); expect(resource.proxy).toBeUndefined(); expect(resource.retryCallback).toBeUndefined(); expect(resource.retryAttempts).toEqual(0); expect(resource.request).toBeDefined(); }); it("_makeRequest returns undefined if the request is throttled", function () { var oldMaximumRequests = RequestScheduler.maximumRequests; RequestScheduler.maximumRequests = 0; var resource = new Resource({ url: "http://example.invalid/testuri", request: new Request({ throttle: true, }), }); var promise = resource._makeRequest({ method: "GET" }); expect(promise).toBeUndefined(); RequestScheduler.maximumRequests = oldMaximumRequests; }); it("appendForwardSlash appends a /", function () { var resource = new Resource({ url: "http://test.com/tileset", }); expect(resource.url).toEqual("http://test.com/tileset"); resource.appendForwardSlash(); expect(resource.url).toEqual("http://test.com/tileset/"); }); it("Setting a url with a query string sets queryParameters correctly", function () { var resource = new Resource({ url: "http://test.com/tileset?foo=bar&baz=foo", }); expect(resource.getUrlComponent()).toEqual("http://test.com/tileset"); expect(resource.getUrlComponent(true)).toEqual( "http://test.com/tileset?foo=bar&baz=foo" ); expect(resource.queryParameters).toEqual({ foo: "bar", baz: "foo", }); }); it("createIfNeeded returns undefined, if parameter is undefined", function () { expect(Resource.createIfNeeded()).toBeUndefined(); }); it("createIfNeeded returns Resource, if parameter is a Resource", function () { var resource = new Resource({ url: "http://test.com/tileset", }); expect(Resource.createIfNeeded(resource)).toEqual(resource); }); it("createIfNeeded returns Resource, if parameter is a String", function () { var resource = Resource.createIfNeeded("http://test.com/tileset"); expect(resource.url).toEqual("http://test.com/tileset"); }); it("multiple values for query parameters are allowed", function () { var resource = new Resource( "http://test.com/tileset/endpoint?a=1&a=2&b=3&a=4" ); expect(resource.queryParameters.a).toEqual(["1", "2", "4"]); expect(resource.queryParameters.b).toEqual("3"); expect(resource.url).toEqual( "http://test.com/tileset/endpoint?a=1&a=2&a=4&b=3" ); }); it("multiple values for query parameters works with getDerivedResource without preserverQueryParameters", function () { var resource = new Resource( "http://test.com/tileset/endpoint?a=1&a=2&b=3&a=4" ); expect(resource.queryParameters.a).toEqual(["1", "2", "4"]); expect(resource.queryParameters.b).toEqual("3"); expect(resource.url).toEqual( "http://test.com/tileset/endpoint?a=1&a=2&a=4&b=3" ); var derived = resource.getDerivedResource({ url: "other_endpoint?a=5&b=6&a=7", }); expect(derived.queryParameters.a).toEqual(["5", "7"]); expect(derived.queryParameters.b).toEqual("6"); expect(derived.url).toEqual( "http://test.com/tileset/other_endpoint?a=5&a=7&b=6" ); }); it("multiple values for query parameters works with getDerivedResource with preserveQueryParameters", function () { var resource = new Resource( "http://test.com/tileset/endpoint?a=1&a=2&b=3&a=4" ); expect(resource.queryParameters.a).toEqual(["1", "2", "4"]); expect(resource.queryParameters.b).toEqual("3"); expect(resource.url).toEqual( "http://test.com/tileset/endpoint?a=1&a=2&a=4&b=3" ); var derived = resource.getDerivedResource({ url: "other_endpoint?a=5&b=6&a=7", preserveQueryParameters: true, }); expect(derived.queryParameters.a).toEqual(["5", "7", "1", "2", "4"]); expect(derived.queryParameters.b).toEqual(["6", "3"]); expect(derived.url).toEqual( "http://test.com/tileset/other_endpoint?a=5&a=7&a=1&a=2&a=4&b=6&b=3" ); }); it("replaces templateValues in the url", function () { var resource = new Resource({ url: "http://test.com/tileset/{foo}/{bar}", templateValues: { foo: "test1", bar: "test2", }, }); expect(resource.url).toEqual("http://test.com/tileset/test1/test2"); }); it("replaces numeric templateValues", function () { var resource = new Resource({ url: "http://test.com/tileset/{0}/{1}", templateValues: { 0: "test1", 1: "test2", }, }); expect(resource.url).toEqual("http://test.com/tileset/test1/test2"); }); it("leaves templateValues unchanged that are not provided", function () { var resource = new Resource({ url: "http://test.com/tileset/{foo}/{bar}", }); expect(resource.url).toEqual("http://test.com/tileset/{foo}/{bar}"); }); it("url encodes replacement templateValues in the url", function () { var resource = new Resource({ url: "http://test.com/tileset/{foo}/{bar}", templateValues: { foo: "a/b", bar: "x$y#", }, }); expect(resource.url).toEqual("http://test.com/tileset/a%2Fb/x%24y%23"); }); it("getDerivedResource sets correct properties", function () { var proxy = new DefaultProxy("/proxy/"); var request = new Request(); function retryFunc() {} var parent = new Resource({ url: "http://test.com/tileset?key=value", queryParameters: { foo: "bar", }, templateValues: { key5: "value5", key6: "value6", }, }); parent.appendForwardSlash(); var resource = parent.getDerivedResource({ url: "tileset.json", queryParameters: { key1: "value1", key2: "value2", }, templateValues: { key3: "value3", key4: "value4", }, headers: { Accept: "application/test-type", }, proxy: proxy, retryCallback: retryFunc, retryAttempts: 4, request: request, }); expect(resource.getUrlComponent(false, false)).toEqual( "http://test.com/tileset/tileset.json" ); expect(resource.getUrlComponent(true, false)).toEqual( "http://test.com/tileset/tileset.json?key1=value1&key2=value2&key=value&foo=bar" ); expect(resource.getUrlComponent(false, true)).toEqual( proxy.getURL("http://test.com/tileset/tileset.json") ); expect(resource.getUrlComponent(true, true)).toEqual( proxy.getURL( "http://test.com/tileset/tileset.json?key1=value1&key2=value2&key=value&foo=bar" ) ); expect(resource.url).toEqual( proxy.getURL( "http://test.com/tileset/tileset.json?key1=value1&key2=value2&key=value&foo=bar" ) ); expect(resource.queryParameters).toEqual({ foo: "bar", key: "value", key1: "value1", key2: "value2", }); expect(resource.templateValues).toEqual({ key5: "value5", key6: "value6", key3: "value3", key4: "value4", }); expect(resource.headers).toEqual({ Accept: "application/test-type", }); expect(resource.proxy).toBe(proxy); expect(resource.retryCallback).toBe(retryFunc); expect(resource.retryAttempts).toEqual(4); expect(resource._retryCount).toEqual(0); expect(resource.request).toBe(request); }); it("getDerivedResource works with directory parent resource", function () { var parent = new Resource({ url: "http://test.com/tileset/", }); expect(parent.url).toEqual("http://test.com/tileset/"); var resource = parent.getDerivedResource({ url: "tileset.json", }); expect(resource.url).toEqual("http://test.com/tileset/tileset.json"); }); it("getDerivedResource works with file parent resource", function () { var parent = new Resource({ url: "http://test.com/tileset/tileset.json", }); expect(parent.url).toEqual("http://test.com/tileset/tileset.json"); var resource = parent.getDerivedResource({ url: "0/0/0.b3dm", }); expect(resource.url).toEqual("http://test.com/tileset/0/0/0.b3dm"); }); it("getDerivedResource works with only template values", function () { var parent = new Resource({ url: "http://test.com/terrain/{z}/{x}/{y}.terrain", }); expect(parent.url).toEqual("http://test.com/terrain/{z}/{x}/{y}.terrain"); var resource = parent.getDerivedResource({ templateValues: { x: 1, y: 2, z: 0, }, }); expect(resource.url).toEqual("http://test.com/terrain/0/1/2.terrain"); }); it("getDerivedResource works with only query parameters", function () { var parent = new Resource({ url: "http://test.com/terrain", }); expect(parent.url).toEqual("http://test.com/terrain"); var resource = parent.getDerivedResource({ queryParameters: { x: 1, y: 2, z: 0, }, }); expect(resource.url).toEqual("http://test.com/terrain?x=1&y=2&z=0"); }); it("setQueryParameters with useAsDefault set to true", function () { var resource = new Resource({ url: "http://test.com/terrain", queryParameters: { x: 1, y: 2, }, }); expect(resource.queryParameters).toEqual({ x: 1, y: 2, }); resource.setQueryParameters( { x: 3, y: 4, z: 0, }, true ); expect(resource.queryParameters).toEqual({ x: 1, y: 2, z: 0, }); }); it("setQueryParameters with useAsDefault set to false", function () { var resource = new Resource({ url: "http://test.com/terrain", queryParameters: { x: 1, y: 2, }, }); expect(resource.queryParameters).toEqual({ x: 1, y: 2, }); resource.setQueryParameters( { x: 3, y: 4, z: 0, }, false ); expect(resource.queryParameters).toEqual({ x: 3, y: 4, z: 0, }); }); it("appendQueryParameters works with non-arrays", function () { var resource = new Resource({ url: "http://test.com/terrain", queryParameters: { x: 1, y: 2, }, }); expect(resource.queryParameters).toEqual({ x: 1, y: 2, }); resource.appendQueryParameters({ x: 3, y: 4, z: 0, }); expect(resource.queryParameters).toEqual({ x: [3, 1], y: [4, 2], z: 0, }); }); it("appendQueryParameters works with arrays/non-arrays", function () { var resource = new Resource({ url: "http://test.com/terrain", queryParameters: { x: [1, 2], y: 2, z: [-1, -2], }, }); expect(resource.queryParameters).toEqual({ x: [1, 2], y: 2, z: [-1, -2], }); resource.appendQueryParameters({ x: 3, y: [4, 5], z: [-3, -4], }); expect(resource.queryParameters).toEqual({ x: [3, 1, 2], y: [4, 5, 2], z: [-3, -4, -1, -2], }); }); it("setTemplateValues with useAsDefault set to true", function () { var resource = new Resource({ url: "http://test.com/terrain/{z}/{x}/{y}.terrain", templateValues: { x: 1, y: 2, map: "my map", }, }); expect(resource.templateValues).toEqual({ x: 1, y: 2, map: "my map", }); resource.setTemplateValues( { x: 3, y: 4, z: 0, style: "my style", }, true ); expect(resource.templateValues).toEqual({ x: 1, y: 2, map: "my map", z: 0, style: "my style", }); }); it("setTemplateValues with useAsDefault set to false", function () { var resource = new Resource({ url: "http://test.com/terrain/{z}/{x}/{y}.terrain", templateValues: { x: 1, y: 2, map: "my map", }, }); expect(resource.templateValues).toEqual({ x: 1, y: 2, map: "my map", }); resource.setTemplateValues( { x: 3, y: 4, z: 0, style: "my style", }, false ); expect(resource.templateValues).toEqual({ x: 3, y: 4, map: "my map", z: 0, style: "my style", }); }); it("retryOnFail doesn't exceed retryAttempts", function () { var cb = jasmine.createSpy("retry").and.returnValue(true); var resource = new Resource({ url: "http://test.com/terrain", retryCallback: cb, retryAttempts: 3, }); var promises = []; for (var i = 0; i < 6; ++i) { promises.push(resource.retryOnError()); } when.all(promises).then(function (result) { expect(result).toEqual([true, true, true, false, false, false]); expect(cb.calls.count()).toEqual(3); expect(resource._retryCount).toEqual(3); }); }); it("retryOnFail returns value from callback", function () { var result = true; var cb = jasmine.createSpy("retry").and.callFake(function () { result = !result; return result; }); var resource = new Resource({ url: "http://test.com/terrain", retryCallback: cb, retryAttempts: 4, }); var promises = []; for (var i = 0; i < 6; ++i) { promises.push(resource.retryOnError()); } when.all(promises).then(function (result) { expect(result).toEqual([false, true, false, true, false, false]); expect(cb.calls.count()).toEqual(4); expect(resource._retryCount).toEqual(4); }); }); it("isDataUri returns correct values", function () { var dataResource = new Resource({ url: "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3", }); expect(dataResource.isDataUri).toBe(true); var resource = new Resource({ url: "http://invalid.uri/tileset", }); expect(resource.isDataUri).toBe(false); }); it("isBlobUri returns correct values", function () { var dataResource = new Resource({ url: "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf", }); expect(dataResource.isBlobUri).toBe(true); var resource = new Resource({ url: "http://invalid.uri/tileset", }); expect(resource.isBlobUri).toBe(false); }); it("post calls with correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResponseType = "json"; var expectedData = { stuff: "myStuff", }; var expectedHeaders = { "X-My-Header": "My-Value", }; var expectedResult = { status: "success", }; var expectedMimeType = "application/test-data"; var resource = new Resource({ url: expectedUrl, headers: expectedHeaders, }); spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(responseType).toEqual(expectedResponseType); expect(method).toEqual("POST"); expect(data).toEqual(expectedData); expect(headers["X-My-Header"]).toEqual("My-Value"); expect(headers["X-My-Other-Header"]).toEqual("My-Other-Value"); expect(overrideMimeType).toBe(expectedMimeType); deferred.resolve(expectedResult); }); return resource .post(expectedData, { responseType: expectedResponseType, headers: { "X-My-Other-Header": "My-Other-Value", }, overrideMimeType: expectedMimeType, }) .then(function (result) { expect(result).toEqual(expectedResult); }); }); it("static post calls with correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResponseType = "json"; var expectedData = { stuff: "myStuff", }; var expectedHeaders = { "X-My-Header": "My-Value", }; var expectedResult = { status: "success", }; var expectedMimeType = "application/test-data"; spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(responseType).toEqual(expectedResponseType); expect(method).toEqual("POST"); expect(data).toEqual(expectedData); expect(headers).toEqual(expectedHeaders); expect(overrideMimeType).toBe(expectedMimeType); deferred.resolve(expectedResult); }); return Resource.post({ url: expectedUrl, data: expectedData, responseType: expectedResponseType, headers: expectedHeaders, overrideMimeType: expectedMimeType, }).then(function (result) { expect(result).toEqual(expectedResult); }); }); it("put calls with correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResponseType = "json"; var expectedData = { stuff: "myStuff", }; var expectedHeaders = { "X-My-Header": "My-Value", }; var expectedResult = { status: "success", }; var expectedMimeType = "application/test-data"; var resource = new Resource({ url: expectedUrl, headers: expectedHeaders, }); spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(responseType).toEqual(expectedResponseType); expect(method).toEqual("PUT"); expect(data).toEqual(expectedData); expect(headers["X-My-Header"]).toEqual("My-Value"); expect(headers["X-My-Other-Header"]).toEqual("My-Other-Value"); expect(overrideMimeType).toBe(expectedMimeType); deferred.resolve(expectedResult); }); return resource .put(expectedData, { responseType: expectedResponseType, headers: { "X-My-Other-Header": "My-Other-Value", }, overrideMimeType: expectedMimeType, }) .then(function (result) { expect(result).toEqual(expectedResult); }); }); it("static put calls with correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResponseType = "json"; var expectedData = { stuff: "myStuff", }; var expectedHeaders = { "X-My-Header": "My-Value", }; var expectedResult = { status: "success", }; var expectedMimeType = "application/test-data"; spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(responseType).toEqual(expectedResponseType); expect(method).toEqual("PUT"); expect(data).toEqual(expectedData); expect(headers).toEqual(expectedHeaders); expect(overrideMimeType).toBe(expectedMimeType); deferred.resolve(expectedResult); }); return Resource.put({ url: expectedUrl, data: expectedData, responseType: expectedResponseType, headers: expectedHeaders, overrideMimeType: expectedMimeType, }).then(function (result) { expect(result).toEqual(expectedResult); }); }); it("patch calls with correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResponseType = "json"; var expectedData = { stuff: "myStuff", }; var expectedHeaders = { "X-My-Header": "My-Value", }; var expectedResult = { status: "success", }; var expectedMimeType = "application/test-data"; var resource = new Resource({ url: expectedUrl, headers: expectedHeaders, }); spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(responseType).toEqual(expectedResponseType); expect(method).toEqual("PATCH"); expect(data).toEqual(expectedData); expect(headers["X-My-Header"]).toEqual("My-Value"); expect(headers["X-My-Other-Header"]).toEqual("My-Other-Value"); expect(overrideMimeType).toBe(expectedMimeType); deferred.resolve(expectedResult); }); return resource .patch(expectedData, { responseType: expectedResponseType, headers: { "X-My-Other-Header": "My-Other-Value", }, overrideMimeType: expectedMimeType, }) .then(function (result) { expect(result).toEqual(expectedResult); }); }); it("static patch calls with correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResponseType = "json"; var expectedData = { stuff: "myStuff", }; var expectedHeaders = { "X-My-Header": "My-Value", }; var expectedResult = { status: "success", }; var expectedMimeType = "application/test-data"; spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(responseType).toEqual(expectedResponseType); expect(method).toEqual("PATCH"); expect(data).toEqual(expectedData); expect(headers).toEqual(expectedHeaders); expect(overrideMimeType).toBe(expectedMimeType); deferred.resolve(expectedResult); }); return Resource.patch({ url: expectedUrl, data: expectedData, responseType: expectedResponseType, headers: expectedHeaders, overrideMimeType: expectedMimeType, }).then(function (result) { expect(result).toEqual(expectedResult); }); }); it("static fetchArrayBuffer calls correct method", function () { var url = "http://test.com/data"; var expectedResult = when.resolve(); spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue( expectedResult ); var result = Resource.fetchArrayBuffer(url); expect(result).toBe(expectedResult); return Resource.fetchArrayBuffer(url).then(function () { expect(Resource.prototype.fetchArrayBuffer).toHaveBeenCalled(); }); }); it("fetchArrayBuffer calls fetch with expected parameters", function () { var url = "http://test.com/data"; var expectedResult = when.resolve(); spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult); var result = Resource.fetchArrayBuffer(url); expect(result).toBe(expectedResult); return result.then(function () { expect(Resource.prototype.fetch).toHaveBeenCalledWith({ responseType: "arraybuffer", }); }); }); it("static fetchBlob calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "fetchBlob").and.returnValue(when.resolve()); return Resource.fetchBlob(url).then(function () { expect(Resource.prototype.fetchBlob).toHaveBeenCalled(); }); }); it("fetchBlob calls fetch with expected parameters", function () { var url = "http://test.com/data"; var expectedResult = when.resolve(); spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult); var result = Resource.fetchBlob(url); expect(result).toBe(expectedResult); return result.then(function () { expect(Resource.prototype.fetch).toHaveBeenCalledWith({ responseType: "blob", }); }); }); it("fetchArrayBuffer calls fetch with expected parameters", function () { var url = "http://test.com/data"; var expectedResult = when.resolve(); spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult); var result = Resource.fetchArrayBuffer(url); expect(result).toBe(expectedResult); return result.then(function () { expect(Resource.prototype.fetch).toHaveBeenCalledWith({ responseType: "arraybuffer", }); }); }); it("static fetchImage calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "fetchImage").and.returnValue(when.resolve()); return Resource.fetchImage(url).then(function () { expect(Resource.prototype.fetchImage).toHaveBeenCalled(); }); }); it("static fetchText calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "fetchText").and.returnValue(when.resolve()); return Resource.fetchText(url).then(function () { expect(Resource.prototype.fetchText).toHaveBeenCalled(); }); }); it("fetchText calls fetch with expected parameters", function () { var url = "http://test.com/data"; var expectedResult = when.resolve(); spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult); var result = Resource.fetchText(url); expect(result).toBe(expectedResult); return result.then(function () { expect(Resource.prototype.fetch).toHaveBeenCalledWith({ responseType: "text", }); }); }); it("static fetchJson calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "fetchJson").and.returnValue(when.resolve()); return Resource.fetchJson(url).then(function () { expect(Resource.prototype.fetchJson).toHaveBeenCalled(); }); }); it("fetchJson calls fetch with expected parameters and parses result", function () { var expectedResult = { x: 123 }; spyOn(Resource.prototype, "fetch").and.returnValue( when.resolve(JSON.stringify(expectedResult)) ); return Resource.fetchJson("url").then(function (result) { expect(result).toEqual(expectedResult); expect(Resource.prototype.fetch).toHaveBeenCalledWith({ responseType: "text", headers: { Accept: "application/json,*/*;q=0.01", }, }); }); }); it("static fetchXML calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "fetchXML").and.returnValue(when.resolve()); return Resource.fetchXML(url).then(function () { expect(Resource.prototype.fetchXML).toHaveBeenCalled(); }); }); it("fetchXML calls fetch with expected parameters", function () { var url = "http://test.com/data"; var expectedResult = when.resolve(); spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult); var result = Resource.fetchXML(url); expect(result).toBe(expectedResult); return result.then(function () { expect(Resource.prototype.fetch).toHaveBeenCalledWith({ responseType: "document", overrideMimeType: "text/xml", }); }); }); it("static fetchJsonp calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "fetchJsonp").and.returnValue(when.resolve()); return Resource.fetchJsonp(url).then(function () { expect(Resource.prototype.fetchJsonp).toHaveBeenCalled(); }); }); it("static fetch calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "fetch").and.returnValue(when.resolve()); return Resource.fetch(url).then(function () { expect(Resource.prototype.fetch).toHaveBeenCalled(); }); }); it("fetch calls correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResult = { status: "success", }; spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(method).toEqual("GET"); deferred.resolve(expectedResult); }); var resource = new Resource({ url: expectedUrl }); return resource.fetch().then(function (result) { expect(result).toEqual(expectedResult); }); }); it("static delete calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "delete").and.returnValue(when.resolve()); return Resource.delete(url).then(function () { expect(Resource.prototype.delete).toHaveBeenCalled(); }); }); it("delete calls correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResult = { status: "success", }; spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(method).toEqual("DELETE"); deferred.resolve(expectedResult); }); var resource = new Resource({ url: expectedUrl }); return resource.delete().then(function (result) { expect(result).toEqual(expectedResult); }); }); it("static head calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "head").and.returnValue(when.resolve({})); return Resource.head(url).then(function () { expect(Resource.prototype.head).toHaveBeenCalled(); }); }); it("head calls correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResult = { "accept-ranges": "bytes", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", "access-control-allow-origin": "*", "cache-control": "public, max-age=0", connection: "keep-alive", "content-length": "883", "content-type": "image/png", date: "Tue, 13 Feb 2018 03:38:55 GMT", etag: 'W/"373-15e34d146a1"', vary: "Accept-Encoding", "x-powered-vy": "Express", }; var headerString = ""; for (var key in expectedResult) { if (expectedResult.hasOwnProperty(key)) { headerString += key + ": " + expectedResult[key] + "\r\n"; } } var fakeXHR = { status: 200, send: function () { this.onload(); }, open: function () {}, getAllResponseHeaders: function () { return headerString; }, }; spyOn(window, "XMLHttpRequest").and.returnValue(fakeXHR); spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(method).toEqual("HEAD"); Resource._DefaultImplementations.loadWithXhr( url, responseType, method, data, headers, deferred, overrideMimeType ); }); var resource = new Resource({ url: expectedUrl }); return resource.head().then(function (result) { expect(result.date).toEqual(expectedResult.date); expect(result["last-modified"]).toEqual(expectedResult["last-modified"]); expect(result["x-powered-by"]).toEqual(expectedResult["x-powered-by"]); expect(result.etag).toEqual(expectedResult.etag); expect(result["content-type"]).toEqual(expectedResult["content-type"]); expect(result["access-control-allow-origin"]).toEqual( expectedResult["access-control-allow-origin"] ); expect(result["cache-control"]).toEqual(expectedResult["cache-control"]); expect(result["accept-ranges"]).toEqual(expectedResult["accept-ranges"]); expect(result["access-control-allow-headers"]).toEqual( expectedResult["access-control-allow-headers"] ); expect(result["content-length"]).toEqual( expectedResult["content-length"] ); }); }); it("static options calls correct method", function () { var url = "http://test.com/data"; spyOn(Resource.prototype, "options").and.returnValue(when.resolve({})); return Resource.options(url).then(function () { expect(Resource.prototype.options).toHaveBeenCalled(); }); }); it("options calls correct method", function () { var expectedUrl = "http://test.com/endpoint"; var expectedResult = { "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", "access-control-allow-methods": "GET, PUT, POST, DELETE, OPTIONS", "access-control-allow-origin": "*", connection: "keep-alive", "content-length": "2", "content-type": "text/plain; charset=utf-8", date: "Tue, 13 Feb 2018 03:38:55 GMT", etag: 'W/"2-nOO9QiTIwXgNtWtBJezz8kv3SLc"', vary: "Accept-Encoding", "x-powered-vy": "Express", }; var headerString = ""; for (var key in expectedResult) { if (expectedResult.hasOwnProperty(key)) { headerString += key + ": " + expectedResult[key] + "\r\n"; } } var fakeXHR = { status: 200, send: function () { this.onload(); }, open: function () {}, getAllResponseHeaders: function () { return headerString; }, }; spyOn(window, "XMLHttpRequest").and.returnValue(fakeXHR); spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(method).toEqual("OPTIONS"); Resource._DefaultImplementations.loadWithXhr( url, responseType, method, data, headers, deferred, overrideMimeType ); }); var resource = new Resource({ url: expectedUrl }); return resource.options().then(function (result) { expect(result.date).toEqual(expectedResult.date); expect(result["x-powered-by"]).toEqual(expectedResult["x-powered-by"]); expect(result.etag).toEqual(expectedResult.etag); expect(result["content-type"]).toEqual(expectedResult["content-type"]); expect(result["access-control-allow-origin"]).toEqual( expectedResult["access-control-allow-origin"] ); expect(result["access-control-allow-methods"]).toEqual( expectedResult["access-control-allow-methods"] ); expect(result["access-control-allow-headers"]).toEqual( expectedResult["access-control-allow-headers"] ); expect(result["content-length"]).toEqual( expectedResult["content-length"] ); }); }); it("can load an SVG", function () { return Resource.fetchImage("./Data/Images/Red16x16.svg").then(function ( loadedImage ) { expect(loadedImage.width).toEqual(16); expect(loadedImage.height).toEqual(16); }); }); it("can load an image preferring blob", function () { return Resource.fetchImage("./Data/Images/Green.png", true).then(function ( loadedImage ) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); }); }); it("can load an image from a data URI", function () { return Resource.fetchImage(dataUri).then(function (loadedImage) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); }); }); describe("fetchImage with ImageBitmap", function () { var canvas; beforeAll(function () { canvas = createCanvas(1, 2); }); afterAll(function () { document.body.removeChild(canvas); }); function getColorAtPixel(image, x, y) { var context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); var imageData = context.getImageData(0, 0, 1, 1); return [ imageData.data[0], imageData.data[1], imageData.data[2], imageData.data[3], ]; } it("can call supportsImageBitmapOptions", function () { if (!supportsImageBitmapOptions) { return; } return Resource.supportsImageBitmapOptions().then(function (result) { expect(typeof result).toEqual("boolean"); }); }); it("can load and decode an image", function () { if (!supportsImageBitmapOptions) { return; } return Resource.fetchImage({ url: "./Data/Images/Green.png", preferImageBitmap: true, }).then(function (loadedImage) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); expect(loadedImage).toBeInstanceOf(ImageBitmap); }); }); it("correctly flips image when ImageBitmapOptions are supported", function () { if (!supportsImageBitmapOptions) { return; } var loadedImage; return Resource.fetchImage({ url: "./Data/Images/BlueOverRed.png", flipY: true, preferImageBitmap: true, }) .then(function (image) { loadedImage = image; return Resource.supportsImageBitmapOptions(); }) .then(function (supportsImageBitmapOptions) { if (supportsImageBitmapOptions) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 255, 0, 0, 255, ]); } else { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 0, 0, 255, 255, ]); } }); }); it("correctly loads image without flip when ImageBitmapOptions are supported", function () { if (!supportsImageBitmapOptions) { return; } var loadedImage; return Resource.fetchImage({ url: "./Data/Images/BlueOverRed.png", flipY: false, preferImageBitmap: true, }) .then(function (image) { loadedImage = image; return Resource.supportsImageBitmapOptions(); }) .then(function (supportsImageBitmapOptions) { if (supportsImageBitmapOptions) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 0, 0, 255, 255, ]); } else { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 0, 0, 255, 255, ]); } }); }); it("correctly ignores gamma color profile when ImageBitmapOptions are supported", function () { if (!supportsImageBitmapOptions) { return; } var loadedImage; return Resource.fetchImage({ url: "./Data/Images/Gamma.png", flipY: false, skipColorSpaceConversion: true, preferImageBitmap: true, }) .then(function (image) { loadedImage = image; return Resource.supportsImageBitmapOptions(); }) .then(function (supportsImageBitmapOptions) { if (supportsImageBitmapOptions) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 0, 136, 0, 255, ]); } else { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 59, 0, 255]); } }); }); it("correctly allows gamma color profile when ImageBitmapOptions are supported", function () { if (!supportsImageBitmapOptions) { return; } var loadedImage; return Resource.fetchImage({ url: "./Data/Images/Gamma.png", flipY: false, skipColorSpaceConversion: false, preferImageBitmap: true, }) .then(function (image) { loadedImage = image; return Resource.supportsImageBitmapOptions(); }) .then(function (supportsImageBitmapOptions) { if (supportsImageBitmapOptions) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 59, 0, 255]); } else { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 59, 0, 255]); } }); }); it("correctly ignores custom color profile when ImageBitmapOptions are supported", function () { if (!supportsImageBitmapOptions) { return; } var loadedImage; return Resource.fetchImage({ url: "./Data/Images/CustomColorProfile.png", flipY: false, skipColorSpaceConversion: true, preferImageBitmap: true, }) .then(function (image) { loadedImage = image; return Resource.supportsImageBitmapOptions(); }) .then(function (supportsImageBitmapOptions) { if (supportsImageBitmapOptions) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 0, 136, 0, 255, ]); } else { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 193, 0, 0, 255, ]); } }); }); it("correctly allows custom color profile when ImageBitmapOptions are supported", function () { if (!supportsImageBitmapOptions) { return; } var loadedImage; return Resource.fetchImage({ url: "./Data/Images/CustomColorProfile.png", flipY: false, skipColorSpaceConversion: false, preferImageBitmap: true, }) .then(function (image) { loadedImage = image; return Resource.supportsImageBitmapOptions(); }) .then(function (supportsImageBitmapOptions) { if (supportsImageBitmapOptions) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 193, 0, 0, 255, ]); } else { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([ 193, 0, 0, 255, ]); } }); }); it("does not use ImageBitmap when ImageBitmapOptions are not supported", function () { if (!supportsImageBitmapOptions) { return; } spyOn(Resource, "supportsImageBitmapOptions").and.returnValue( when.resolve(false) ); spyOn(window, "createImageBitmap").and.callThrough(); return Resource.fetchImage({ url: "./Data/Images/Green.png", preferImageBitmap: true, }).then(function (loadedImage) { expect(window.createImageBitmap).not.toHaveBeenCalledWith(); }); }); it("rejects the promise when the image errors", function () { if (!supportsImageBitmapOptions) { return; } return Resource.fetchImage({ url: "http://example.invalid/testuri.png", preferImageBitmap: true, }) .then(function () { fail("expected promise to reject"); }) .otherwise(function (error) { expect(error).toBeInstanceOf(RequestErrorEvent); }); }); it("rejects the promise with extra error information when image errors and options.preferBlob is true", function () { if (!supportsImageBitmapOptions) { return; } // Force the fetching of a bad blob that is not an image to trigger the error spyOn(Resource.prototype, "fetch").and.returnValue( when.resolve(new Blob([new Uint8Array([])], { type: "text/plain" })) ); return Resource.fetchImage({ url: "http://example.invalid/testuri.png", preferImageBitmap: true, preferBlob: true, }) .then(function () { fail("expected promise to reject"); }) .otherwise(function (error) { expect(error.blob).toBeInstanceOf(Blob); }); }); }); describe("fetchImage without ImageBitmap", function () { beforeAll(function () { // Force it to use the Image constructor since these specs all test // specific functionality of this code path. For example, the crossOrigin // restriction does not apply to images loaded with ImageBitmap. spyOn(Resource, "supportsImageBitmapOptions").and.returnValue( when.resolve(false) ); }); it("can load an image", function () { return Resource.fetchImage("./Data/Images/Green.png").then(function ( loadedImage ) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); }); }); it("sets the crossOrigin property for cross-origin images", function () { var fakeImage = {}; var imageConstructorSpy = spyOn(window, "Image").and.returnValue( fakeImage ); Resource.fetchImage("http://example.invalid/someImage.png"); expect(imageConstructorSpy).toHaveBeenCalled(); expect(fakeImage.crossOrigin).toEqual(""); }); it("does not set the crossOrigin property for non-cross-origin images", function () { var fakeImage = {}; var imageConstructorSpy = spyOn(window, "Image").and.returnValue( fakeImage ); Resource.fetchImage("./someImage.png"); expect(imageConstructorSpy).toHaveBeenCalled(); expect(fakeImage.crossOrigin).toBeUndefined(); }); it("does not set the crossOrigin property for data URIs", function () { var fakeImage = {}; var imageConstructorSpy = spyOn(window, "Image").and.returnValue( fakeImage ); Resource.fetchImage(dataUri); expect(imageConstructorSpy).toHaveBeenCalled(); expect(fakeImage.crossOrigin).toBeUndefined(); }); it("resolves the promise when the image loads", function () { var fakeImage = {}; spyOn(window, "Image").and.returnValue(fakeImage); var success = false; var failure = false; var loadedImage; when( Resource.fetchImage(dataUri), function (image) { success = true; loadedImage = image; }, function () { failure = true; } ); // neither callback has fired yet expect(success).toEqual(false); expect(failure).toEqual(false); fakeImage.onload(); expect(success).toEqual(true); expect(failure).toEqual(false); expect(loadedImage).toBe(fakeImage); }); it("rejects the promise when the image errors", function () { var fakeImage = {}; spyOn(window, "Image").and.returnValue(fakeImage); var success = false; var failure = false; var loadedImage; when( Resource.fetchImage(dataUri), function (image) { success = true; loadedImage = image; }, function () { failure = true; } ); // neither callback has fired yet expect(success).toEqual(false); expect(failure).toEqual(false); fakeImage.onerror(); expect(success).toEqual(false); expect(failure).toEqual(true); expect(loadedImage).toBeUndefined(); }); it("Calls loadWithXhr with blob response type if headers is set", function () { var expectedUrl = "http://example.invalid/testuri.png"; var expectedHeaders = { "X-my-header": "my-value", }; spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { expect(url).toEqual(expectedUrl); expect(headers).toEqual(expectedHeaders); expect(responseType).toEqual("blob"); var binary = dataUriToBuffer(dataUri); deferred.resolve(new Blob([binary], { type: "image/png" })); }); var testResource = new Resource({ url: expectedUrl, headers: expectedHeaders, }); var promise = testResource.fetchImage(); expect(promise).toBeDefined(); return promise.then(function (image) { expect(image).toBeDefined(); }); }); it("Doesn't call loadWithXhr with blob response type if headers is set but is a data URI", function () { spyOn(Resource._Implementations, "loadWithXhr").and.callFake(function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { deferred.reject("this shouldn't happen"); }); spyOn(Resource._Implementations, "createImage") .and.callFake(function (url, crossOrigin, deferred) { expect(url).toEqual(dataUri); }) .and.callThrough(); var testResource = new Resource({ url: dataUri, headers: { "X-my-header": "my-value", }, }); var promise = testResource.fetchImage(); expect(promise).toBeDefined(); return promise.then(function (image) { expect(image).toBeDefined(); }); }); describe("retries when Resource has the callback set", function () { it("rejects after too many retries", function () { var fakeImage = {}; spyOn(window, "Image").and.returnValue(fakeImage); var cb = jasmine.createSpy("retry").and.returnValue(true); var resource = new Resource({ url: "http://example.invalid/image.png", retryCallback: cb, retryAttempts: 1, }); var promise = resource.fetchImage(); expect(promise).toBeDefined(); var success = false; var failure = false; promise .then(function () { success = true; }) .otherwise(function () { failure = true; }); expect(success).toBe(false); expect(failure).toBe(false); fakeImage.onerror("some error"); // This should retry expect(success).toBe(false); expect(failure).toBe(false); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1]).toEqual("some error"); fakeImage.onerror(); // This fails because we only retry once expect(success).toBe(false); expect(failure).toBe(true); }); it("rejects after callback returns false", function () { var fakeImage = {}; spyOn(window, "Image").and.returnValue(fakeImage); var cb = jasmine.createSpy("retry").and.returnValue(false); var resource = new Resource({ url: "http://example.invalid/image.png", retryCallback: cb, retryAttempts: 2, }); var promise = resource.fetchImage(); expect(promise).toBeDefined(); var success = false; var failure = false; promise .then(function (value) { success = true; }) .otherwise(function (error) { failure = true; }); expect(success).toBe(false); expect(failure).toBe(false); fakeImage.onerror("some error"); // This fails because the callback returns false expect(success).toBe(false); expect(failure).toBe(true); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1]).toEqual("some error"); }); it("resolves after retry", function () { var fakeImage = {}; spyOn(window, "Image").and.returnValue(fakeImage); var cb = jasmine.createSpy("retry").and.returnValue(true); var resource = new Resource({ url: "http://example.invalid/image.png", retryCallback: cb, retryAttempts: 1, }); var promise = resource.fetchImage(); expect(promise).toBeDefined(); var success = false; var failure = false; promise .then(function (value) { success = true; }) .otherwise(function (error) { failure = true; }); expect(success).toBe(false); expect(failure).toBe(false); fakeImage.onerror("some error"); // This should retry expect(success).toBe(false); expect(failure).toBe(false); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1]).toEqual("some error"); fakeImage.onload(); expect(success).toBe(true); expect(failure).toBe(false); }); }); }); describe("loadWithXhr", function () { var loadWithXhr = function (options) { var resource = new Resource(options); return resource._makeRequest({ responseType: options.responseType, overrideMimeType: options.overrideMimeType, method: defaultValue(options.method, "GET"), data: options.data, }); }; describe("data URI loading", function () { it("can load URI escaped text with default response type", function () { return loadWithXhr({ url: "data:,Hello%2C%20World!", }).then(function (result) { expect(result).toEqual("Hello, World!"); }); }); it("can load URI escaped text with responseType=text", function () { return loadWithXhr({ url: "data:,Hello%2C%20World!", responseType: "text", }).then(function (result) { expect(result).toEqual("Hello, World!"); }); }); it("can load Base64 encoded text with default response type", function () { return loadWithXhr({ url: "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==", }).then(function (result) { expect(result).toEqual("Hello, World!"); }); }); it("can load Base64 encoded text with responseType=text", function () { return loadWithXhr({ url: "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==", responseType: "text", }).then(function (result) { expect(result).toEqual("Hello, World!"); }); }); it("can load Base64 & URI encoded text with default responseType", function () { return loadWithXhr({ url: "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D", }).then(function (result) { expect(result).toEqual("Hello, World!"); }); }); it("can load Base64 & URI encoded text with responseType=text", function () { return loadWithXhr({ url: "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D", responseType: "text", }).then(function (result) { expect(result).toEqual("Hello, World!"); }); }); it("can load URI escaped HTML as text with default responseType", function () { return loadWithXhr({ url: "data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E", }).then(function (result) { expect(result).toEqual("

Hello, World!

"); }); }); it("can load URI escaped HTML as text with responseType=text", function () { return loadWithXhr({ url: "data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E", responseType: "text", }).then(function (result) { expect(result).toEqual("

Hello, World!

"); }); }); it("can load URI escaped text as JSON", function () { return loadWithXhr({ url: "data:application/json,%7B%22key%22%3A%22value%22%7D", responseType: "json", }).then(function (result) { expect(result.key).toEqual("value"); }); }); it("can load Base64 encoded text as JSON", function () { return loadWithXhr({ url: "data:application/json;base64,eyJrZXkiOiJ2YWx1ZSJ9", responseType: "json", }).then(function (result) { expect(result.key).toEqual("value"); }); }); it("can load URI escaped HTML as document", function () { return loadWithXhr({ url: "data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E", responseType: "document", }).then(function (result) { expect(result.querySelector("h1")).not.toBeNull(); }); }); var tile = "data:;base64,rZ+P95jW+j0AAABAplRYwQAAAAAAAAAAFwEcw/RY1UUqDN/so3WJQETWnpq/aabA19EOeYzEh8D+6WPqtldYQYlaPeMGcRRAO+KDbxWIcsMAAAAAAAAAANsAAAD+N/03/mf/UQDq/f/+T/8foDBgL/8TACwAWP8H/xf/H/8b/yMAGP8r/wv/H/8P/w8ACP8PAEj/F/8TADwAIAAg/w8AMAAg/wcAGP8v/xf/J4gPIBOnIv8P/wf/FwAo/xfkM+MXACwACAAAAAgAGAAA/w//B/8P/wf/D/8XmgmZGf8XAAgAEBYZFQkAGAAQABj/DwAQAAgAEAAA/w8ACP8P/w8AIP8v2QD9DScR/w8AGAAQABAACAAQABwABAAIAAAAAAAI/xf/HwAQ/y8ADP8j/wf/D/8HRAlDEQAAABAAIP8fADAAFP8P/xP/H/8H5wf4Ag8TAAAABAAEAAD/D/8HAAAAAAAANhLKBf8HABAAIAAA/y95GHoIAAD/D/8DAAz/BwAAAAD/DwAAAAAAAAAAABD/Gz4Vwhb/G/8XAKz9t/4XAAj/D/0P/g8AAP0PAAAAAP4P/Q8AAAAAAAD+//8TABT/PwBA/y8AGP8n/w8AKAAYABAAAAAAAAAAAP8PABD/DwAQAAD/DwAIAAgAAP8PABD9//4P/Q/+H/8P/Q/+EwAYAAz/H/8PABj9H/43ABAACP1PAADWBdUF/gMADv0R/g8AEP8LdwCHBwAI/wsACP8HAAAACAAMAAQACP8DAAwACP8P/wcAIP8PABAACAAE/xsAAP8HABj/H/8HABT/CwAQABAAAL4WFwSlAv8PABAAAAAIAAh0A4wEAAD/B/8P/w8AAAAQAAgAEAAQ/wcAAAAI9wQHCwAAABAAAAgI+Af/BwAA/wcAEAAA/xf/BwAYAAAAGAAAAAD/DwAQuw/4AMQO/w8AGAAAAAAAEP8HAAT/E/8HABAAIFESUgoACP8D/wsAFP8T/w//D/8H1hvVA/8PACAADAAEAAgABAAQ/xMACP8HYREzDGkK/w8AHP8j/w8AAAAY/w8AMP8P5hoaHf8PACD/BwAQAAQ5HjoS/yf/HwAU/xv/B/8P/w8AEP8PACAAEAAgAAj/G7ItTh7/CwAIAAQACP8f/w//D/8P/w//DwAQ/w//DwAAAEAAEAAQABAAEP8TAAT/B/8H/wcACP8HABj/JwAIAAD/D/8P/w//D/8PAAD/DwAA/w8AAP8PAAD/D/8H/wcAQAAA/w8AAAAA/w8ABAAAABT/JwAIAAD/BwAYABD/D/83ABBwbM1nul3BXggB7QCqADoARH8JagIPNQkNGk5YCQunTVhLcUvMAAsBWAAbAKkArQCgAckA0AAXABwAGwCCAL4AHgqlCgIWfxVhAGAA3wByAKTgYdIxDm8AIwAxAIYAQwAYqNmncha+DYckCgDaADcAiwCuAs4GFAUvBlcI0vb59iAAYQAHAHbgr986A34DMgGBCbIAOABKAH4AlwDMABUB4QC4AdkBDm0OV83CDQCdAPsAPgAuAdwA6AAzAD4W3QmLC+Rs3WzvANwAgQPOCxEAuwomAFUAXoP9gygAKiIvEVYKWQUlFJsAWgQUBrYPnGdRNw9MQgAVAGAAlAA4AHMAkgAXASYApJgDkeZO+VX8BVQRkxb8w2vEpQB0AEcAHgCOAIAAWwCgAFMACQApAE0AKAA5AHTgz99zAD4AtABVAK4ApwCfAD8ApgAeAGMAagBYAD8AwwBMACAAEwBkAAgBLQCKZhlmQgA+AJxXuxLVRE8ATQCAAfIkBxvDA6kIEQA5AAIAPgA1ACwAEAAcADIAFwCSALkAIgGlAZwAnADrAJkAKQAJAMYAcwAaAG0ADwAFALcBuACSAQAAAAAAAAAAAwAAAAMAAwAAAAMABAACAAAABgAAAAAABQAIAAEAAgAIAAAABwABAAkABwAAAAAAAAADAAoACAABAAoAAgAEAAoACAAAAAAACgAAAAQAAQALAAIAAQAAAAUAAQAAAAYABgABAAgAAAAJAAAAAQAKAAMACgACAAkACwAJAAAACgADAAEAAQAOAAwAAAAPAAIADwABAAAAAQAAABEAEAARAAEAFAAQAAAAAQARAAIAAgADAAAAAwABAAAABAABAAMAAAAGAAcABgABAAAABAAHAAAAAQAIAAIAAAAKAAsACgABAAQACwANAAEAAAAOAA0AAQACAA4AAAAAAA8AEQACAA8ADwABAAMAEgACABEAEgATAAAAAQADABMAFAAAAAIAAQAVABYAFgAXAAEAAgABAAAAAgAAAAIAAQADABkABAACAAAAAQAHAAUABwABAAAABwAIAAEACQAHAAAAAAAJAAAACgAAAAQAAgALAAUACwADAAEAAAAPAA4AAQAOAAUADwABABIAAgAAAAIAEwACAAEABgADAAIAEwAAABMAFAACAAEAAgAAAAAAAgAEAAYAAwAEAAEACAACAAYACAAHAAAACQAAAAQAAgABAAoAAAAKAAAACwAOAAAABQAMAAMAAgAMAAEADwAQAAAAAgAQAAEAAAACABIAEgAUAAEAAAADAAIAAAAEAAIABAABAAUABQABAAYAAAAIAAcABwACAAEACAAAAAAAAwACAAoAAQAMAAoAAAAMAA0AAQANAAIAAAANAAAAAAASAA8AAwABAA8ADwAEAAIAEQASAAEAAAASAAAAEwACABUAAwABABMAAAAEAAYAAQACAAQAAAAAAAgACAABAAMABwACAAgAAgAHAAAAAQAIAAoACgAAAAIADAABAAsADQABAAwADQAOAAAAAgAOAAAAAQAPAAAAAwABABAAEgAAABIABAASAAEAEwAAAAIAFQABABQAAQAAAAMAAwAAAAcABAACAAEAAQAFAAcABQAAAAAABwAAAAkAAQAIAAIAAwAIAAAAAQAJAAUAAgAAAAsACwABAAAADAABAA4ADwAOAAEAAAASABAAAgABABAAAQARABIAAAASAAIAEgABAAAAFQATAAEAAAAEAAAABgAHAAUAAQAFAAcABQACAAQABwAAAAIACAAKAAEACgAAAAIAAQALAAAAAQAMAA0ADgABAA0AAAACAA8AAQAPAAAADQABABAAEQASAA0AEgABAA0AAQASAAAAAgABAAAAAQAEAAMAAAAFAAAAAAAHAAMAAQAIAAcAAgAHAAQACAAAAAoAAQAJAAAACgADAAEACwAAAA0ADAADAAAAAQACAA0ADwAOAAIAAgAQAA8AAgAAABEAEwARAAAAAQASAAIAAQAAABUAFAAVAAEAAQAAABUAFQABACkAAgAAAAAAAgAEAAUAAwAEAAAAAQAFAAIABwAAAAcABwABAAQAAAAAAAoAAwAKAAEACgAMAAIAAAANAAwADQABAAMAAAACAA0ADQAQAAEAAAAAAAQABgAEAAEAAwACAAQAAQAFAAYABQAAAAAABwADAAIABwAAAAoAAQAIAAIAAAAOAAsACwACAAEADQAOAAEAAAAAAA8ADwABAA4AAgAPAAAAAQAQAAQAAAATABAAEAADAAEAPAA7ABMAAQA8ABMAPAABAAAAAQACAAAAAwAFAAEABQAGAAAAAAAHAAIAAwAHAAEACAAAAAAACAACAAoAAQAEAAoACAAMAAAAAQADAAkADAANAAkADgABAA0ADwAAAA8AAgAPAAAAAQAQAAIAAAASAAAAEwAUAAEABAATAAIAFAAVAAEAAQAVAAAAAwACAAEAAQAAAAQAAQAGAAQAAAAHAAAACAACAAcAAQAIAAMAAAAKAAgACAADAAEADAAKAAEADAAAAAwAAgAAAA4AAgAOAAEAAAARAA4ADgADAAEAAAAAABMAAwACABMAEwABABIAAAAVABMAAQATAAIAAgAAAAAABAABAAMABQACAAQABgAIAAUAAAADAAYABgAJAAEAAAACAAoACQABAAoAAAAKAAAAAwALAAAAAQAMAAMAAgAMAA4ADgAQAAIAAAADABEAEQAQAAEAAAAAABIAFAACABIAAwASAAEAFAAVAAIAAgAAAAIAAwAWAAAAAAADAAUAAgABAAUAAAAFAAQABQABAAcAAAAKAAgACAACAAEACgABAAsACwABAAAACgAMAAEACgAAAAAAAQAPAAwAAwACAAwAAAAQAAIAEAABABEAAQATABEAAQAAAAAAAAAEAAIAAAAXAAUABQACAAEABAAFAAYABgAHAAQAAAAIAAkACQAAAAIACwABAAoACwAMAAEAAQAMAAAAEAABAA0AEAAOAAAAAgARAAEAAAAQABEAAQARAAAAAgABAAAAAgATAAAAAwAAAAMABAAAAAAABgAEAAIAAQADAAYABABCAAAAQwAFAAAABAAGAAIAAQAGABkABAAAAAQAVQAFAAMABQBVAAEAAAAHAAUABQACAAEABwABAAAAAgADAFoAAgAAAAIAAQADAFsAWwAEAFkAWQAEAFcABABYAFcAWAAGAEkABgBIAEkAMwBIAAUAHQAzAAUAWwBaAAEAAQBaAAAAAABcAF4AXAABAAIAAAACAGAAXwBgAAIAAAAAAAMAAgADAGIAYgB0AAIAAAACAAMAdQABAAMAAgABAAAAAgAAAAIAAwB3AHgAAQADAAAAAQAEAHkAAAADAAIAAwABAAAAewCPAAMAAwCPAAIAAAADAAAAkQCSAAQABAACAAMAAQAEAJIAAQAAAAMAkwCmAAIApgABAAIApgClAAEAIQAAACEAIQABAAAAAQA4ACIAAAACAAMAOQACADgAOAACAAAAAAACAAQABAADAAEAAwAAAAIAAAAEAAMAAwACAAEABAABAAAAPQAFAD8AAQAAAAYAQAAGAAEAAAAEAAAABAAFAAAABgAHAAIAAQAGAAMABwAAAAMAAQAEAAMAvQC+AAQABAABAL0ABAC+AAIAvgAGAAIAAAAGAAcAAQAHALsAuwAHAL8ABgABAAAABwBaAEYAWgAHAAEAAQACAAAAvgADAL0AAQADAL4AAgBcAFsAAQCZAAIAAgCZAFwArACZAAEAvgCsAAEAXACZAJgAwAAAAMMAwQAFAAAAAgDCAAEA2AACANYAxAACANgAEQAAAAEAnwCoANUA2QAFANoAzgDLAMkApwCmAKMAqgCsAK0AqwACAAAAAQAEABEAAAAEAK4ADAAkAMYAxQDIAMIAwQC/ALsAugC5ALIAsAC8AL0AAgAAAJ8ArgA="; it("can load Base64 encoded data as arraybuffer", function () { return loadWithXhr({ url: tile, responseType: "arraybuffer", }).then(function (result) { expect(result.byteLength).toEqual(3914); }); }); var image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEISURBVEhLvVXBDYQwDOuojHKj8LhBbpTbpBCEkZsmIVTXq1RVQGrHiWlLmTTqPiZBlyLgy/KSZQ5JSHDQ/mCYCsC8106kDU0AdwRnvYZArWRcAl0dcYJq1hWCb3hBrumbDAVMwAC82WoRvgMnVMDBnB0nYZFTbE6BBvdUGqVqCbjBIk3PyFFR/NU7EKzru+qZsau3ryPwwCRLKYOzutZuCL6fUmWeJGzNzL/RxAMrUmASSCkkAayk2IxPlwhAAYGpsiHQjbLccfdOY5gKkCXAMi7SscAwbQpAnKyctWyUZ6z8ja3OGMepwD8asz+9FnSvbhU8uVOHFIwQsI3/p0CfhuqCSQuxLqsN6mu8SS+N42MAAAAASUVORK5CYII="; it("can load Base64 encoded data as blob", function () { return loadWithXhr({ url: image, responseType: "blob", }).then(function (result) { expect(result.type).toEqual("image/png"); var blobUrl = URL.createObjectURL(result); return Resource.fetchImage(blobUrl).then(function (image) { expect(image.width).toEqual(24); expect(image.height).toEqual(24); }); }); }); xit("can support 2xx HTTP status (other than 200)", function () { return loadWithXhr({ method: "POST", url: "http://jsonplaceholder.typicode.com/posts", data: { title: "foo", body: "bar", userId: 1, }, }).then(function (result) { expect(JSON.parse(result).id).toEqual(101); }); }); }); describe("URL loading using XHR", function () { describe("returns a promise that rejects when the request", function () { it("results in an HTTP status code greater than or equal to 300", function () { return loadWithXhr({ url: "http://example.invalid", }) .then(function () { fail("expected promise to reject"); }) .otherwise(function (err) { expect(err).toBeInstanceOf(RequestErrorEvent); }); }); it("loads an invalid JSON string response with a json responseType", function () { return loadWithXhr({ url: "Data/htmlString.txt", responseType: "json", }) .then(function () { fail("expected promise to reject"); }) .otherwise(function (err) { expect(err).toBeDefined(); expect(err).toBeInstanceOf(Error); }); }); }); describe("returns a promise that resolves when the request loads", function () { it("a non-null response with default responseType", function () { return loadWithXhr({ url: "Data/Models/Box/ReadMe.txt", }).then(function (result) { expect(result).toBe( "CesiumBoxTest-NoTechnique.gltf is a modified glTF that has techniques, shaders & programs removed." ); }); }); it("a non-null response with a browser-supported responseType", function () { return loadWithXhr({ url: "Data/Models/Box/ReadMe.txt", responseType: "text", }).then(function (result) { expect(result).toBe( "CesiumBoxTest-NoTechnique.gltf is a modified glTF that has techniques, shaders & programs removed." ); }); }); it("a valid JSON string response as JSON with a json responseType", function () { return loadWithXhr({ url: "Data/jsonString.txt", responseType: "json", }).then(function (result) { expect(result).toEqual( jasmine.objectContaining({ hello: "world" }) ); }); }); }); }); describe("URL loading using mocked XHR", function () { var fakeXHR; beforeEach(function () { fakeXHR = jasmine.createSpyObj("XMLHttpRequest", [ "send", "open", "setRequestHeader", "abort", "getAllResponseHeaders", ]); fakeXHR.simulateError = function () { fakeXHR.response = ""; if (typeof fakeXHR.onerror === "function") { fakeXHR.onerror(); } }; fakeXHR.simulateHttpResponse = function (statusCode, response) { fakeXHR.status = statusCode; fakeXHR.response = response; if (typeof fakeXHR.onload === "function") { fakeXHR.onload(); } }; fakeXHR.simulateResponseXMLLoad = function (responseXML) { fakeXHR.status = 200; fakeXHR.responseXML = responseXML; if (typeof fakeXHR.onload === "function") { fakeXHR.onload(); } }; fakeXHR.simulateResponseTextLoad = function (responseText) { fakeXHR.simulateHttpResponse(200, responseText); }; spyOn(window, "XMLHttpRequest").and.returnValue(fakeXHR); }); describe("returns a promise that rejects when the request", function () { it("errors", function () { var promise = loadWithXhr({ url: "http://example.invalid", }); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); fakeXHR.simulateError(); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeInstanceOf(RequestErrorEvent); }); it("results in an HTTP status code less than 200", function () { var promise = loadWithXhr({ url: "http://example.invalid", }); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); fakeXHR.simulateHttpResponse(199); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeInstanceOf(RequestErrorEvent); }); it("is an image with status code 204 with preferImageBitmap", function () { if (!supportsImageBitmapOptions) { return; } var promise = Resource.fetchImage({ url: "./Data/Images/Green.png", preferImageBitmap: true, }); expect(promise).toBeDefined(); var resolved = false; var resolvedValue; var rejectedError; promise .then(function (value) { resolved = true; resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); fakeXHR.simulateHttpResponse(204); expect(resolved).toBe(false); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeDefined(); }); it("resolves undefined for status code 204", function () { var promise = loadWithXhr({ url: "http://example.invalid", }); expect(promise).toBeDefined(); var resolved = false; var resolvedValue; var rejectedError; promise .then(function (value) { resolved = true; resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); fakeXHR.simulateHttpResponse(204); expect(resolved).toBe(true); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); }); }); describe("returns a promise that resolves when the request loads", function () { it("a null response with a '' responseType and non-null responseXML with child nodes", function () { var promise = loadWithXhr({ url: "http://example.invalid", responseType: "", }); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); var responseXML = { hasChildNodes: jasmine .createSpy("hasChildNodes") .and.returnValue(true), }; fakeXHR.simulateResponseXMLLoad(responseXML); expect(resolvedValue).toEqual(responseXML); expect(rejectedError).toBeUndefined(); }); it("a null response with a document responseType and non-null responseXML with child nodes", function () { var promise = loadWithXhr({ url: "http://example.invalid", responseType: "document", }); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); var responseXML = { hasChildNodes: jasmine .createSpy("hasChildNodes") .and.returnValue(true), }; fakeXHR.simulateResponseXMLLoad(responseXML); expect(resolvedValue).toEqual(responseXML); expect(rejectedError).toBeUndefined(); }); it("a null response with a '' responseType and non-null responseText", function () { var promise = loadWithXhr({ url: "http://example.invalid", responseType: "", }); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); var responseText = "hello world"; fakeXHR.simulateResponseTextLoad(responseText); expect(resolvedValue).toEqual(responseText); expect(rejectedError).toBeUndefined(); }); it("a null response with a text responseType and non-null responseText", function () { var promise = loadWithXhr({ url: "http://example.invalid", responseType: "text", }); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); var responseText = "hello world"; fakeXHR.simulateResponseTextLoad(responseText); expect(resolvedValue).toEqual(responseText); expect(rejectedError).toBeUndefined(); }); }); describe("retries when Resource has the callback set", function () { it("rejects after too many retries", function () { var cb = jasmine.createSpy("retry").and.returnValue(true); var resource = new Resource({ url: "http://example.invalid", retryCallback: cb, retryAttempts: 1, }); var promise = loadWithXhr(resource); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); fakeXHR.simulateError(); // This should retry expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1] instanceof RequestErrorEvent).toBe( true ); fakeXHR.simulateError(); // This fails because we only retry once expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeInstanceOf(RequestErrorEvent); }); it("rejects after callback returns false", function () { var cb = jasmine.createSpy("retry").and.returnValue(false); var resource = new Resource({ url: "http://example.invalid", retryCallback: cb, retryAttempts: 2, }); var promise = loadWithXhr(resource); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); fakeXHR.simulateError(); // This fails because the callback returns false expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeInstanceOf(RequestErrorEvent); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1] instanceof RequestErrorEvent).toBe( true ); }); it("resolves after retry", function () { var cb = jasmine.createSpy("retry").and.returnValue(true); var resource = new Resource({ url: "http://example.invalid", retryCallback: cb, retryAttempts: 1, }); var promise = loadWithXhr(resource); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); fakeXHR.simulateError(); // This should retry expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1] instanceof RequestErrorEvent).toBe( true ); fakeXHR.simulateHttpResponse(200, "OK"); expect(resolvedValue).toBeDefined(); expect(rejectedError).toBeUndefined(); }); }); }); }); describe("fetchJsonp", function () { it("returns a promise that resolves when the request loads", function () { var testUrl = "http://example.invalid/testuri"; spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake( function (url, name, deffered) { expect(url).toContain(testUrl); expect(name).toContain("loadJsonp"); expect(deffered).toBeDefined(); } ); Resource.fetchJsonp(testUrl); }); it("returns a promise that rejects when the request errors", function () { var testUrl = "http://example.invalid/testuri"; return Resource.fetchJsonp(testUrl).otherwise(function (error) { expect(error).toBeDefined(); }); }); it("Uses callback name specified in options", function () { var testUrl = "test"; var options = { callbackParameterName: "testCallback", }; spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake( function (url, functionName, deferred) { expect(url).toContain("callback=loadJsonp"); } ); Resource.fetchJsonp(testUrl, options); }); describe("retries when Resource has the callback set", function () { it("rejects after too many retries", function () { //var cb = jasmine.createSpy('retry').and.returnValue(true); var cb = jasmine .createSpy("retry") .and.callFake(function (resource, error) { return true; }); var lastDeferred; spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake( function (url, functionName, deferred) { lastDeferred = deferred; } ); var resource = new Resource({ url: "http://example.invalid", retryCallback: cb, retryAttempts: 1, }); var promise = resource.fetchJsonp(); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); lastDeferred.reject("some error"); // This should retry expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1]).toEqual("some error"); lastDeferred.reject("another error"); // This fails because we only retry once expect(resolvedValue).toBeUndefined(); expect(rejectedError).toEqual("another error"); }); it("rejects after callback returns false", function () { var cb = jasmine.createSpy("retry").and.returnValue(false); var lastDeferred; spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake( function (url, functionName, deferred) { lastDeferred = deferred; } ); var resource = new Resource({ url: "http://example.invalid", retryCallback: cb, retryAttempts: 2, }); var promise = resource.fetchJsonp(); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); lastDeferred.reject("some error"); // This fails because the callback returns false expect(resolvedValue).toBeUndefined(); expect(rejectedError).toEqual("some error"); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1]).toEqual("some error"); }); it("resolves after retry", function () { var cb = jasmine.createSpy("retry").and.returnValue(true); var lastDeferred; var lastUrl; spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake( function (url, functionName, deferred) { lastUrl = url; lastDeferred = deferred; } ); var resource = new Resource({ url: "http://example.invalid", retryCallback: cb, retryAttempts: 1, }); var promise = resource.fetchJsonp(); expect(promise).toBeDefined(); var resolvedValue; var rejectedError; promise .then(function (value) { resolvedValue = value; }) .otherwise(function (error) { rejectedError = error; }); expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); lastDeferred.reject("some error"); // This should retry expect(resolvedValue).toBeUndefined(); expect(rejectedError).toBeUndefined(); expect(cb.calls.count()).toEqual(1); var receivedResource = cb.calls.argsFor(0)[0]; expect(receivedResource.url).toEqual(resource.url); expect(receivedResource._retryCount).toEqual(1); expect(cb.calls.argsFor(0)[1]).toEqual("some error"); var uri = new Uri(lastUrl); var query = queryToObject(uri.query()); window[query.callback]("something good"); expect(resolvedValue).toEqual("something good"); expect(rejectedError).toBeUndefined(); }); }); }); });