/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var App = require('app'); require('utils/http_client'); describe('App.HttpClient', function () { describe('#defaultErrorHandler', function () { var cases = [ { isAsserted: false, title: 'no response text' }, { responseText: null, title: 'empty response text' }, { responseText: 404, title: 'invalid response text (number)' }, { responseText: 'error', title: 'invalid response text (string)' }, { responseText: '{error}', title: 'malformed response text (incorrect literal)' }, { responseText: '{error: 404}', title: 'malformed response text (no parentheses)' }, { responseText: '{\'error\': 404}', title: 'malformed response text (incorrect parentheses)' }, { responseText: '{"error": 404}', title: 'valid response text' } ]; cases.forEach(function (item) { describe(item.title, function () { var jqXHR = { responseText: item.responseText }; beforeEach(function () { sinon.stub(App.ajax, 'defaultErrorHandler', Em.K); sinon.spy(Em, 'assert'); try { App.HttpClient.defaultErrorHandler(jqXHR, '', '', 'http://localhost'); } catch (e) {} }); afterEach(function () { App.ajax.defaultErrorHandler.restore(); Em.assert.restore(); }); it('default error handler call', function () { expect(App.ajax.defaultErrorHandler.calledOnce).to.be.true; }); it('default error handler arguments', function () { expect(App.ajax.defaultErrorHandler.firstCall.args).to.eql([jqXHR, 'http://localhost']); }); it('no console error assertion', function () { expect(Em.assert.threw()).to.be.false; }); }); }); }); describe('#request', function () { var errorHandler = Em.K, ajaxOptions = { params: 'property=value' }, mapper = {}, cases = [ { url: 'url', generatedUrl: 'url?_=1000', errorHandler: null, passedErrorHandler: App.HttpClient.defaultErrorHandler, isGetAsPost: false, method: 'GET', setRequestHeaderCallCount: 0, params: null, title: 'no request parameters, default error handler' }, { url: 'url?property0=value0&property1=value1', generatedUrl: 'url?property0=value0&property1=value1&_=1000', errorHandler: errorHandler, passedErrorHandler: errorHandler, isGetAsPost: true, method: 'POST', setRequestHeaderCallCount: 2, params: '{"RequestInfo":{"query":"property=value"}}', title: 'request parameters passed, POST request' } ]; cases.forEach(function (item) { describe(item.title, function () { beforeEach(function () { sinon.stub(XMLHttpRequest.prototype, 'open', Em.K); sinon.stub(XMLHttpRequest.prototype, 'setRequestHeader', Em.K); sinon.stub(XMLHttpRequest.prototype, 'send', Em.K); sinon.stub(App, 'dateTime').returns(1000); sinon.stub(App.HttpClient, 'onReady', Em.K); App.HttpClient.request(item.url, ajaxOptions, mapper, item.errorHandler, item.isGetAsPost); }); afterEach(function () { XMLHttpRequest.prototype.open.restore(); XMLHttpRequest.prototype.setRequestHeader.restore(); XMLHttpRequest.prototype.send.restore(); App.dateTime.restore(); App.HttpClient.onReady.restore(); }); it('request method', function () { expect(XMLHttpRequest.prototype.open.firstCall.args[0]).to.equal(item.method); }); it('request URL', function () { expect(XMLHttpRequest.prototype.open.firstCall.args[1]).to.equal(item.generatedUrl); }); it('setting request headers', function () { expect(XMLHttpRequest.prototype.setRequestHeader.callCount).to.equal(item.setRequestHeaderCallCount); }); it('request params', function () { expect(XMLHttpRequest.prototype.send.firstCall.args[0]).to.equal(item.params); }); it('onReady callback: ajaxOptions', function () { expect(App.HttpClient.onReady.firstCall.args[2]).to.eql(ajaxOptions); }); it('onReady callback: mapper', function () { expect(App.HttpClient.onReady.firstCall.args[3]).to.eql(mapper); }); it('onReady callback: errorHandler', function () { expect(App.HttpClient.onReady.firstCall.args[4]).to.eql(item.passedErrorHandler); }); it('onReady callback: url', function () { expect(App.HttpClient.onReady.firstCall.args[5]).to.equal(item.url); }); }); }); }); describe('#onReady', function () { var clock, xhr = { responseText: '{"property": "value"}', statusText: 'status', abort: Em.K }, ajaxOptions = { complete: Em.K }, mapper = { map: Em.K }, mock = { errorHandler: Em.K }, cases = [ { readyState: 4, status: 200, isCommitError: false, commitCallCount: 1, mapCallCount: 1, completeCallCount: 1, abortCallCount: 1, errorHandlerCallCount: 0, onReadyCallCount: 1, title: 'successful request' }, { readyState: 4, status: 200, isCommitError: true, commitCallCount: 1, mapCallCount: 1, completeCallCount: 1, abortCallCount: 1, errorHandlerCallCount: 0, onReadyCallCount: 1, title: 'successful request, App.store.commit error' }, { readyState: 4, status: 404, isCommitError: false, commitCallCount: 0, mapCallCount: 0, completeCallCount: 0, abortCallCount: 0, errorHandlerCallCount: 1, onReadyCallCount: 1, title: 'failed request' }, { readyState: 3, status: 200, isCommitError: false, commitCallCount: 1, mapCallCount: 1, completeCallCount: 1, abortCallCount: 1, errorHandlerCallCount: 0, onReadyCallCount: 2, title: 'incomplete request, later successful' }, { readyState: 3, status: 404, isCommitError: false, commitCallCount: 0, mapCallCount: 0, completeCallCount: 0, abortCallCount: 0, errorHandlerCallCount: 1, onReadyCallCount: 2, title: 'incomplete request, later failed' } ]; cases.forEach(function (item) { describe(item.title, function () { beforeEach(function () { clock = sinon.useFakeTimers(); sinon.stub(App.store, 'commit'); sinon.spy(xhr, 'abort'); sinon.spy(mapper, 'map'); sinon.spy(mock, 'errorHandler'); sinon.spy(ajaxOptions, 'complete'); sinon.spy(App.HttpClient, 'onReady'); xhr.readyState = item.readyState; xhr.status = item.status; if (item.isCommitError) { App.store.commit.throws(); } App.HttpClient.onReady(xhr, null, ajaxOptions, mapper, mock.errorHandler, 'url'); clock.tick(10); xhr.readyState = 4; clock.tick(10); }); afterEach(function () { clock.restore(); App.store.commit.restore(); xhr.abort.restore(); mapper.map.restore(); mock.errorHandler.restore(); ajaxOptions.complete.restore(); App.HttpClient.onReady.restore(); }); it('App.store.commit call', function () { expect(App.store.commit.callCount).to.equal(item.commitCallCount); }); it('mapping data', function () { expect(mapper.map.callCount).to.equal(item.mapCallCount); }); if (item.mapCallCount) { it('mapped data', function () { expect(mapper.map.alwaysCalledWith({ property: 'value' })).to.be.true; }); } it('complete callback call', function () { expect(ajaxOptions.complete.callCount).to.equal(item.completeCallCount); }); if (item.completeCallCount) { it('complete callback context', function () { expect(ajaxOptions.complete.alwaysCalledOn(App.HttpClient)).to.be.true; }); } it('abort request', function () { expect(xhr.abort.callCount).to.equal(item.abortCallCount); }); it('error handler call', function () { expect(mock.errorHandler.callCount).to.equal(item.errorHandlerCallCount); }); if (item.errorHandlerCallCount) { it('error handler arguments', function () { expect(mock.errorHandler.alwaysCalledWith(xhr, 'error', 'status', 'url')).to.be.true; }); } it('onReady iterations number', function () { expect(App.HttpClient.onReady.callCount).to.equal(item.onReadyCallCount); }); }); }); }); describe('#get', function () { var mapper = {}, cases = [ { data: { error: Em.clb }, errorHandler: Em.K, passedErrorHandler: Em.K, isGetAsPost: false, title: 'custom error handler' }, { data: { error: Em.clb, doGetAsPost: true }, interval: 1, passedErrorHandler: Em.clb, isGetAsPost: true, title: 'error handler from data, interval provided, POST request' } ]; cases.forEach(function (item) { describe(item.title, function () { beforeEach(function () { sinon.stub(App.HttpClient, 'request', Em.K); sinon.stub($, 'periodic', function (options, callback) { callback(); }); App.HttpClient.get('url', mapper, item.data, item.errorHandler, item.interval); }); afterEach(function () { App.HttpClient.request.restore(); $.periodic.restore(); }); it('request call', function () { expect(App.HttpClient.request.calledOnce).to.be.true; }); it('request arguments', function () { expect(App.HttpClient.request.firstCall.args).to.eql(['url', item.data, mapper, item.passedErrorHandler, item.isGetAsPost]); }); }); }); }); describe('#post', function () { var args = ['url', {}, {}, Em.K, 1]; beforeEach(function () { sinon.stub(App.HttpClient, 'get', Em.K); App.HttpClient.post.apply(App.HttpClient, args); }); afterEach(function () { App.HttpClient.get.restore(); }); it('should call get method', function () { expect(App.HttpClient.get.calledOnce).to.be.true; }); it('get method arguments', function () { expect(App.HttpClient.get.firstCall.args).to.eql(args); }); }); });