123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- // Copyright 2011 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.labs.net.xhrTest');
- goog.setTestOnly('goog.labs.net.xhrTest');
- goog.require('goog.Promise');
- goog.require('goog.events');
- goog.require('goog.events.EventType');
- goog.require('goog.labs.net.xhr');
- goog.require('goog.net.WrapperXmlHttpFactory');
- goog.require('goog.net.XmlHttp');
- goog.require('goog.testing.MockClock');
- goog.require('goog.testing.TestCase');
- goog.require('goog.testing.jsunit');
- goog.require('goog.userAgent');
- /** Path to a small download target used for testing binary requests. */
- var TEST_IMAGE = 'testdata/cleardot.gif';
- /** The expected bytes of the test image. */
- var TEST_IMAGE_BYTES = [
- 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80,
- 0xFF, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3B
- ];
- function setUpPage() {
- goog.testing.TestCase.getActiveTestCase().promiseTimeout = 10000; // 10s
- }
- function stubXhrToReturn(status, opt_responseText, opt_latency) {
- if (goog.isDefAndNotNull(opt_latency)) {
- mockClock = new goog.testing.MockClock(true);
- }
- var stubXhr = {
- sent: false,
- aborted: false,
- status: 0,
- headers: {},
- open: function(method, url, async) {
- this.method = method;
- this.url = url;
- this.async = async;
- },
- setRequestHeader: function(key, value) { this.headers[key] = value; },
- overrideMimeType: function(mimeType) { this.mimeType = mimeType; },
- abort: function() {
- this.aborted = true;
- this.load(0);
- },
- send: function(data) {
- this.data = data;
- this.sent = true;
- // Fulfill the send asynchronously, or possibly with the MockClock.
- window.setTimeout(goog.bind(this.load, this, status), opt_latency || 0);
- if (mockClock) {
- mockClock.tick(opt_latency);
- }
- },
- load: function(status) {
- this.status = status;
- if (goog.isDefAndNotNull(opt_responseText)) {
- this.responseText = opt_responseText;
- }
- this.readyState = 4;
- if (this.onreadystatechange) this.onreadystatechange();
- }
- };
- stubXmlHttpWith(stubXhr);
- }
- function stubXhrToThrow(err) {
- stubXmlHttpWith(buildThrowingStubXhr(err));
- }
- function buildThrowingStubXhr(err) {
- return {
- sent: false,
- aborted: false,
- status: 0,
- headers: {},
- open: function(method, url, async) {
- this.method = method;
- this.url = url;
- this.async = async;
- },
- setRequestHeader: function(key, value) { this.headers[key] = value; },
- overrideMimeType: function(mimeType) { this.mimeType = mimeType; },
- send: function(data) { throw err; }
- };
- }
- function stubXmlHttpWith(stubXhr) {
- goog.net.XmlHttp = function() { return stubXhr; };
- for (var x in originalXmlHttp) {
- goog.net.XmlHttp[x] = originalXmlHttp[x];
- }
- }
- var xhr = goog.labs.net.xhr;
- var originalXmlHttp = goog.net.XmlHttp;
- var mockClock;
- function tearDown() {
- if (mockClock) {
- mockClock.dispose();
- mockClock = null;
- }
- goog.net.XmlHttp = originalXmlHttp;
- }
- /**
- * Tests whether the test was loaded from a file: protocol. Tests that use a
- * real network request cannot be run from the local file system due to
- * cross-origin restrictions, but will run if the tests are hosted on a server.
- * A log message is added to the test case to warn users that the a test was
- * skipped.
- *
- * @return {boolean} Whether the test is running on a local file system.
- */
- function isRunningLocally() {
- if (window.location.protocol == 'file:') {
- var testCase = goog.global['G_testRunner'].testCase;
- testCase.saveMessage('Test skipped while running on local file system.');
- return true;
- }
- return false;
- }
- function testSimpleRequest() {
- if (isRunningLocally()) return;
- return xhr.send('GET', 'testdata/xhr_test_text.data').then(function(xhr) {
- assertEquals('Just some data.', xhr.responseText);
- assertEquals(200, xhr.status);
- });
- }
- function testGetText() {
- if (isRunningLocally()) return;
- return xhr.get('testdata/xhr_test_text.data').then(function(responseText) {
- assertEquals('Just some data.', responseText);
- });
- }
- function testGetTextWithJson() {
- if (isRunningLocally()) return;
- return xhr.get('testdata/xhr_test_json.data').then(function(responseText) {
- assertEquals('while(1);\n{"stat":"ok","count":12345}\n', responseText);
- });
- }
- function testPostText() {
- if (isRunningLocally()) return;
- return xhr.post('testdata/xhr_test_text.data', 'post-data')
- .then(function(responseText) {
- // No good way to test post-data gets transported.
- assertEquals('Just some data.', responseText);
- });
- }
- function testGetJson() {
- if (isRunningLocally()) return;
- return xhr.getJson('testdata/xhr_test_json.data', {xssiPrefix: 'while(1);\n'})
- .then(function(responseObj) {
- assertEquals('ok', responseObj['stat']);
- assertEquals(12345, responseObj['count']);
- });
- }
- function testGetBlob() {
- if (isRunningLocally()) return;
- // IE9 and earlier do not support blobs.
- if (!('Blob' in goog.global)) {
- var err = assertThrows(function() { xhr.getBlob(TEST_IMAGE); });
- assertEquals(
- 'Assertion failed: getBlob is not supported in this browser.',
- err.message);
- return;
- }
- var options = {withCredentials: true};
- return xhr.getBlob(TEST_IMAGE, options)
- .then(function(blob) {
- var reader = new FileReader();
- return new goog.Promise(function(resolve, reject) {
- goog.events.listenOnce(reader, goog.events.EventType.LOAD, resolve);
- reader.readAsArrayBuffer(blob);
- });
- })
- .then(function(e) {
- assertElementsEquals(TEST_IMAGE_BYTES, new Uint8Array(e.target.result));
- assertObjectEquals(
- 'input options should not have mutated.', {withCredentials: true},
- options);
- });
- }
- function testGetBytes() {
- if (isRunningLocally()) return;
- // IE8 requires a VBScript fallback to read the bytes from the response.
- if (goog.userAgent.IE && !goog.userAgent.isDocumentMode(9)) {
- var err = assertThrows(function() { xhr.getBytes(TEST_IMAGE); });
- assertEquals(
- 'Assertion failed: getBytes is not supported in this browser.',
- err.message);
- return;
- }
- var options = {withCredentials: true};
- return xhr.getBytes(TEST_IMAGE).then(function(bytes) {
- assertElementsEquals(TEST_IMAGE_BYTES, bytes);
- assertObjectEquals(
- 'input options should not have mutated.', {withCredentials: true},
- options);
- });
- }
- function testSerialRequests() {
- if (isRunningLocally()) return;
- return xhr.get('testdata/xhr_test_text.data')
- .then(function(response) {
- return xhr.getJson(
- 'testdata/xhr_test_json.data', {xssiPrefix: 'while(1);\n'});
- })
- .then(function(responseObj) {
- // Data that comes through to callbacks should be from the 2nd request.
- assertEquals('ok', responseObj['stat']);
- assertEquals(12345, responseObj['count']);
- });
- }
- function testBadUrlDetectedAsError() {
- if (isRunningLocally()) return;
- return xhr.getJson('unknown-file.dat')
- .then(fail /* opt_onFulfilled */, function(err) {
- assertTrue(
- 'Error should be an HTTP error', err instanceof xhr.HttpError);
- assertEquals(404, err.status);
- assertNotNull(err.xhr);
- });
- }
- function testBadOriginTriggersOnErrorHandler() {
- if (goog.userAgent.EDGE) return; // failing b/62677027
- return xhr.get('http://www.google.com')
- .then(
- function() {
- fail(
- 'XHR to http://www.google.com should\'ve failed due to ' +
- 'same-origin policy.');
- } /* opt_onFulfilled */,
- function(err) {
- // In IE this will be a goog.labs.net.xhr.Error since it is thrown
- // when calling xhr.open(), other browsers will raise an HttpError.
- assertTrue(
- 'Error should be an xhr error', err instanceof xhr.Error);
- assertNotNull(err.xhr);
- });
- }
- //============================================================================
- // The following tests use a stubbed out XMLHttpRequest.
- //============================================================================
- function testSendNoOptions() {
- var called = false;
- stubXhrToReturn(200);
- assertFalse('Callback should not yet have been called', called);
- return xhr.send('GET', 'test-url', null).then(function(stubXhr) {
- called = true;
- assertEquals('GET', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- });
- }
- function testSendPostSetsDefaultHeader() {
- stubXhrToReturn(200);
- return xhr.send('POST', 'test-url', null).then(function(stubXhr) {
- assertEquals('POST', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- assertEquals(
- 'application/x-www-form-urlencoded;charset=utf-8',
- stubXhr.headers['Content-Type']);
- });
- }
- function testSendPostDoesntSetHeaderWithFormData() {
- if (!goog.global['FormData']) {
- return;
- }
- var formData = new goog.global['FormData']();
- formData.append('name', 'value');
- stubXhrToReturn(200);
- return xhr.send('POST', 'test-url', formData).then(function(stubXhr) {
- assertEquals('POST', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- assertEquals(undefined, stubXhr.headers['Content-Type']);
- });
- }
- function testSendPostHeaders() {
- stubXhrToReturn(200);
- return xhr
- .send(
- 'POST', 'test-url', null,
- {headers: {'Content-Type': 'text/plain', 'X-Made-Up': 'FooBar'}})
- .then(function(stubXhr) {
- assertEquals('POST', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- assertEquals('text/plain', stubXhr.headers['Content-Type']);
- assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
- });
- }
- function testSendPostHeadersWithFormData() {
- if (!goog.global['FormData']) {
- return;
- }
- var formData = new goog.global['FormData']();
- formData.append('name', 'value');
- stubXhrToReturn(200);
- return xhr
- .send(
- 'POST', 'test-url', formData,
- {headers: {'Content-Type': 'text/plain', 'X-Made-Up': 'FooBar'}})
- .then(function(stubXhr) {
- assertEquals('POST', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- assertEquals('text/plain', stubXhr.headers['Content-Type']);
- assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
- });
- }
- function testSendNullPostHeaders() {
- stubXhrToReturn(200);
- return xhr
- .send('POST', 'test-url', null, {
- headers:
- {'Content-Type': null, 'X-Made-Up': 'FooBar', 'Y-Made-Up': null}
- })
- .then(function(stubXhr) {
- assertEquals('POST', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- assertEquals(undefined, stubXhr.headers['Content-Type']);
- assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
- assertEquals(undefined, stubXhr.headers['Y-Made-Up']);
- });
- }
- function testSendNullPostHeadersWithFormData() {
- if (!goog.global['FormData']) {
- return;
- }
- var formData = new goog.global['FormData']();
- formData.append('name', 'value');
- stubXhrToReturn(200);
- return xhr
- .send('POST', 'test-url', formData, {
- headers:
- {'Content-Type': null, 'X-Made-Up': 'FooBar', 'Y-Made-Up': null}
- })
- .then(function(stubXhr) {
- assertEquals('POST', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- assertEquals(undefined, stubXhr.headers['Content-Type']);
- assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
- assertEquals(undefined, stubXhr.headers['Y-Made-Up']);
- });
- }
- function testSendWithCredentials() {
- stubXhrToReturn(200);
- return xhr.send('POST', 'test-url', null, {withCredentials: true})
- .then(function(stubXhr) {
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertTrue(stubXhr.withCredentials);
- });
- }
- function testSendWithMimeType() {
- stubXhrToReturn(200);
- return xhr.send('POST', 'test-url', null, {mimeType: 'text/plain'})
- .then(function(stubXhr) {
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertEquals('text/plain', stubXhr.mimeType);
- });
- }
- function testSendWithHttpError() {
- stubXhrToReturn(500);
- return xhr.send('POST', 'test-url', null)
- .then(fail /* opt_onResolved */, function(err) {
- assertTrue(err instanceof xhr.HttpError);
- assertTrue(err.xhr.sent);
- assertEquals(500, err.status);
- });
- }
- function testSendWithTimeoutNotHit() {
- stubXhrToReturn(200, null /* opt_responseText */, 1400 /* opt_latency */);
- return xhr.send('POST', 'test-url', null, {timeoutMs: 1500})
- .then(function(stubXhr) {
- assertTrue(mockClock.getTimeoutsMade() > 0);
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertFalse('XHR should not have been aborted', stubXhr.aborted);
- });
- }
- function testSendWithTimeoutHit() {
- stubXhrToReturn(200, null /* opt_responseText */, 50 /* opt_latency */);
- return xhr.send('POST', 'test-url', null, {timeoutMs: 50})
- .then(fail /* opt_onResolved */, function(err) {
- assertTrue('XHR should have been sent', err.xhr.sent);
- assertTrue('XHR should have been aborted', err.xhr.aborted);
- assertTrue(err instanceof xhr.TimeoutError);
- });
- }
- function testCancelRequest() {
- stubXhrToReturn(200);
- var promise =
- xhr.send('GET', 'test-url')
- .then(fail /* opt_onResolved */, function(error) {
- assertTrue(error instanceof goog.Promise.CancellationError);
- return null; // Return a non-error value for the test runner.
- });
- promise.cancel();
- return promise;
- }
- function testGetJson() {
- var stubXhr = stubXhrToReturn(200, '{"a": 1, "b": 2}');
- xhr.getJson('test-url').then(function(responseObj) {
- assertObjectEquals({a: 1, b: 2}, responseObj);
- });
- }
- function testGetJsonWithXssiPrefix() {
- stubXhrToReturn(200, 'while(1);\n{"a": 1, "b": 2}');
- return xhr.getJson('test-url', {xssiPrefix: 'while(1);\n'})
- .then(function(responseObj) {
- assertObjectEquals({a: 1, b: 2}, responseObj);
- });
- }
- function testSendWithClientException() {
- stubXhrToThrow(new Error('CORS XHR with file:// schemas not allowed.'));
- return xhr.send('POST', 'file://test-url', null)
- .then(fail /* opt_onResolved */, function(err) {
- assertFalse('XHR should not have been sent', err.xhr.sent);
- assertTrue(err instanceof Error);
- assertTrue(
- /CORS XHR with file:\/\/ schemas not allowed./.test(err.message));
- });
- }
- function testSendWithFactory() {
- stubXhrToReturn(200);
- var options = {
- xmlHttpFactory: new goog.net.WrapperXmlHttpFactory(
- goog.partial(buildThrowingStubXhr, new Error('Bad factory')),
- goog.net.XmlHttp.getOptions)
- };
- return xhr.send('POST', 'file://test-url', null, options)
- .then(fail /* opt_onResolved */, function(err) {
- assertTrue(err instanceof Error);
- });
- }
|