defineSuite([ 'Core/IonResource', 'Core/Ion', 'Core/RequestErrorEvent', 'Core/Resource', 'Core/RuntimeError', 'ThirdParty/when' ], function( IonResource, Ion, RequestErrorEvent, Resource, RuntimeError, when) { 'use strict'; var assetId = 123890213; var endpoint = { type: '3DTILES', url: 'https://assets.cesium.com/' + assetId + '/tileset.json', accessToken: 'not_really_a_refresh_token', attributions: [] }; it('constructs with expected values', function() { spyOn(Resource, 'call').and.callThrough(); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(endpoint, endpointResource); expect(resource).toBeInstanceOf(Resource); expect(resource._ionEndpoint).toEqual(endpoint); expect(Resource.call).toHaveBeenCalledWith(resource, { url: endpoint.url, retryCallback: resource.retryCallback, retryAttempts: 1 }); }); it('clone works', function() { var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(endpoint, endpointResource); var cloned = resource.clone(); expect(cloned).not.toBe(resource); expect(cloned._ionRoot).toBe(resource); cloned._ionRoot = undefined; expect(cloned.retryCallback).toBe(resource.retryCallback); expect(cloned.headers.Authorization).toBe(resource.headers.Authorization); expect(cloned).toEqual(resource); }); it('create creates the expected resource', function() { var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(endpoint, endpointResource); expect(resource.getUrlComponent()).toEqual(endpoint.url); expect(resource._ionEndpoint).toBe(endpoint); expect(resource._ionEndpointResource).toEqual(endpointResource); expect(resource.retryCallback).toBeDefined(); expect(resource.retryAttempts).toBe(1); }); it('fromAssetId calls constructor for non-external endpoint with expected parameters', function() { var tilesAssetId = 123890213; var tilesEndpoint = { type: '3DTILES', url: 'https://assets.cesium.com/' + tilesAssetId + '/tileset.json', accessToken: 'not_really_a_refresh_token', attributions: [] }; var options = {}; var resourceEndpoint = IonResource._createEndpointResource(tilesAssetId, options); spyOn(IonResource, '_createEndpointResource').and.returnValue(resourceEndpoint); spyOn(resourceEndpoint, 'fetchJson').and.returnValue(when.resolve(tilesEndpoint)); return IonResource.fromAssetId(tilesAssetId, options) .then(function(resource) { expect(IonResource._createEndpointResource).toHaveBeenCalledWith(tilesAssetId, options); expect(resourceEndpoint.fetchJson).toHaveBeenCalled(); expect(resource._ionEndpointResource).toEqual(resourceEndpoint); expect(resource._ionEndpoint).toEqual(tilesEndpoint); }); }); function testNonImageryExternalResource(externalEndpoint) { var resourceEndpoint = IonResource._createEndpointResource(123890213); spyOn(IonResource, '_createEndpointResource').and.returnValue(resourceEndpoint); spyOn(resourceEndpoint, 'fetchJson').and.returnValue(when.resolve(externalEndpoint)); return IonResource.fromAssetId(123890213) .then(function(resource) { expect(resource.url).toEqual(externalEndpoint.options.url); expect(resource.headers.Authorization).toBeUndefined(); expect(resource.retryCallback).toBeUndefined(); }); } it('fromAssetId returns basic Resource for external 3D tilesets', function() { return testNonImageryExternalResource({ type: '3DTILES', externalType: '3DTILES', options: { url: 'http://test.invalid/tileset.json' }, attributions: [] }); }); it('fromAssetId returns basic Resource for external 3D tilesets', function() { return testNonImageryExternalResource({ type: 'TERRAIN', externalType: 'STK_TERRAIN_SERVER', options: { url: 'http://test.invalid/world' }, attributions: [] }); }); it('fromAssetId rejects for external imagery', function() { return testNonImageryExternalResource({ type: 'IMAGERY', externalType: 'URL_TEMPLATE', url: 'http://test.invalid/world', attributions: [] }) .then(fail) .otherwise(function(e) { expect(e).toBeInstanceOf(RuntimeError); }); }); it('createEndpointResource creates expected values with default parameters', function() { var assetId = 2348234; var resource = IonResource._createEndpointResource(assetId); expect(resource.url).toBe(Ion.defaultServer.url + 'v1/assets/' + assetId + '/endpoint?access_token=' + Ion.defaultAccessToken); }); it('createEndpointResource creates expected values with overridden options', function() { var serverUrl = 'https://api.cesium.test/'; var accessToken = 'not_a_token'; var assetId = 2348234; var resource = IonResource._createEndpointResource(assetId, { server: serverUrl, accessToken: accessToken }); expect(resource.url).toBe(serverUrl + 'v1/assets/' + assetId + '/endpoint?access_token=' + accessToken); }); it('createEndpointResource creates expected values with overridden defaults', function() { var defaultServer = Ion.defaultServer; var defaultAccessToken = Ion.defaultAccessToken; Ion.defaultServer = new Resource({ url: 'https://api.cesium.test/' }); Ion.defaultAccessToken = 'not_a_token'; var assetId = 2348234; var resource = IonResource._createEndpointResource(assetId); expect(resource.url).toBe(Ion.defaultServer.url + 'v1/assets/' + assetId + '/endpoint?access_token=' + Ion.defaultAccessToken); Ion.defaultServer = defaultServer; Ion.defaultAccessToken = defaultAccessToken; }); it('Calls base _makeRequest with expected options when resource no Accept header is already defined', function() { var originalOptions = {}; var expectedOptions = { headers: { Accept: '*/*;access_token=' + endpoint.accessToken } }; var _makeRequest = spyOn(Resource.prototype, '_makeRequest'); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(endpoint, endpointResource); resource._makeRequest(originalOptions); expect(_makeRequest).toHaveBeenCalledWith(expectedOptions); }); it('Calls base _makeRequest with expected options when resource Accept header is already defined', function() { var originalOptions = {}; var expectedOptions = { headers: { Accept: 'application/json,*/*;access_token=' + endpoint.accessToken } }; var _makeRequest = spyOn(Resource.prototype, '_makeRequest'); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(endpoint, endpointResource); resource.headers.Accept = 'application/json'; resource._makeRequest(originalOptions); expect(_makeRequest).toHaveBeenCalledWith(expectedOptions); }); it('Calls base _makeRequest with expected options when options header.Accept is already defined', function() { var originalOptions = { headers: { Accept: 'application/json' } }; var expectedOptions = { headers: { Accept: 'application/json,*/*;access_token=' + endpoint.accessToken } }; var _makeRequest = spyOn(Resource.prototype, '_makeRequest'); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(endpoint, endpointResource); resource._makeRequest(originalOptions); expect(_makeRequest).toHaveBeenCalledWith(expectedOptions); }); it('Calls base _makeRequest with no changes for external assets', function() { var externalEndpoint = { type: '3DTILES', externalType: '3DTILES', options: { url: 'http://test.invalid/tileset.json' }, attributions: [] }; var options = {}; var _makeRequest = spyOn(Resource.prototype, '_makeRequest'); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(externalEndpoint, endpointResource); resource._makeRequest(options); expect(_makeRequest.calls.argsFor(0)[0]).toBe(options); }); it('Calls base fetchImage with preferBlob for ion assets', function() { var fetchImage = spyOn(Resource.prototype, 'fetchImage'); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(endpoint, endpointResource); resource.fetchImage(false, true); expect(fetchImage).toHaveBeenCalledWith(true, true); }); it('Calls base fetchImage with no changes for external assets', function() { var externalEndpoint = { type: '3DTILES', externalType: '3DTILES', options: { url: 'http://test.invalid/tileset.json' }, attributions: [] }; var fetchImage = spyOn(Resource.prototype, 'fetchImage'); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(externalEndpoint, endpointResource); resource.fetchImage(false, true); expect(fetchImage).toHaveBeenCalledWith(false, true); }); describe('retryCallback', function() { var endpointResource; var resource; var retryCallback; beforeEach(function() { endpointResource = new Resource({ url: 'https://api.test.invalid', access_token: 'not_the_token' }); resource = new IonResource(endpoint, endpointResource); retryCallback = resource.retryCallback; }); it('returns false when error is undefined', function() { return retryCallback(resource, undefined).then(function(result) { expect(result).toBe(false); }); }); it('returns false when error is non-401', function() { var error = new RequestErrorEvent(404); return retryCallback(resource, error).then(function(result) { expect(result).toBe(false); }); }); it('returns false when error is event with non-Image target', function() { var event = { target: {} }; return retryCallback(resource, event).then(function(result) { expect(result).toBe(false); }); }); function testCallback(resource, event) { var deferred = when.defer(); spyOn(endpointResource, 'fetchJson').and.returnValue(deferred.promise); var newEndpoint = { type: '3DTILES', url: 'https://assets.cesium.com/' + assetId, accessToken: 'not_not_really_a_refresh_token' }; var promise = retryCallback(resource, event); var resultPromise = promise.then(function(result) { expect(result).toBe(true); expect(resource._ionEndpoint).toBe(newEndpoint); }); expect(endpointResource.fetchJson).toHaveBeenCalledWith(); //A second retry should re-use the same pending promise var promise2 = retryCallback(resource, event); expect(promise._pendingPromise).toBe(promise2._pendingPromise); deferred.resolve(newEndpoint); return resultPromise; } it('works when error is a 401', function() { var error = new RequestErrorEvent(401); return testCallback(resource, error); }); it('works when error is event with Image target', function() { var event = { target: new Image() }; return testCallback(resource, event); }); it('works with derived resource and sets root access_token', function() { var derived = resource.getDerivedResource('1'); var error = new RequestErrorEvent(401); return testCallback(derived, error) .then(function(){ expect(derived._ionEndpoint).toBe(resource._ionEndpoint); expect(derived.headers.Authorization).toEqual(resource.headers.Authorization); }); }); }); });