mirror of
https://github.com/status-im/react-native.git
synced 2025-01-10 01:25:39 +00:00
08c375f828
Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
184 lines
5.3 KiB
JavaScript
184 lines
5.3 KiB
JavaScript
/**
|
|
* Copyright (c) 2013-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
jest
|
|
.disableAutomock()
|
|
.dontMock('event-target-shim')
|
|
.setMock('NativeModules', {
|
|
Networking: {
|
|
addListener: function() {},
|
|
removeListeners: function() {},
|
|
sendRequest: (options, callback) => {
|
|
callback(1);
|
|
},
|
|
abortRequest: function() {},
|
|
}
|
|
});
|
|
|
|
const XMLHttpRequest = require('XMLHttpRequest');
|
|
|
|
describe('XMLHttpRequest', function() {
|
|
var xhr;
|
|
var handleTimeout;
|
|
var handleError;
|
|
var handleLoad;
|
|
var handleReadyStateChange;
|
|
|
|
beforeEach(() => {
|
|
xhr = new XMLHttpRequest();
|
|
|
|
xhr.ontimeout = jest.fn();
|
|
xhr.onerror = jest.fn();
|
|
xhr.onload = jest.fn();
|
|
xhr.onreadystatechange = jest.fn();
|
|
|
|
handleTimeout = jest.fn();
|
|
handleError = jest.fn();
|
|
handleLoad = jest.fn();
|
|
handleReadyStateChange = jest.fn();
|
|
|
|
xhr.addEventListener('timeout', handleTimeout);
|
|
xhr.addEventListener('error', handleError);
|
|
xhr.addEventListener('load', handleLoad);
|
|
xhr.addEventListener('readystatechange', handleReadyStateChange);
|
|
});
|
|
|
|
afterEach(() => {
|
|
xhr = null;
|
|
handleTimeout = null;
|
|
handleError = null;
|
|
handleLoad = null;
|
|
});
|
|
|
|
it('should transition readyState correctly', function() {
|
|
expect(xhr.readyState).toBe(xhr.UNSENT);
|
|
|
|
xhr.open('GET', 'blabla');
|
|
|
|
expect(xhr.onreadystatechange.mock.calls.length).toBe(1);
|
|
expect(handleReadyStateChange.mock.calls.length).toBe(1);
|
|
expect(xhr.readyState).toBe(xhr.OPENED);
|
|
});
|
|
|
|
it('should expose responseType correctly', function() {
|
|
expect(xhr.responseType).toBe('');
|
|
|
|
// Setting responseType to an unsupported value has no effect.
|
|
xhr.responseType = 'arrayblobbuffertextfile';
|
|
expect(xhr.responseType).toBe('');
|
|
|
|
xhr.responseType = 'arraybuffer';
|
|
expect(xhr.responseType).toBe('arraybuffer');
|
|
|
|
// Can't change responseType after first data has been received.
|
|
xhr.open('GET', 'blabla');
|
|
xhr.send();
|
|
expect(() => { xhr.responseType = 'text'; }).toThrow();
|
|
});
|
|
|
|
it('should expose responseText correctly', function() {
|
|
xhr.responseType = '';
|
|
expect(xhr.responseText).toBe('');
|
|
expect(xhr.response).toBe('');
|
|
|
|
xhr.responseType = 'arraybuffer';
|
|
expect(() => xhr.responseText).toThrow();
|
|
expect(xhr.response).toBe(null);
|
|
|
|
xhr.responseType = 'text';
|
|
expect(xhr.responseText).toBe('');
|
|
expect(xhr.response).toBe('');
|
|
|
|
// responseText is read-only.
|
|
expect(() => { xhr.responseText = 'hi'; }).toThrow();
|
|
expect(xhr.responseText).toBe('');
|
|
expect(xhr.response).toBe('');
|
|
|
|
xhr.open('GET', 'blabla');
|
|
xhr.send();
|
|
xhr.__didReceiveData(1, 'Some data');
|
|
expect(xhr.responseText).toBe('Some data');
|
|
});
|
|
|
|
it('should call ontimeout function when the request times out', function() {
|
|
xhr.open('GET', 'blabla');
|
|
xhr.send();
|
|
xhr.__didCompleteResponse(1, 'Timeout', true);
|
|
xhr.__didCompleteResponse(1, 'Timeout', true);
|
|
|
|
expect(xhr.readyState).toBe(xhr.DONE);
|
|
|
|
expect(xhr.ontimeout.mock.calls.length).toBe(1);
|
|
expect(xhr.onerror).not.toBeCalled();
|
|
expect(xhr.onload).not.toBeCalled();
|
|
|
|
expect(handleTimeout.mock.calls.length).toBe(1);
|
|
expect(handleError).not.toBeCalled();
|
|
expect(handleLoad).not.toBeCalled();
|
|
});
|
|
|
|
it('should call onerror function when the request times out', function() {
|
|
xhr.open('GET', 'blabla');
|
|
xhr.send();
|
|
xhr.__didCompleteResponse(1, 'Generic error');
|
|
|
|
expect(xhr.readyState).toBe(xhr.DONE);
|
|
|
|
expect(xhr.onreadystatechange.mock.calls.length).toBe(2);
|
|
expect(xhr.onerror.mock.calls.length).toBe(1);
|
|
expect(xhr.ontimeout).not.toBeCalled();
|
|
expect(xhr.onload).not.toBeCalled();
|
|
|
|
expect(handleReadyStateChange.mock.calls.length).toBe(2);
|
|
expect(handleError.mock.calls.length).toBe(1);
|
|
expect(handleTimeout).not.toBeCalled();
|
|
expect(handleLoad).not.toBeCalled();
|
|
});
|
|
|
|
it('should call onload function when there is no error', function() {
|
|
xhr.open('GET', 'blabla');
|
|
xhr.send();
|
|
xhr.__didCompleteResponse(1, null);
|
|
|
|
expect(xhr.readyState).toBe(xhr.DONE);
|
|
|
|
expect(xhr.onreadystatechange.mock.calls.length).toBe(2);
|
|
expect(xhr.onload.mock.calls.length).toBe(1);
|
|
expect(xhr.onerror).not.toBeCalled();
|
|
expect(xhr.ontimeout).not.toBeCalled();
|
|
|
|
expect(handleReadyStateChange.mock.calls.length).toBe(2);
|
|
expect(handleLoad.mock.calls.length).toBe(1);
|
|
expect(handleError).not.toBeCalled();
|
|
expect(handleTimeout).not.toBeCalled();
|
|
});
|
|
|
|
it('should call onload function when there is no error', function() {
|
|
xhr.open('GET', 'blabla');
|
|
xhr.send();
|
|
|
|
xhr.upload.onprogress = jest.fn();
|
|
var handleProgress = jest.fn();
|
|
xhr.upload.addEventListener('progress', handleProgress);
|
|
|
|
xhr.__didUploadProgress(1, 42, 100);
|
|
|
|
expect(xhr.upload.onprogress.mock.calls.length).toBe(1);
|
|
expect(handleProgress.mock.calls.length).toBe(1);
|
|
|
|
expect(xhr.upload.onprogress.mock.calls[0][0].loaded).toBe(42);
|
|
expect(xhr.upload.onprogress.mock.calls[0][0].total).toBe(100);
|
|
expect(handleProgress.mock.calls[0][0].loaded).toBe(42);
|
|
expect(handleProgress.mock.calls[0][0].total).toBe(100);
|
|
});
|
|
|
|
});
|