// Copyright 2008 The Closure Library Authors. All Rights Reserved. // // Licensed 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. goog.provide('goog.testing.net.XhrIoTest'); goog.setTestOnly('goog.testing.net.XhrIoTest'); goog.require('goog.dom.xml'); goog.require('goog.events'); goog.require('goog.events.Event'); goog.require('goog.net.ErrorCode'); goog.require('goog.net.EventType'); goog.require('goog.net.XmlHttp'); goog.require('goog.object'); goog.require('goog.testing.MockControl'); goog.require('goog.testing.asserts'); goog.require('goog.testing.jsunit'); goog.require('goog.testing.mockmatchers.InstanceOf'); goog.require('goog.testing.net.XhrIo'); // In order to emulate the actual behavior of XhrIo, set this value for all // tests until the default value is false. goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks = false; var mockControl; function setUp() { mockControl = new goog.testing.MockControl(); } function testStaticSend() { sendInstances = goog.testing.net.XhrIo.getSendInstances(); var returnedXhr = goog.testing.net.XhrIo.send('url'); assertEquals('sendInstances_ after send', 1, sendInstances.length); xhr = sendInstances[sendInstances.length - 1]; assertTrue('isActive after request', xhr.isActive()); assertEquals(returnedXhr, xhr); assertEquals( 'readyState after request', goog.net.XmlHttp.ReadyState.LOADING, xhr.getReadyState()); xhr.simulateResponse(200, ''); assertFalse('isActive after response', xhr.isActive()); assertEquals( 'readyState after response', goog.net.XmlHttp.ReadyState.COMPLETE, xhr.getReadyState()); xhr.simulateReady(); assertEquals('sendInstances_ after READY', 0, sendInstances.length); } function testStaticSendWithException() { goog.testing.net.XhrIo.send('url', function() { if (!this.isSuccess()) { throw Error('The xhr did not complete successfully!'); } }); var sendInstances = goog.testing.net.XhrIo.getSendInstances(); var xhr = sendInstances[sendInstances.length - 1]; try { xhr.simulateResponse(400, ''); } catch (e) { // Do nothing with the exception; we just want to make sure // the class cleans itself up properly when an exception is // thrown. } assertEquals( 'Send instance array not cleaned up properly!', 0, sendInstances.length); } function testMultipleSend() { var xhr = new goog.testing.net.XhrIo(); assertFalse('isActive before first request', xhr.isActive()); assertEquals( 'readyState before first request', goog.net.XmlHttp.ReadyState.UNINITIALIZED, xhr.getReadyState()); xhr.send('url'); assertTrue('isActive after first request', xhr.isActive()); assertEquals( 'readyState after first request', goog.net.XmlHttp.ReadyState.LOADING, xhr.getReadyState()); xhr.simulateResponse(200, ''); assertFalse('isActive after first response', xhr.isActive()); assertEquals( 'readyState after first response', goog.net.XmlHttp.ReadyState.COMPLETE, xhr.getReadyState()); xhr.send('url'); assertTrue('isActive after second request', xhr.isActive()); assertEquals( 'readyState after second request', goog.net.XmlHttp.ReadyState.LOADING, xhr.getReadyState()); } function testGetLastUri() { var xhr = new goog.testing.net.XhrIo(); assertEquals('nothing sent yet, empty URI', '', xhr.getLastUri()); var requestUrl = 'http://www.example.com/'; xhr.send(requestUrl); assertEquals('message sent, URI saved', requestUrl, xhr.getLastUri()); } function testGetLastMethod() { var xhr = new goog.testing.net.XhrIo(); assertUndefined('nothing sent yet, empty method', xhr.getLastMethod()); var method = 'POKE'; xhr.send('http://www.example.com/', method); assertEquals('message sent, method saved', method, xhr.getLastMethod()); xhr.simulateResponse(200, ''); xhr.send('http://www.example.com/'); assertEquals('message sent, method saved', 'GET', xhr.getLastMethod()); } function testGetLastContent() { var xhr = new goog.testing.net.XhrIo(); assertUndefined('nothing sent yet, empty content', xhr.getLastContent()); var postContent = 'var=value&var2=value2'; xhr.send('http://www.example.com/', undefined, postContent); assertEquals( 'POST message sent, content saved', postContent, xhr.getLastContent()); xhr.simulateResponse(200, ''); xhr.send('http://www.example.com/'); assertUndefined('GET message sent, content cleaned', xhr.getLastContent()); } function testGetLastRequestHeaders() { var xhr = new goog.testing.net.XhrIo(); assertUndefined( 'nothing sent yet, empty headers', xhr.getLastRequestHeaders()); xhr.send( 'http://www.example.com/', undefined, undefined, {'From': 'page@google.com'}); assertObjectEquals( 'Request sent with extra headers, headers saved', {'From': 'page@google.com'}, xhr.getLastRequestHeaders()); xhr.simulateResponse(200, ''); xhr.send('http://www.example.com'); assertUndefined( 'New request sent without extra headers', xhr.getLastRequestHeaders()); xhr.simulateResponse(200, ''); xhr.headers.set('X', 'A'); xhr.headers.set('Y', 'B'); xhr.send( 'http://www.example.com/', undefined, undefined, {'Y': 'P', 'Z': 'Q'}); assertObjectEquals( 'Default headers combined with call headers', {'X': 'A', 'Y': 'P', 'Z': 'Q'}, xhr.getLastRequestHeaders()); xhr.simulateResponse(200, ''); } function testGetResponseText() { // Text response came. var called = false; var xhr = new goog.testing.net.XhrIo(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) { called = true; assertEquals('text', e.target.getResponseText()); }); xhr.simulateResponse(200, 'text'); assertTrue(called); // XML response came. called = false; xhr = new goog.testing.net.XhrIo(); var xml = goog.dom.xml.createDocument(); xml.appendChild(xml.createElement('root')); goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) { called = true; var text = e.target.getResponseText(); assertTrue(//.test(text)); }); xhr.simulateResponse(200, xml); assertTrue(called); // Outside the callback, getResponseText returns an empty string. assertEquals('', xhr.getResponseText()); } function testGetResponseJson() { // Valid JSON response came. var called = false; var xhr = new goog.testing.net.XhrIo(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) { called = true; assertArrayEquals([0, 1], e.target.getResponseJson()); }); xhr.simulateResponse(200, '[0, 1]'); assertTrue(called); // Valid JSON response with XSSI prefix encoded came. var called = false; var xhr = new goog.testing.net.XhrIo(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) { called = true; assertArrayEquals([0, 1], e.target.getResponseJson(')]}\', \n')); }); xhr.simulateResponse(200, ')]}\', \n[0, 1]'); assertTrue(called); // Outside the callback, getResponseJson returns undefined. assertUndefined(xhr.getResponseJson()); // Invalid JSON response came. var called = false; var xhr = new goog.testing.net.XhrIo(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) { called = true; assertThrows(goog.bind(e.target.getResponseJson, e.target)); }); xhr.simulateResponse(200, '[0, 1'); assertTrue(called); // XML response came. var called = false; var xhr = new goog.testing.net.XhrIo(); var xml = goog.dom.xml.createDocument(); xml.appendChild(xml.createElement('root')); goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) { called = true; assertThrows(goog.bind(e.target.getResponseJson, e.target)); }); xhr.simulateResponse(200, xml); assertTrue(called); } function testGetResponseXml() { // Text response came. var called = false; var xhr = new goog.testing.net.XhrIo(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) { called = true; assertNull(e.target.getResponseXml()); }); xhr.simulateResponse(200, 'text'); assertTrue(called); // XML response came. var called = false; var xhr = new goog.testing.net.XhrIo(); var xml = goog.dom.xml.createDocument(); xml.appendChild(xml.createElement('root')); goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) { called = true; assertEquals(xml, e.target.getResponseXml()); }); xhr.simulateResponse(200, xml); assertTrue(called); // Outside the callback, getResponseXml returns null. assertNull(xhr.getResponseXml()); } function testGetResponsesAllowUnsafeAccessToXhrIoOutsideCallbacks() { // Test that if true is passed for opt_allowAccessToXhrIoOutsideOfCallback, // then we can call getResponse*() outside of the SUCCESS event callback. goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks = true; var xhr = new goog.testing.net.XhrIo(); xhr.simulateResponse(200, 'text'); assertEquals('text', xhr.getResponseText()); xhr.simulateResponse(200, '[0, 1]'); assertArrayEquals([0, 1], xhr.getResponseJson()); var xml = goog.dom.xml.createDocument(); xml.appendChild(xml.createElement('root')); xhr.simulateResponse(200, xml); assertEquals(xml, xhr.getResponseXml()); var headers = {'test1': 'foo', 'test2': 'bar'}; xhr.simulateResponse(200, '', headers); assertObjectEquals(headers, xhr.getResponseHeaders()); assertEquals('test1: foo\r\ntest2: bar', xhr.getAllResponseHeaders()); // Reset the value for future tests. goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks = false; } function testGetResponseHeaders_noHeadersPresent() { var xhr = new goog.testing.net.XhrIo(); var mockListener = mockControl.createFunctionMock(); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { assertTrue(e.type == goog.net.EventType.SUCCESS); assertUndefined(e.target.getResponseHeader('XHR')); }); mockControl.$replayAll(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener); xhr.simulateResponse(200, ''); mockControl.$verifyAll(); mockControl.$resetAll(); } function testGetResponseHeaders_headersPresent() { var xhr = new goog.testing.net.XhrIo(); var mockListener = mockControl.createFunctionMock(); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { assertTrue(e.type == goog.net.EventType.SUCCESS); assertUndefined(e.target.getResponseHeader('XHR')); assertEquals(e.target.getResponseHeader('Pragma'), 'no-cache'); }); mockControl.$replayAll(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener); xhr.simulateResponse(200, '', {'Pragma': 'no-cache'}); mockControl.$verifyAll(); mockControl.$resetAll(); } function testAbort_WhenNoPendingSentRequests() { var xhr = new goog.testing.net.XhrIo(); var eventListener = mockControl.createFunctionMock(); mockControl.$replayAll(); goog.events.listen(xhr, goog.net.EventType.COMPLETE, eventListener); goog.events.listen(xhr, goog.net.EventType.SUCCESS, eventListener); goog.events.listen(xhr, goog.net.EventType.ABORT, eventListener); goog.events.listen(xhr, goog.net.EventType.ERROR, eventListener); goog.events.listen(xhr, goog.net.EventType.READY, eventListener); xhr.abort(); mockControl.$verifyAll(); mockControl.$resetAll(); } function testAbort_PendingSentRequest() { var xhr = new goog.testing.net.XhrIo(); var mockListener = mockControl.createFunctionMock(); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { assertTrue(e.type == goog.net.EventType.COMPLETE); assertObjectEquals(e.target, xhr); assertEquals(e.target.getStatus(), -1); assertEquals(e.target.getLastErrorCode(), goog.net.ErrorCode.ABORT); assertTrue(e.target.isActive()); }); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { assertTrue(e.type == goog.net.EventType.ABORT); assertObjectEquals(e.target, xhr); assertTrue(e.target.isActive()); }); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { assertTrue(e.type == goog.net.EventType.READY); assertObjectEquals(e.target, xhr); assertFalse(e.target.isActive()); }); mockControl.$replayAll(); goog.events.listen(xhr, goog.net.EventType.COMPLETE, mockListener); goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener); goog.events.listen(xhr, goog.net.EventType.ABORT, mockListener); goog.events.listen(xhr, goog.net.EventType.ERROR, mockListener); goog.events.listen(xhr, goog.net.EventType.READY, mockListener); xhr.send('dummyurl'); xhr.abort(); mockControl.$verifyAll(); mockControl.$resetAll(); } function testEvents_Success() { var xhr = new goog.testing.net.XhrIo(); var mockListener = mockControl.createFunctionMock(); var readyState = goog.net.XmlHttp.ReadyState.UNINITIALIZED; function readyStateListener(e) { assertEquals(e.type, goog.net.EventType.READY_STATE_CHANGE); assertObjectEquals(e.target, xhr); readyState++; assertEquals(e.target.getReadyState(), readyState); assertTrue(e.target.isActive()); } mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { assertEquals(e.type, goog.net.EventType.COMPLETE); assertObjectEquals(e.target, xhr); assertEquals(e.target.getLastErrorCode(), goog.net.ErrorCode.NO_ERROR); assertTrue(e.target.isActive()); }); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { assertEquals(e.type, goog.net.EventType.SUCCESS); assertObjectEquals(e.target, xhr); assertTrue(e.target.isActive()); }); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { assertEquals(e.type, goog.net.EventType.READY); assertObjectEquals(e.target, xhr); assertFalse(e.target.isActive()); }); mockControl.$replayAll(); goog.events.listen( xhr, goog.net.EventType.READY_STATE_CHANGE, readyStateListener); goog.events.listen(xhr, goog.net.EventType.COMPLETE, mockListener); goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener); goog.events.listen(xhr, goog.net.EventType.ABORT, mockListener); goog.events.listen(xhr, goog.net.EventType.ERROR, mockListener); goog.events.listen(xhr, goog.net.EventType.READY, mockListener); xhr.send('dummyurl'); xhr.simulateResponse(200, null); mockControl.$verifyAll(); mockControl.$resetAll(); } function testGetResponseHeaders() { var xhr = new goog.testing.net.XhrIo(); var mockListener = mockControl.createFunctionMock(); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { var headers = e.target.getResponseHeaders(); assertEquals(2, goog.object.getCount(headers)); assertEquals('foo', headers['test1']); assertEquals('bar', headers['test2']); }); mockControl.$replayAll(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener); // Simulate an XHR with 2 headers. xhr.simulateResponse(200, '', {'test1': 'foo', 'test2': 'bar'}); // Outside the callback, getResponseHeaders returns an empty object. assertObjectEquals({}, xhr.getResponseHeaders()); assertEquals('', xhr.getAllResponseHeaders()); mockControl.$verifyAll(); mockControl.$resetAll(); } function testGetResponseHeadersWithColonInValue() { var xhr = new goog.testing.net.XhrIo(); var mockListener = mockControl.createFunctionMock(); mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event)) .$does(function(e) { var headers = e.target.getResponseHeaders(); assertEquals(1, goog.object.getCount(headers)); assertEquals('f:o:o', headers['test1']); }); mockControl.$replayAll(); goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener); // Simulate an XHR with a colon in the http header value. xhr.simulateResponse(200, '', {'test1': 'f:o:o'}); mockControl.$verifyAll(); mockControl.$resetAll(); }