mirror of https://github.com/CesiumGS/cesium.git
2744 lines
86 KiB
JavaScript
2744 lines
86 KiB
JavaScript
import Uri from "urijs";
|
|
import {
|
|
DefaultProxy,
|
|
defer,
|
|
defined,
|
|
queryToObject,
|
|
Request,
|
|
RequestErrorEvent,
|
|
RequestScheduler,
|
|
Resource,
|
|
} from "../../index.js";
|
|
import createCanvas from "../../../../Specs/createCanvas.js";
|
|
import dataUriToBuffer from "../../../../Specs/dataUriToBuffer.js";
|
|
import pollToPromise from "../../../../Specs/pollToPromise.js";
|
|
|
|
describe("Core/Resource", function () {
|
|
const dataUri =
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2Nk+M/wHwAEBgIA5agATwAAAABJRU5ErkJggg==";
|
|
let supportsImageBitmapOptions;
|
|
|
|
beforeAll(function () {
|
|
return Resource.supportsImageBitmapOptions().then(function (result) {
|
|
supportsImageBitmapOptions = result;
|
|
});
|
|
});
|
|
|
|
it("Constructor sets correct properties", function () {
|
|
const proxy = new DefaultProxy("/proxy/");
|
|
const request = new Request();
|
|
function retryFunc() {}
|
|
|
|
const 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 () {
|
|
const url = "http://invalid.domain.com/tileset";
|
|
const 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 () {
|
|
const oldMaximumRequests = RequestScheduler.maximumRequests;
|
|
RequestScheduler.maximumRequests = 0;
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid/testuri",
|
|
request: new Request({
|
|
throttle: true,
|
|
}),
|
|
});
|
|
const promise = resource._makeRequest({ method: "GET" });
|
|
expect(promise).toBeUndefined();
|
|
|
|
RequestScheduler.maximumRequests = oldMaximumRequests;
|
|
return promise;
|
|
});
|
|
|
|
it("appendForwardSlash appends a /", function () {
|
|
const 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 () {
|
|
const 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("Constructing with parseUrl false does not strip query parameters from url", function () {
|
|
const resource = new Resource({
|
|
url: "http://test.com/tileset?foo=bar&baz=foo",
|
|
parseUrl: false,
|
|
});
|
|
expect(resource.getUrlComponent()).toEqual(
|
|
"http://test.com/tileset?foo=bar&baz=foo",
|
|
);
|
|
expect(resource.getUrlComponent(true)).toEqual(
|
|
"http://test.com/tileset?foo=bar&baz=foo",
|
|
);
|
|
expect(resource.queryParameters).toEqual({});
|
|
});
|
|
|
|
it("createIfNeeded returns undefined, if parameter is undefined", function () {
|
|
expect(Resource.createIfNeeded()).toBeUndefined();
|
|
});
|
|
|
|
it("createIfNeeded returns Resource, if parameter is a Resource", function () {
|
|
const resource = new Resource({
|
|
url: "http://test.com/tileset",
|
|
});
|
|
expect(Resource.createIfNeeded(resource)).toEqual(resource);
|
|
});
|
|
|
|
it("createIfNeeded returns Resource, if parameter is a String", function () {
|
|
const resource = Resource.createIfNeeded("http://test.com/tileset");
|
|
expect(resource.url).toEqual("http://test.com/tileset");
|
|
});
|
|
|
|
it("multiple values for query parameters are allowed", function () {
|
|
const 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 () {
|
|
const 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",
|
|
);
|
|
|
|
const 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 () {
|
|
const 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",
|
|
);
|
|
|
|
const 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 () {
|
|
const 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 () {
|
|
const 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 () {
|
|
const 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 () {
|
|
const 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 () {
|
|
const proxy = new DefaultProxy("/proxy/");
|
|
const request = new Request();
|
|
function retryFunc() {}
|
|
|
|
const parent = new Resource({
|
|
url: "http://test.com/tileset?key=value",
|
|
queryParameters: {
|
|
foo: "bar",
|
|
},
|
|
templateValues: {
|
|
key5: "value5",
|
|
key6: "value6",
|
|
},
|
|
});
|
|
parent.appendForwardSlash();
|
|
|
|
const 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 () {
|
|
const parent = new Resource({
|
|
url: "http://test.com/tileset/",
|
|
});
|
|
|
|
expect(parent.url).toEqual("http://test.com/tileset/");
|
|
|
|
const resource = parent.getDerivedResource({
|
|
url: "tileset.json",
|
|
});
|
|
|
|
expect(resource.url).toEqual("http://test.com/tileset/tileset.json");
|
|
});
|
|
|
|
it("getDerivedResource works with file parent resource", function () {
|
|
const parent = new Resource({
|
|
url: "http://test.com/tileset/tileset.json",
|
|
});
|
|
|
|
expect(parent.url).toEqual("http://test.com/tileset/tileset.json");
|
|
|
|
const 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 () {
|
|
const parent = new Resource({
|
|
url: "http://test.com/terrain/{z}/{x}/{y}.terrain",
|
|
});
|
|
|
|
expect(parent.url).toEqual("http://test.com/terrain/{z}/{x}/{y}.terrain");
|
|
|
|
const 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 () {
|
|
const parent = new Resource({
|
|
url: "http://test.com/terrain",
|
|
});
|
|
|
|
expect(parent.url).toEqual("http://test.com/terrain");
|
|
|
|
const 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 () {
|
|
const 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 () {
|
|
const 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 () {
|
|
const 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 () {
|
|
const 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 () {
|
|
const 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 () {
|
|
const 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 () {
|
|
const cb = jasmine.createSpy("retry").and.returnValue(true);
|
|
const resource = new Resource({
|
|
url: "http://test.com/terrain",
|
|
retryCallback: cb,
|
|
retryAttempts: 3,
|
|
});
|
|
|
|
let promise = resource.retryOnError();
|
|
const promises = [promise];
|
|
for (let i = 1; i < 6; ++i) {
|
|
promise = promise.then(function (result) {
|
|
return resource.retryOnError();
|
|
});
|
|
promises.push(promise);
|
|
}
|
|
|
|
return Promise.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 () {
|
|
let result = true;
|
|
const cb = jasmine.createSpy("retry").and.callFake(function () {
|
|
result = !result;
|
|
return result;
|
|
});
|
|
|
|
const resource = new Resource({
|
|
url: "http://test.com/terrain",
|
|
retryCallback: cb,
|
|
retryAttempts: 4,
|
|
});
|
|
|
|
let promise = resource.retryOnError();
|
|
const promises = [promise];
|
|
for (let i = 1; i < 6; ++i) {
|
|
promise = promise.then(function (result) {
|
|
return resource.retryOnError();
|
|
});
|
|
promises.push(promise);
|
|
}
|
|
|
|
return Promise.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 () {
|
|
const dataResource = new Resource({
|
|
url: "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3",
|
|
});
|
|
|
|
expect(dataResource.isDataUri).toBe(true);
|
|
|
|
const resource = new Resource({
|
|
url: "http://invalid.uri/tileset",
|
|
});
|
|
|
|
expect(resource.isDataUri).toBe(false);
|
|
});
|
|
|
|
it("isBlobUri returns correct values", function () {
|
|
const dataResource = new Resource({
|
|
url: "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf",
|
|
});
|
|
|
|
expect(dataResource.isBlobUri).toBe(true);
|
|
|
|
const resource = new Resource({
|
|
url: "http://invalid.uri/tileset",
|
|
});
|
|
|
|
expect(resource.isBlobUri).toBe(false);
|
|
});
|
|
|
|
it("post calls with correct method", function () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const expectedResponseType = "json";
|
|
const expectedData = {
|
|
stuff: "myStuff",
|
|
};
|
|
const expectedHeaders = {
|
|
"X-My-Header": "My-Value",
|
|
};
|
|
const expectedResult = {
|
|
status: "success",
|
|
};
|
|
const expectedMimeType = "application/test-data";
|
|
const 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 () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const expectedResponseType = "json";
|
|
const expectedData = {
|
|
stuff: "myStuff",
|
|
};
|
|
const expectedHeaders = {
|
|
"X-My-Header": "My-Value",
|
|
};
|
|
const expectedResult = {
|
|
status: "success",
|
|
};
|
|
const 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 () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const expectedResponseType = "json";
|
|
const expectedData = {
|
|
stuff: "myStuff",
|
|
};
|
|
const expectedHeaders = {
|
|
"X-My-Header": "My-Value",
|
|
};
|
|
const expectedResult = {
|
|
status: "success",
|
|
};
|
|
const expectedMimeType = "application/test-data";
|
|
const 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 () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const expectedResponseType = "json";
|
|
const expectedData = {
|
|
stuff: "myStuff",
|
|
};
|
|
const expectedHeaders = {
|
|
"X-My-Header": "My-Value",
|
|
};
|
|
const expectedResult = {
|
|
status: "success",
|
|
};
|
|
const 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 () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const expectedResponseType = "json";
|
|
const expectedData = {
|
|
stuff: "myStuff",
|
|
};
|
|
const expectedHeaders = {
|
|
"X-My-Header": "My-Value",
|
|
};
|
|
const expectedResult = {
|
|
status: "success",
|
|
};
|
|
const expectedMimeType = "application/test-data";
|
|
const 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 () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const expectedResponseType = "json";
|
|
const expectedData = {
|
|
stuff: "myStuff",
|
|
};
|
|
const expectedHeaders = {
|
|
"X-My-Header": "My-Value",
|
|
};
|
|
const expectedResult = {
|
|
status: "success",
|
|
};
|
|
const 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 () {
|
|
const url = "http://test.com/data";
|
|
const expectedResult = Promise.resolve();
|
|
spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
|
|
expectedResult,
|
|
);
|
|
const 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 () {
|
|
const url = "http://test.com/data";
|
|
const expectedResult = Promise.resolve();
|
|
spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult);
|
|
const 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 () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "fetchBlob").and.returnValue(Promise.resolve());
|
|
return Resource.fetchBlob(url).then(function () {
|
|
expect(Resource.prototype.fetchBlob).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("fetchBlob calls fetch with expected parameters", function () {
|
|
const url = "http://test.com/data";
|
|
const expectedResult = Promise.resolve();
|
|
spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult);
|
|
const 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 () {
|
|
const url = "http://test.com/data";
|
|
const expectedResult = Promise.resolve();
|
|
spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult);
|
|
const 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 () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "fetchImage").and.returnValue(Promise.resolve());
|
|
return Resource.fetchImage(url).then(function () {
|
|
expect(Resource.prototype.fetchImage).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("static fetchText calls correct method", function () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "fetchText").and.returnValue(Promise.resolve());
|
|
return Resource.fetchText(url).then(function () {
|
|
expect(Resource.prototype.fetchText).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("fetchText calls fetch with expected parameters", function () {
|
|
const url = "http://test.com/data";
|
|
const expectedResult = Promise.resolve();
|
|
spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult);
|
|
const 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 () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "fetchJson").and.returnValue(Promise.resolve());
|
|
return Resource.fetchJson(url).then(function () {
|
|
expect(Resource.prototype.fetchJson).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("fetchJson calls fetch with expected parameters and parses result", function () {
|
|
const expectedResult = { x: 123 };
|
|
spyOn(Resource.prototype, "fetch").and.returnValue(
|
|
Promise.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 () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "fetchXML").and.returnValue(Promise.resolve());
|
|
return Resource.fetchXML(url).then(function () {
|
|
expect(Resource.prototype.fetchXML).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("fetchXML calls fetch with expected parameters", function () {
|
|
const url = "http://test.com/data";
|
|
const expectedResult = Promise.resolve();
|
|
spyOn(Resource.prototype, "fetch").and.returnValue(expectedResult);
|
|
const 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 () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "fetchJsonp").and.returnValue(Promise.resolve());
|
|
return Resource.fetchJsonp(url).then(function () {
|
|
expect(Resource.prototype.fetchJsonp).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("static fetch calls correct method", function () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "fetch").and.returnValue(Promise.resolve());
|
|
return Resource.fetch(url).then(function () {
|
|
expect(Resource.prototype.fetch).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("fetch calls correct method", function () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const 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);
|
|
},
|
|
);
|
|
|
|
const resource = new Resource({ url: expectedUrl });
|
|
return resource.fetch().then(function (result) {
|
|
expect(result).toEqual(expectedResult);
|
|
});
|
|
});
|
|
|
|
it("static delete calls correct method", function () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "delete").and.returnValue(Promise.resolve());
|
|
return Resource.delete(url).then(function () {
|
|
expect(Resource.prototype.delete).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("delete calls correct method", function () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const 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);
|
|
},
|
|
);
|
|
|
|
const resource = new Resource({ url: expectedUrl });
|
|
return resource.delete().then(function (result) {
|
|
expect(result).toEqual(expectedResult);
|
|
});
|
|
});
|
|
|
|
it("static head calls correct method", function () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "head").and.returnValue(Promise.resolve({}));
|
|
return Resource.head(url).then(function () {
|
|
expect(Resource.prototype.head).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("head calls correct method", function () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const 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",
|
|
};
|
|
let headerString = "";
|
|
for (const key in expectedResult) {
|
|
if (expectedResult.hasOwnProperty(key)) {
|
|
headerString += `${key}: ${expectedResult[key]}\r\n`;
|
|
}
|
|
}
|
|
const 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,
|
|
);
|
|
},
|
|
);
|
|
|
|
const 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 () {
|
|
const url = "http://test.com/data";
|
|
spyOn(Resource.prototype, "options").and.returnValue(Promise.resolve({}));
|
|
return Resource.options(url).then(function () {
|
|
expect(Resource.prototype.options).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("options calls correct method", function () {
|
|
const expectedUrl = "http://test.com/endpoint";
|
|
const 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",
|
|
};
|
|
let headerString = "";
|
|
for (const key in expectedResult) {
|
|
if (expectedResult.hasOwnProperty(key)) {
|
|
headerString += `${key}: ${expectedResult[key]}\r\n`;
|
|
}
|
|
}
|
|
const 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,
|
|
);
|
|
},
|
|
);
|
|
|
|
const 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 a dimensionless SVG", function () {
|
|
return Resource.fetchImage("./Data/Images/Blue.svg").then(
|
|
function (loadedImage) {
|
|
expect(loadedImage.width).toBeGreaterThan(0);
|
|
expect(loadedImage.height).toBeGreaterThan(0);
|
|
},
|
|
);
|
|
});
|
|
|
|
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 () {
|
|
let canvas;
|
|
beforeAll(function () {
|
|
canvas = createCanvas(1, 2);
|
|
});
|
|
|
|
afterAll(function () {
|
|
document.body.removeChild(canvas);
|
|
});
|
|
|
|
function getColorAtPixel(image, x, y) {
|
|
const context = canvas.getContext("2d");
|
|
context.drawImage(image, 0, 0, image.width, image.height);
|
|
const 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;
|
|
}
|
|
|
|
return Resource.fetchImage({
|
|
url: "./Data/Images/BlueOverRed.png",
|
|
flipY: true,
|
|
preferImageBitmap: true,
|
|
}).then(function (loadedImage) {
|
|
expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([255, 0, 0, 255]);
|
|
});
|
|
});
|
|
|
|
it("correctly loads image without flip when ImageBitmapOptions are supported", function () {
|
|
if (!supportsImageBitmapOptions) {
|
|
return;
|
|
}
|
|
|
|
return Resource.fetchImage({
|
|
url: "./Data/Images/BlueOverRed.png",
|
|
flipY: false,
|
|
preferImageBitmap: true,
|
|
}).then(function (loadedImage) {
|
|
expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 0, 255, 255]);
|
|
});
|
|
});
|
|
|
|
it("correctly ignores gamma color profile when ImageBitmapOptions are supported", function () {
|
|
if (!supportsImageBitmapOptions) {
|
|
return;
|
|
}
|
|
|
|
return Resource.fetchImage({
|
|
url: "./Data/Images/Gamma.png",
|
|
flipY: false,
|
|
skipColorSpaceConversion: true,
|
|
preferImageBitmap: true,
|
|
}).then(function (loadedImage) {
|
|
expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 136, 0, 255]);
|
|
});
|
|
});
|
|
|
|
it("correctly allows gamma color profile when ImageBitmapOptions are supported", function () {
|
|
if (!supportsImageBitmapOptions) {
|
|
return;
|
|
}
|
|
|
|
return Resource.fetchImage({
|
|
url: "./Data/Images/Gamma.png",
|
|
flipY: false,
|
|
skipColorSpaceConversion: false,
|
|
preferImageBitmap: true,
|
|
}).then(function (loadedImage) {
|
|
expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 59, 0, 255]);
|
|
});
|
|
});
|
|
|
|
it("correctly ignores custom color profile when ImageBitmapOptions are supported", function () {
|
|
if (!supportsImageBitmapOptions) {
|
|
return;
|
|
}
|
|
|
|
return Resource.fetchImage({
|
|
url: "./Data/Images/CustomColorProfile.png",
|
|
flipY: false,
|
|
skipColorSpaceConversion: true,
|
|
preferImageBitmap: true,
|
|
}).then(function (loadedImage) {
|
|
expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 136, 0, 255]);
|
|
});
|
|
});
|
|
|
|
it("correctly allows custom color profile when ImageBitmapOptions are supported", function () {
|
|
if (!supportsImageBitmapOptions) {
|
|
return;
|
|
}
|
|
|
|
return Resource.fetchImage({
|
|
url: "./Data/Images/CustomColorProfile.png",
|
|
flipY: false,
|
|
skipColorSpaceConversion: false,
|
|
preferImageBitmap: true,
|
|
}).then(function (loadedImage) {
|
|
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(
|
|
Promise.resolve(false),
|
|
);
|
|
spyOn(window, "createImageBitmap").and.callThrough();
|
|
|
|
return Resource.fetchImage({
|
|
url: "./Data/Images/Green.png",
|
|
preferImageBitmap: true,
|
|
}).then(function () {
|
|
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");
|
|
})
|
|
.catch(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(
|
|
Promise.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");
|
|
})
|
|
.catch(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(
|
|
Promise.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 () {
|
|
const fakeImage = {};
|
|
const deferred = defer();
|
|
const imageConstructorSpy = spyOn(window, "Image").and.callFake(
|
|
function () {
|
|
deferred.resolve();
|
|
return fakeImage;
|
|
},
|
|
);
|
|
|
|
// mock image loading so that the promise resolves
|
|
deferred.promise.then(function () {
|
|
fakeImage.onload();
|
|
});
|
|
|
|
return Resource.fetchImage("http://example.invalid/someImage.png").then(
|
|
function () {
|
|
expect(imageConstructorSpy).toHaveBeenCalled();
|
|
expect(fakeImage.crossOrigin).toEqual("");
|
|
},
|
|
);
|
|
});
|
|
|
|
it("does not set the crossOrigin property for non-cross-origin images", function () {
|
|
const fakeImage = {};
|
|
const deferred = defer();
|
|
const imageConstructorSpy = spyOn(window, "Image").and.callFake(
|
|
function () {
|
|
deferred.resolve();
|
|
return fakeImage;
|
|
},
|
|
);
|
|
|
|
// mock image loading so that the promise resolves
|
|
deferred.promise.then(function () {
|
|
fakeImage.onload();
|
|
});
|
|
|
|
return Resource.fetchImage("./someImage.png").then(function () {
|
|
expect(imageConstructorSpy).toHaveBeenCalled();
|
|
expect(fakeImage.crossOrigin).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("does not set the crossOrigin property for data URIs", function () {
|
|
const fakeImage = {};
|
|
const deferred = defer();
|
|
const imageConstructorSpy = spyOn(window, "Image").and.callFake(
|
|
function () {
|
|
deferred.resolve();
|
|
return fakeImage;
|
|
},
|
|
);
|
|
|
|
// mock image loading so that the promise resolves
|
|
deferred.promise.then(function () {
|
|
fakeImage.onload();
|
|
});
|
|
|
|
return Resource.fetchImage(dataUri).then(function () {
|
|
expect(imageConstructorSpy).toHaveBeenCalled();
|
|
expect(fakeImage.crossOrigin).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("resolves the promise when the image loads", function () {
|
|
const fakeImage = {};
|
|
const deferred = defer();
|
|
spyOn(window, "Image").and.callFake(function () {
|
|
deferred.resolve();
|
|
return fakeImage;
|
|
});
|
|
|
|
let success = false;
|
|
let failure = false;
|
|
let loadedImage;
|
|
|
|
deferred.promise.then(function () {
|
|
// neither callback has fired yet
|
|
expect(success).toEqual(false);
|
|
expect(failure).toEqual(false);
|
|
|
|
fakeImage.onload();
|
|
});
|
|
|
|
const promise = Promise.resolve(Resource.fetchImage(dataUri))
|
|
.then(function (image) {
|
|
success = true;
|
|
loadedImage = image;
|
|
})
|
|
.catch(function () {
|
|
failure = true;
|
|
});
|
|
|
|
return promise.finally(function () {
|
|
expect(success).toEqual(true);
|
|
expect(failure).toEqual(false);
|
|
expect(loadedImage).toBe(fakeImage);
|
|
});
|
|
});
|
|
|
|
it("rejects the promise when the image errors", function () {
|
|
const deferred = defer();
|
|
const fakeImage = {};
|
|
spyOn(window, "Image").and.callFake(function () {
|
|
deferred.resolve();
|
|
return fakeImage;
|
|
});
|
|
|
|
let success = false;
|
|
let failure = false;
|
|
let loadedImage;
|
|
|
|
deferred.promise.then(function () {
|
|
// neither callback has fired yet
|
|
expect(success).toEqual(false);
|
|
expect(failure).toEqual(false);
|
|
|
|
fakeImage.onerror(new Error());
|
|
});
|
|
|
|
return Resource.fetchImage(dataUri)
|
|
.then(function (image) {
|
|
success = true;
|
|
loadedImage = image;
|
|
})
|
|
.catch(function () {
|
|
failure = true;
|
|
})
|
|
.finally(function () {
|
|
expect(success).toEqual(false);
|
|
expect(failure).toEqual(true);
|
|
expect(loadedImage).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("Calls loadWithXhr with blob response type if headers is set", function () {
|
|
const expectedUrl = "http://example.invalid/testuri.png";
|
|
const 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");
|
|
|
|
const binary = dataUriToBuffer(dataUri);
|
|
|
|
deferred.resolve(new Blob([binary], { type: "image/png" }));
|
|
},
|
|
);
|
|
|
|
const testResource = new Resource({
|
|
url: expectedUrl,
|
|
headers: expectedHeaders,
|
|
});
|
|
const 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();
|
|
|
|
const testResource = new Resource({
|
|
url: dataUri,
|
|
headers: {
|
|
"X-my-header": "my-value",
|
|
},
|
|
});
|
|
const 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 () {
|
|
let deferred = defer();
|
|
let fakeImage = {};
|
|
spyOn(window, "Image").and.callFake(function () {
|
|
deferred.resolve();
|
|
fakeImage = {};
|
|
return fakeImage;
|
|
});
|
|
|
|
const cb = jasmine.createSpy("retry").and.returnValue(true);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid/image.png",
|
|
retryCallback: cb,
|
|
retryAttempts: 1,
|
|
});
|
|
|
|
deferred.promise
|
|
.then(function () {
|
|
fakeImage.onerror("some error"); // This should retry
|
|
|
|
deferred = defer();
|
|
return deferred.promise;
|
|
})
|
|
.then(function () {
|
|
fakeImage.onerror(); // This fails because we only retry once
|
|
});
|
|
|
|
let success = false;
|
|
let failure = false;
|
|
return resource
|
|
.fetchImage()
|
|
.then(function () {
|
|
success = true;
|
|
})
|
|
.catch(function () {
|
|
failure = true;
|
|
})
|
|
.finally(function () {
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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");
|
|
|
|
expect(success).toBe(false);
|
|
expect(failure).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("rejects after callback returns false", function () {
|
|
const deferred = defer();
|
|
let fakeImage = {};
|
|
spyOn(window, "Image").and.callFake(function () {
|
|
deferred.resolve();
|
|
fakeImage = {};
|
|
return fakeImage;
|
|
});
|
|
|
|
const cb = jasmine.createSpy("retry").and.returnValue(false);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid/image.png",
|
|
retryCallback: cb,
|
|
retryAttempts: 2,
|
|
});
|
|
|
|
deferred.promise.then(function () {
|
|
fakeImage.onerror("some error"); // This fails because the callback returns false
|
|
});
|
|
|
|
let success = false;
|
|
let failure = false;
|
|
return resource
|
|
.fetchImage()
|
|
.then(function (value) {
|
|
success = true;
|
|
})
|
|
.catch(function (error) {
|
|
failure = true;
|
|
})
|
|
.finally(function () {
|
|
expect(success).toBe(false);
|
|
expect(failure).toBe(true);
|
|
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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 () {
|
|
let deferred = defer();
|
|
let fakeImage = {};
|
|
spyOn(window, "Image").and.callFake(function () {
|
|
deferred.resolve();
|
|
fakeImage = {};
|
|
return fakeImage;
|
|
});
|
|
|
|
const cb = jasmine.createSpy("retry").and.returnValue(true);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid/image.png",
|
|
retryCallback: cb,
|
|
retryAttempts: 1,
|
|
});
|
|
|
|
deferred.promise
|
|
.then(function () {
|
|
fakeImage.onerror("some error"); // This should retry
|
|
|
|
deferred = defer();
|
|
return deferred.promise;
|
|
})
|
|
.then(function () {
|
|
fakeImage.onload(); // This succeeds
|
|
});
|
|
|
|
let success = false;
|
|
let failure = false;
|
|
return resource
|
|
.fetchImage()
|
|
.then(function (value) {
|
|
success = true;
|
|
})
|
|
.catch(function (error) {
|
|
failure = true;
|
|
})
|
|
.finally(function () {
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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 () {
|
|
const loadWithXhr = function (options) {
|
|
const resource = new Resource(options);
|
|
return resource._makeRequest({
|
|
responseType: options.responseType,
|
|
overrideMimeType: options.overrideMimeType,
|
|
method: 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("<h1>Hello, World!</h1>");
|
|
});
|
|
});
|
|
|
|
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("<h1>Hello, World!</h1>");
|
|
});
|
|
});
|
|
|
|
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();
|
|
});
|
|
});
|
|
|
|
const 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);
|
|
});
|
|
});
|
|
|
|
const 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");
|
|
|
|
const 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");
|
|
})
|
|
.catch(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");
|
|
})
|
|
.catch(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/textString.txt",
|
|
}).then(function (result) {
|
|
expect(result).toBe("Hello, World!");
|
|
});
|
|
});
|
|
|
|
it("a non-null response with a browser-supported responseType", function () {
|
|
return loadWithXhr({
|
|
url: "Data/textString.txt",
|
|
responseType: "text",
|
|
}).then(function (result) {
|
|
expect(result).toBe("Hello, World!");
|
|
});
|
|
});
|
|
|
|
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 () {
|
|
let fakeXHR;
|
|
let requestConstructorSpy;
|
|
|
|
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);
|
|
};
|
|
|
|
requestConstructorSpy = spyOn(window, "XMLHttpRequest").and.returnValue(
|
|
fakeXHR,
|
|
);
|
|
});
|
|
|
|
describe("returns a promise that rejects when the request", function () {
|
|
it("errors", function () {
|
|
const promise = loadWithXhr({
|
|
url: "http://example.invalid",
|
|
});
|
|
|
|
let resolvedValue;
|
|
let rejectedError;
|
|
expect(promise).toBeDefined();
|
|
|
|
const handledPromise = promise
|
|
.then(function (value) {
|
|
resolvedValue = value;
|
|
})
|
|
.catch(function (error) {
|
|
rejectedError = error;
|
|
});
|
|
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeUndefined();
|
|
|
|
fakeXHR.simulateError();
|
|
|
|
return handledPromise.finally(function () {
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeInstanceOf(RequestErrorEvent);
|
|
});
|
|
});
|
|
|
|
it("results in an HTTP status code less than 200", function () {
|
|
const promise = loadWithXhr({
|
|
url: "http://example.invalid",
|
|
});
|
|
|
|
expect(promise).toBeDefined();
|
|
|
|
let resolvedValue;
|
|
let rejectedError;
|
|
const handledPromise = promise
|
|
.then(function (value) {
|
|
resolvedValue = value;
|
|
})
|
|
.catch(function (error) {
|
|
rejectedError = error;
|
|
});
|
|
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeUndefined();
|
|
|
|
fakeXHR.simulateHttpResponse(199);
|
|
|
|
return handledPromise.finally(function () {
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeInstanceOf(RequestErrorEvent);
|
|
});
|
|
});
|
|
|
|
it("is an image with status code 204 with preferImageBitmap", function () {
|
|
if (!supportsImageBitmapOptions) {
|
|
return;
|
|
}
|
|
|
|
spyOn(Resource.prototype, "fetchBlob").and.callFake(function () {
|
|
return Promise.resolve({});
|
|
});
|
|
|
|
const promise = Resource.fetchImage({
|
|
url: "./Data/Images/Green.png",
|
|
preferImageBitmap: true,
|
|
// We only load with xhr if the resource has headers
|
|
headers: {
|
|
"a-header": true,
|
|
},
|
|
});
|
|
|
|
expect(promise).toBeDefined();
|
|
|
|
let resolved = false;
|
|
let resolvedValue;
|
|
let rejectedError;
|
|
const handledPromise = promise
|
|
.then(function (value) {
|
|
resolved = true;
|
|
resolvedValue = value;
|
|
})
|
|
.catch(function (error) {
|
|
rejectedError = error;
|
|
});
|
|
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeUndefined();
|
|
|
|
fakeXHR.simulateHttpResponse(204);
|
|
|
|
return handledPromise.finally(function () {
|
|
expect(resolved).toBe(false);
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeDefined();
|
|
});
|
|
});
|
|
|
|
it("resolves undefined for status code 204", function () {
|
|
const promise = loadWithXhr({
|
|
url: "http://example.invalid",
|
|
});
|
|
|
|
expect(promise).toBeDefined();
|
|
|
|
let resolved = false;
|
|
let resolvedValue;
|
|
let rejectedError;
|
|
const handledPromise = promise
|
|
.then(function (value) {
|
|
resolved = true;
|
|
resolvedValue = value;
|
|
})
|
|
.catch(function (error) {
|
|
rejectedError = error;
|
|
});
|
|
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeUndefined();
|
|
|
|
fakeXHR.simulateHttpResponse(204);
|
|
|
|
return handledPromise.finally(function () {
|
|
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 () {
|
|
const promise = loadWithXhr({
|
|
url: "http://example.invalid",
|
|
responseType: "",
|
|
});
|
|
|
|
expect(promise).toBeDefined();
|
|
|
|
let resolvedValue;
|
|
let rejectedError;
|
|
const handledPromise = promise
|
|
.then(function (value) {
|
|
resolvedValue = value;
|
|
})
|
|
.catch(function (error) {
|
|
rejectedError = error;
|
|
});
|
|
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeUndefined();
|
|
|
|
const responseXML = {
|
|
hasChildNodes: jasmine
|
|
.createSpy("hasChildNodes")
|
|
.and.returnValue(true),
|
|
};
|
|
|
|
fakeXHR.simulateResponseXMLLoad(responseXML);
|
|
|
|
return handledPromise.finally(function () {
|
|
expect(resolvedValue).toEqual(responseXML);
|
|
expect(rejectedError).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("a null response with a document responseType and non-null responseXML with child nodes", function () {
|
|
const promise = loadWithXhr({
|
|
url: "http://example.invalid",
|
|
responseType: "document",
|
|
});
|
|
|
|
expect(promise).toBeDefined();
|
|
|
|
let resolvedValue;
|
|
let rejectedError;
|
|
const handledPromise = promise
|
|
.then(function (value) {
|
|
resolvedValue = value;
|
|
})
|
|
.catch(function (error) {
|
|
rejectedError = error;
|
|
});
|
|
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeUndefined();
|
|
|
|
const responseXML = {
|
|
hasChildNodes: jasmine
|
|
.createSpy("hasChildNodes")
|
|
.and.returnValue(true),
|
|
};
|
|
fakeXHR.simulateResponseXMLLoad(responseXML);
|
|
|
|
return handledPromise.finally(function () {
|
|
expect(resolvedValue).toEqual(responseXML);
|
|
expect(rejectedError).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("a null response with a '' responseType and non-null responseText", function () {
|
|
const promise = loadWithXhr({
|
|
url: "http://example.invalid",
|
|
responseType: "",
|
|
});
|
|
|
|
expect(promise).toBeDefined();
|
|
|
|
let resolvedValue;
|
|
let rejectedError;
|
|
const handledPromise = promise
|
|
.then(function (value) {
|
|
resolvedValue = value;
|
|
})
|
|
.catch(function (error) {
|
|
rejectedError = error;
|
|
});
|
|
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeUndefined();
|
|
|
|
const responseText = "hello world";
|
|
fakeXHR.simulateResponseTextLoad(responseText);
|
|
|
|
return handledPromise.finally(function () {
|
|
expect(resolvedValue).toEqual(responseText);
|
|
expect(rejectedError).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("a null response with a text responseType and non-null responseText", function () {
|
|
const promise = loadWithXhr({
|
|
url: "http://example.invalid",
|
|
responseType: "text",
|
|
});
|
|
|
|
expect(promise).toBeDefined();
|
|
|
|
let resolvedValue;
|
|
let rejectedError;
|
|
const handledPromise = promise
|
|
.then(function (value) {
|
|
resolvedValue = value;
|
|
})
|
|
.catch(function (error) {
|
|
rejectedError = error;
|
|
});
|
|
|
|
expect(resolvedValue).toBeUndefined();
|
|
expect(rejectedError).toBeUndefined();
|
|
|
|
const responseText = "hello world";
|
|
fakeXHR.simulateResponseTextLoad(responseText);
|
|
|
|
return handledPromise.finally(function () {
|
|
expect(resolvedValue).toEqual(responseText);
|
|
expect(rejectedError).toBeUndefined();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("retries when Resource has the callback set", function () {
|
|
it("rejects after too many retries", function () {
|
|
const cb = jasmine.createSpy("retry").and.returnValue(true);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid",
|
|
retryCallback: cb,
|
|
retryAttempts: 1,
|
|
});
|
|
|
|
const promise = loadWithXhr(resource);
|
|
|
|
expect(promise).toBeDefined();
|
|
|
|
fakeXHR.simulateError(); // This should retry
|
|
// Wait for the next request to be created, then simulate error
|
|
pollToPromise(function () {
|
|
return requestConstructorSpy.calls.count() > 1;
|
|
}).then(function () {
|
|
fakeXHR.simulateError(); // This fails because we only retry once
|
|
});
|
|
|
|
return promise
|
|
.then(function () {
|
|
fail();
|
|
})
|
|
.catch(function (error) {
|
|
expect(error).toBeInstanceOf(RequestErrorEvent);
|
|
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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("rejects after callback returns false", function () {
|
|
const cb = jasmine.createSpy("retry").and.returnValue(false);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid",
|
|
retryCallback: cb,
|
|
retryAttempts: 2,
|
|
});
|
|
|
|
const promise = loadWithXhr(resource);
|
|
expect(promise).toBeDefined();
|
|
|
|
fakeXHR.simulateError(); // This fails because the callback returns false
|
|
return promise
|
|
.then(function () {
|
|
fail();
|
|
})
|
|
.catch(function (error) {
|
|
expect(error).toBeInstanceOf(RequestErrorEvent);
|
|
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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 () {
|
|
const cb = jasmine.createSpy("retry").and.returnValue(true);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid",
|
|
retryCallback: cb,
|
|
retryAttempts: 1,
|
|
});
|
|
|
|
const promise = loadWithXhr(resource);
|
|
expect(promise).toBeDefined();
|
|
|
|
fakeXHR.simulateError(); // This should retry
|
|
|
|
// Wait for the next request to be created, then simulate success
|
|
pollToPromise(function () {
|
|
return requestConstructorSpy.calls.count() > 1;
|
|
}).then(function () {
|
|
fakeXHR.simulateHttpResponse(200, "OK");
|
|
});
|
|
|
|
return promise.then(function (value) {
|
|
expect(value).toBeDefined();
|
|
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("fetchJsonp", function () {
|
|
it("returns a promise that resolves when the request loads", function () {
|
|
const testUrl = "http://example.invalid/testuri";
|
|
spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake(
|
|
function (url, name, deferred) {
|
|
expect(url).toContain(testUrl);
|
|
expect(name).toContain("loadJsonp");
|
|
expect(deferred).toBeDefined();
|
|
deferred.resolve();
|
|
},
|
|
);
|
|
return Resource.fetchJsonp(testUrl);
|
|
});
|
|
|
|
it("returns a promise that rejects when the request errors", function () {
|
|
const testUrl = "http://example.invalid/testuri";
|
|
return Resource.fetchJsonp(testUrl).catch(function (error) {
|
|
expect(error).toBeDefined();
|
|
});
|
|
});
|
|
|
|
it("Uses callback name specified in options", function () {
|
|
const testUrl = "test";
|
|
const options = {
|
|
callbackParameterName: "testCallback",
|
|
};
|
|
spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake(
|
|
function (url, functionName, deferred) {
|
|
expect(url).toContain("callback=loadJsonp");
|
|
deferred.resolve();
|
|
},
|
|
);
|
|
return Resource.fetchJsonp(testUrl, options);
|
|
});
|
|
|
|
describe("retries when Resource has the callback set", function () {
|
|
it("rejects after too many retries", function () {
|
|
const cb = jasmine.createSpy("retry").and.returnValue(true);
|
|
|
|
let lastDeferred;
|
|
spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake(
|
|
function (url, functionName, deferred) {
|
|
lastDeferred = deferred;
|
|
},
|
|
);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid",
|
|
retryCallback: cb,
|
|
retryAttempts: 1,
|
|
});
|
|
|
|
const promise = resource.fetchJsonp();
|
|
expect(promise).toBeDefined();
|
|
|
|
lastDeferred.reject("some error"); // This should retry
|
|
lastDeferred = undefined;
|
|
pollToPromise(function () {
|
|
return defined(lastDeferred);
|
|
}).then(function () {
|
|
lastDeferred.reject("another error"); // This fails because we only retry once
|
|
});
|
|
|
|
return promise
|
|
.then(function () {
|
|
fail();
|
|
})
|
|
.catch(function (error) {
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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");
|
|
|
|
expect(error).toEqual("another error");
|
|
});
|
|
});
|
|
|
|
it("rejects after callback returns false", function () {
|
|
const cb = jasmine.createSpy("retry").and.returnValue(false);
|
|
|
|
let lastDeferred;
|
|
spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake(
|
|
function (url, functionName, deferred) {
|
|
lastDeferred = deferred;
|
|
},
|
|
);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid",
|
|
retryCallback: cb,
|
|
retryAttempts: 2,
|
|
});
|
|
|
|
const promise = resource.fetchJsonp();
|
|
expect(promise).toBeDefined();
|
|
|
|
lastDeferred.reject("some error"); // This fails because the callback returns false
|
|
|
|
return promise
|
|
.then(function () {
|
|
fail();
|
|
})
|
|
.catch(function (error) {
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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");
|
|
expect(error).toEqual("some error");
|
|
});
|
|
});
|
|
|
|
it("resolves after retry", function () {
|
|
const cb = jasmine.createSpy("retry").and.returnValue(true);
|
|
|
|
let lastDeferred;
|
|
let lastUrl;
|
|
spyOn(Resource._Implementations, "loadAndExecuteScript").and.callFake(
|
|
function (url, functionName, deferred) {
|
|
lastUrl = url;
|
|
lastDeferred = deferred;
|
|
},
|
|
);
|
|
|
|
const resource = new Resource({
|
|
url: "http://example.invalid",
|
|
retryCallback: cb,
|
|
retryAttempts: 1,
|
|
});
|
|
|
|
const promise = resource.fetchJsonp();
|
|
expect(promise).toBeDefined();
|
|
|
|
lastDeferred.reject("some error"); // This should retry
|
|
lastDeferred = undefined;
|
|
pollToPromise(function () {
|
|
return defined(lastDeferred);
|
|
}).then(function () {
|
|
const uri = new Uri(lastUrl);
|
|
const query = queryToObject(uri.query());
|
|
window[query.callback]("something good");
|
|
lastDeferred.resolve(); // This should resolve
|
|
});
|
|
return promise.then(function (result) {
|
|
expect(result).toEqual("something good");
|
|
|
|
expect(cb.calls.count()).toEqual(1);
|
|
const 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");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|