jasmine-ajax源码剖析

整体概述

jasmine-ajax重要五个对象构成:

  • 要求捏造对象(FackXMLHttpRequest):
function FakeXMLHttpRequest() {}
  • 要求跟踪对象(RequestTracker):
function RequestTracker() {}
  • 要求阻拦对象(RequestStub):用刁难捏造的AJAX要求做出相应。
function RequestStub(url, stubData, method) {}
  • 要求阻拦跟踪对象(StubTracker):
function StubTracker() {}
  • 参数剖析(ParamParser):
function ParamParser() {}

辅佐函数

arrayContains

function arrayContains(arr, item) {
for (var i = 0; i < arr.length; i++) {
  if (arr[i] === item) {
    return true;
  }
}
return false;
}

函数构造很显著,剖断一个数组是不是包括某一个值。

extend

function extend(destination, source, propertiesToSkip) {
    propertiesToSkip = propertiesToSkip || [];
    for (var property in source) {
      if (!arrayContains(propertiesToSkip, property)) {
        destination[property] = source[property];
      }
    }
    return destination;
  }

此函数完成对象扩大,propertiesToSkip参数为source中需排撤除的键,余下的键值对掩盖写入destination

初始化语句

  if (typeof window === "undefined" && typeof exports === "object") {
    exports.MockAjax = MockAjax;
    jasmine.Ajax = new MockAjax(exports);
  } else {
    window.MockAjax = MockAjax;
    jasmine.Ajax = new MockAjax(window);
  }

起首剖断是不是commonjs环境,不是则通报window变量。在describe内部,即可接见经由过程jasmine.Ajax运用MockAjax对象。

RequestTracker

实在构造异常显著,运用jasmine.Ajax.requests即可接见到RequestTracker对象,然后经由过程first, mostRecent, at, filter 等要领猎取仿制的request对象,多用于对HTTP要求对象举行剖断,如要领,途径,数据,headers等等,须要重点关注。reset, track手动挪用意义不大,前者用于重置变量环境,后者在天生捏造XHR时挪用。

 function RequestTracker() {
    var requests = [];

    this.track = function(request) {
      requests.push(request);
    };

    this.first = function() {
      return requests[0];
    };

    this.count = function() {
      return requests.length;
    };

    this.reset = function() {
      requests = [];
    };

    this.mostRecent = function() {
      return requests[requests.length - 1];
    };

    this.at = function(index) {
      return requests[index];
    };

    this.filter = function(url_to_match) {
      if (requests.length === 0) { return []; }
      var matching_requests = [];

      for (var i = 0; i < requests.length; i++) {
        if (url_to_match instanceof RegExp &&
            url_to_match.test(requests[i].url)) {
            matching_requests.push(requests[i]);
        } else if (url_to_match instanceof Function &&
            url_to_match(requests[i])) {
            matching_requests.push(requests[i]);
        } else {
          if (requests[i].url === url_to_match) {
            matching_requests.push(requests[i]);
          }
        }
      }
      return matching_requests;
    };
  }

StubTracker

经由过程jasmine.Ajax.stubs即可接见到StubTracker对象,主动挪用意义不大。reset要领用于变量环境重置,addStub要领在天生RequestStub对象时挪用。findStubxhr.open()挪用时,剖断是不是定义过response

  function StubTracker() {
    var stubs = [];

    this.addStub = function(stub) {
      stubs.push(stub);
    };

    this.reset = function() {
      stubs = [];
    };

    this.findStub = function(url, data, method) {
      for (var i = stubs.length - 1; i >= 0; i--) {
        var stub = stubs[i];
        if (stub.matches(url, data, method)) {
          return stub;
        }
      }
    };
  }

RequestStub

要求阻拦在url, stubData, method同时婚配的情况下才会启动阻拦。

 function RequestStub(url, stubData, method) {
    var normalizeQuery = function(query) {
      return query ? query.split('&').sort().join('&') : undefined;
    };

    if (url instanceof RegExp) {
      this.url = url;
      this.query = undefined;
    } else {
      var split = url.split('?');
      this.url = split[0];
      this.query = split.length > 1 ? normalizeQuery(split[1]) : undefined;
    }

    this.data = normalizeQuery(stubData);
    this.method = method;

    this.andReturn = function(options) {
      this.status = options.status || 200;

      this.contentType = options.contentType;
      this.responseText = options.responseText;
    };

    this.matches = function(fullUrl, data, method) {
      var matches = false;
      fullUrl = fullUrl.toString();
      if (this.url instanceof RegExp) {
        matches = this.url.test(fullUrl);
      } else {
        var urlSplit = fullUrl.split('?'),
            url = urlSplit[0],
            query = urlSplit[1];
        matches = this.url === url && this.query === normalizeQuery(query);
      }
      return matches && (!this.data || this.data === normalizeQuery(data)) && (!this.method || this.method === method);
    };
  }

ParamParser

重要担任值的剖析。paramParsers参数为parser对象数组。findParser遍历paramParsers,经由过程test属性剖断是不是启用对应的parse要领处置惩罚数据。若决议挪用,则遍历完毕,返回该对象。add要领平常会经由过程jasmine.Ajax.addCustomParamParser(parser)挪用,优先级上,后增加的>先增加的>默许的。

  function ParamParser() {
    var defaults = [
      {
        test: function(xhr) {
          return (/^application\/json/).test(xhr.contentType());
        },
        parse: function jsonParser(paramString) {
          return JSON.parse(paramString);
        }
      },
      {
        test: function(xhr) {
          return true;
        },
        parse: function naiveParser(paramString) {
          var data = {};
          var params = paramString.split('&');

          for (var i = 0; i < params.length; ++i) {
            var kv = params[i].replace(/\+/g, ' ').split('=');
            var key = decodeURIComponent(kv[0]);
            data[key] = data[key] || [];
            data[key].push(decodeURIComponent(kv[1]));
          }
          return data;
        }
      }
    ];
    var paramParsers = [];

    this.add = function(parser) {
      paramParsers.unshift(parser);
    };

    this.findParser = function(xhr) {
        for(var i in paramParsers) {
          var parser = paramParsers[i];
          if (parser.test(xhr)) {
            return parser;
          }
        }
    };

    this.reset = function() {
      paramParsers = [];
      for(var i in defaults) {
        paramParsers.push(defaults[i]);
      }
    };

    this.reset();
  }

FackXMLHttpRequest

代码太长,分段申明。

  • FakeXMLHttpRequest
    实例化后,即增加进入requestTracker,便于后期接见。
function FakeXMLHttpRequest() {
  requestTracker.track(this);
  this.requestHeaders = {};
  this.overriddenMimeType = null;
}
  • 原型继续
    此处觉得比较好玩,是继续真的XMLHttpRequest对象,只是去掉几个特别键值对,后面会举行处置惩罚。
var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout'];
    extend(FakeXMLHttpRequest.prototype, new window.XMLHttpRequest(), iePropertiesThatCannotBeCopied);
  • Request捏造
    熟习原生ajax的应当很轻易看懂。重点在于,send要领挪用时,会立即在stubs里寻觅婚配当前url,method,data的相应阻拦器,假如预定义过了,则会马上通报给response函数举行相应,不然须要以后手动挪用response函数举行相应。
    extend(FakeXMLHttpRequest.prototype, {

      open: function() {
        this.method = arguments[0];
        this.url = arguments[1];
        this.username = arguments[3];
        this.password = arguments[4];
        this.readyState = 1;
        this.onreadystatechange();
      },

      setRequestHeader: function(header, value) {
        if(this.requestHeaders.hasOwnProperty(header)) {
          this.requestHeaders[header] = [this.requestHeaders[header], value].join(', ');
        } else {
          this.requestHeaders[header] = value;
        }
      },

      overrideMimeType: function(mime) {
        this.overriddenMimeType = mime;
      },

      abort: function() {
        this.readyState = 0;
        this.status = 0;
        this.statusText = "abort";
        this.onreadystatechange();
      },

      readyState: 0,

      onload: function() {
      },

      onreadystatechange: function(isTimeout) {
      },

      status: null,

      send: function(data) {
        this.params = data;
        this.readyState = 2;
        this.onreadystatechange();

        var stub = stubTracker.findStub(this.url, data, this.method);
        if (stub) {
          this.response(stub);
        }
      },

      contentType: function() {
        return findHeader('content-type', this.requestHeaders);
      },

      data: function() {
        if (!this.params) {
          return {};
        }

        return paramParser.findParser(this).parse(this.params);
      },

      getResponseHeader: function(name) {
        return findHeader(name, this.responseHeaders);
      },

      getAllResponseHeaders: function() {
        var responseHeaders = [];
        for (var i in this.responseHeaders) {
          if (this.responseHeaders.hasOwnProperty(i)) {
            responseHeaders.push(i + ': ' + this.responseHeaders[i]);
          }
        }
        return responseHeaders.join('\r\n');
      },

      responseText: null,

      response: function(response) {
        if (this.readyState === 4) {
          throw new Error("FakeXMLHttpRequest already completed");
        }
        this.status = response.status;
        this.statusText = response.statusText || "";
        this.responseText = response.responseText || "";
        this.readyState = 4;
        this.responseHeaders = response.responseHeaders ||
          {"Content-Type": response.contentType || "application/json" };

        this.onload();
        this.onreadystatechange();
      },

      responseTimeout: function() {
        if (this.readyState === 4) {
          throw new Error("FakeXMLHttpRequest already completed");
        }
        this.readyState = 4;
        jasmine.clock().tick(30000);
        this.onreadystatechange('timeout');
      }
    });

    return FakeXMLHttpRequest;
  }

经验交流

QQ: 491229492
Email: huang.jian@eisoo.com

    原文作者:怀疑真爱的流浪者jason
    原文地址: https://segmentfault.com/a/1190000000623658
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞