pretenderjs/pretender

View on GitHub
src/create-passthrough.ts

Summary

Maintainability
B
5 hrs
Test Coverage
export function createPassthrough(fakeXHR, nativeXMLHttpRequest) {
  // event types to handle on the xhr
  var evts = ['error', 'timeout', 'abort', 'readystatechange'];

  // event types to handle on the xhr.upload
  var uploadEvents = [];

  // properties to copy from the native xhr to fake xhr
  var lifecycleProps = [
    'readyState',
    'responseText',
    'response',
    'responseXML',
    'responseURL',
    'status',
    'statusText',
  ];

  var xhr = (fakeXHR._passthroughRequest = new nativeXMLHttpRequest());
  xhr.open(
    fakeXHR.method,
    fakeXHR.url,
    fakeXHR.async,
    fakeXHR.username,
    fakeXHR.password
  );

  if (fakeXHR.responseType === 'arraybuffer') {
    lifecycleProps = ['readyState', 'response', 'status', 'statusText'];
    xhr.responseType = fakeXHR.responseType;
  }

  // use onload if the browser supports it
  if ('onload' in xhr) {
    evts.push('load');
  }

  // add progress event for async calls
  // avoid using progress events for sync calls, they will hang https://bugs.webkit.org/show_bug.cgi?id=40996.
  if (fakeXHR.async && fakeXHR.responseType !== 'arraybuffer') {
    evts.push('progress');
    uploadEvents.push('progress');
  }

  // update `propertyNames` properties from `fromXHR` to `toXHR`
  function copyLifecycleProperties(propertyNames, fromXHR, toXHR) {
    for (var i = 0; i < propertyNames.length; i++) {
      var prop = propertyNames[i];
      if (prop in fromXHR) {
        toXHR[prop] = fromXHR[prop];
      }
    }
  }

  // fire fake event on `eventable`
  function dispatchEvent(eventable, eventType, event) {
    eventable.dispatchEvent(event);
    if (eventable['on' + eventType]) {
      eventable['on' + eventType](event);
    }
  }

  // set the on- handler on the native xhr for the given eventType
  function createHandler(eventType) {
    xhr['on' + eventType] = function (event) {
      copyLifecycleProperties(lifecycleProps, xhr, fakeXHR);
      dispatchEvent(fakeXHR, eventType, event);
    };
  }

  // set the on- handler on the native xhr's `upload` property for
  // the given eventType
  function createUploadHandler(eventType) {
    if (xhr.upload && fakeXHR.upload && fakeXHR.upload['on' + eventType]) {
      xhr.upload['on' + eventType] = function (event) {
        dispatchEvent(fakeXHR.upload, eventType, event);
      };
    }
  }

  var i;
  for (i = 0; i < evts.length; i++) {
    createHandler(evts[i]);
  }
  for (i = 0; i < uploadEvents.length; i++) {
    createUploadHandler(uploadEvents[i]);
  }

  if (fakeXHR.async) {
    xhr.timeout = fakeXHR.timeout;
    xhr.withCredentials = fakeXHR.withCredentials;
  }
  // XMLHttpRequest.timeout default initializes to 0, and is not allowed to be used for
  // synchronous XMLHttpRequests requests in a document environment. However, when a XHR
  // polyfill does not sets the timeout value, it will throw in React Native environment.
  // TODO:
  // synchronous XHR is deprecated, make async the default as XMLHttpRequest.open(),
  // and throw error if sync XHR has timeout not 0
  if (!xhr.timeout && xhr.timeout !== 0) {
    xhr.timeout = 0; // default XMLHttpRequest timeout
  }
  for (var h in fakeXHR.requestHeaders) {
    xhr.setRequestHeader(h, fakeXHR.requestHeaders[h]);
  }
  return xhr;
}