摘要:總體概述主要五個(gè)對(duì)象組成請(qǐng)求偽造對(duì)象請(qǐng)求跟蹤對(duì)象請(qǐng)求攔截對(duì)象用作對(duì)偽造的請(qǐng)求做出響應(yīng)。請(qǐng)求攔截跟蹤對(duì)象參數(shù)解析輔助函數(shù)函數(shù)結(jié)構(gòu)很明顯,判定一個(gè)數(shù)組是否包含某一個(gè)值。主要負(fù)責(zé)值的解析。若決定調(diào)用,則遍歷結(jié)束,返回該對(duì)象。
總體概述
jasmine-ajax主要五個(gè)對(duì)象組成:
請(qǐng)求偽造對(duì)象(FackXMLHttpRequest):
function FakeXMLHttpRequest() {}
請(qǐng)求跟蹤對(duì)象(RequestTracker):
function RequestTracker() {}
請(qǐng)求攔截對(duì)象(RequestStub):用作對(duì)偽造的AJAX請(qǐng)求做出響應(yīng)。
function RequestStub(url, stubData, method) {}
請(qǐng)求攔截跟蹤對(duì)象(StubTracker):
function StubTracker() {}
參數(shù)解析(ParamParser):
function ParamParser() {}輔助函數(shù) arrayContains
function arrayContains(arr, item) { for (var i = 0; i < arr.length; i++) { if (arr[i] === item) { return true; } } return false; }
函數(shù)結(jié)構(gòu)很明顯,判定一個(gè)數(shù)組是否包含某一個(gè)值。
extendfunction extend(destination, source, propertiesToSkip) { propertiesToSkip = propertiesToSkip || []; for (var property in source) { if (!arrayContains(propertiesToSkip, property)) { destination[property] = source[property]; } } return destination; }
此函數(shù)實(shí)現(xiàn)對(duì)象擴(kuò)展,propertiesToSkip參數(shù)為source中需排除掉的鍵,余下的鍵值對(duì)覆蓋寫入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環(huán)境,不是則傳遞window變量。在describe內(nèi)部,即可訪問通過jasmine.Ajax使用MockAjax對(duì)象。
RequestTracker其實(shí)結(jié)構(gòu)非常明顯,使用jasmine.Ajax.requests即可訪問到RequestTracker對(duì)象,然后通過first, mostRecent, at, filter 等方法獲取仿造的request對(duì)象,多用于對(duì)HTTP請(qǐng)求對(duì)象進(jìn)行判定,如方法,路徑,數(shù)據(jù),headers等等,需要重點(diǎn)關(guān)注。reset, track手動(dòng)調(diào)用意義不大,前者用于重置變量環(huán)境,后者在生成偽造XHR時(shí)調(diào)用。
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對(duì)象,主動(dòng)調(diào)用意義不大。reset方法用于變量環(huán)境重置,addStub方法在生成RequestStub對(duì)象時(shí)調(diào)用。findStub在xhr.open()調(diào)用時(shí),判定是否定義過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
請(qǐng)求攔截在url, stubData, method同時(shí)匹配的情況下才會(huì)啟動(dòng)攔截。
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
主要負(fù)責(zé)值的解析。paramParsers參數(shù)為parser對(duì)象數(shù)組。findParser遍歷paramParsers,通過test屬性判定是否啟用對(duì)應(yīng)的parse方法處理數(shù)據(jù)。若決定調(diào)用,則遍歷結(jié)束,返回該對(duì)象。add方法一般會(huì)通過jasmine.Ajax.addCustomParamParser(parser)調(diào)用,優(yōu)先級(jí)上,后添加的>先添加的>默認(rèn)的。
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
實(shí)例化后,即添加進(jìn)入requestTracker,便于后期訪問。
function FakeXMLHttpRequest() { requestTracker.track(this); this.requestHeaders = {}; this.overriddenMimeType = null; }
原型繼承
此處感覺比較好玩,是繼承真的XMLHttpRequest對(duì)象,只是去掉幾個(gè)特殊鍵值對(duì),后面會(huì)進(jìn)行處理。
var iePropertiesThatCannotBeCopied = ["responseBody", "responseText", "responseXML", "status", "statusText", "responseTimeout"]; extend(FakeXMLHttpRequest.prototype, new window.XMLHttpRequest(), iePropertiesThatCannotBeCopied);
Request偽造
熟悉原生ajax的應(yīng)該很容易看懂。重點(diǎn)在于,send方法調(diào)用時(shí),會(huì)立即在stubs里尋找匹配當(dāng)前url,method,data的響應(yīng)攔截器,如果預(yù)定義過了,則會(huì)立刻傳遞給response函數(shù)進(jìn)行響應(yīng),否則需要之后手動(dòng)調(diào)用response函數(shù)進(jìn)行響應(yīng)。
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(" "); }, 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; }經(jīng)驗(yàn)交流
QQ: 491229492
Email: huang.jian@eisoo.com
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/85243.html
摘要:如有排版錯(cuò)誤,請(qǐng)查閱官方文檔未定義昨日遇到問題,在安裝后,對(duì)應(yīng)框架未能引入,需要全局安裝后方可排除。使用單元測試中,為使用單元測試中,鏈?zhǔn)秸{(diào)用函數(shù)與最新文檔有明顯差異。總結(jié)來說,就是使用前,確定使用的是哪一個(gè)版本,再進(jìn)行操作。 如有排版錯(cuò)誤,請(qǐng)查閱https://www.zybuluo.com/bornkiller/note/24702 官方文檔: jasmine : http...
After translated a blog about how Promise works in a more functional programming way, I tried to build something to make Ext Ajax call to work in Promise style as a practice. ExtPromise is a simple w...
摘要:有如下模塊源碼解析源碼解析源碼解析源碼解析源碼解析源碼解析源碼解析源碼解析源碼解析使用和監(jiān)控和博客從到學(xué)習(xí)介紹從到學(xué)習(xí)上搭建環(huán)境并構(gòu)建運(yùn)行簡單程序入門從到學(xué)習(xí)配置文件詳解從到學(xué)習(xí)介紹從到學(xué)習(xí)如何自 Flink Metrics 有如下模塊: Flink Metrics 源碼解析 —— Flink-metrics-core Flink Metrics 源碼解析 —— Flink-metr...
摘要:機(jī)制博客從到學(xué)習(xí)介紹從到學(xué)習(xí)上搭建環(huán)境并構(gòu)建運(yùn)行簡單程序入門從到學(xué)習(xí)配置文件詳解從到學(xué)習(xí)介紹從到學(xué)習(xí)如何自定義從到學(xué)習(xí)介紹從到學(xué)習(xí)如何自定義從到學(xué)習(xí)轉(zhuǎn)換從到學(xué)習(xí)介紹中的從到學(xué)習(xí)中的幾種詳解從到學(xué)習(xí)讀取數(shù)據(jù)寫入到從到學(xué)習(xí)項(xiàng)目如何運(yùn)行從 Flink Checkpoint 機(jī)制 https://t.zsxq.com/ynQNbeM 博客 1、Flink 從0到1學(xué)習(xí) —— Apache Fl...
摘要:序列化機(jī)制博客從到學(xué)習(xí)介紹從到學(xué)習(xí)上搭建環(huán)境并構(gòu)建運(yùn)行簡單程序入門從到學(xué)習(xí)配置文件詳解從到學(xué)習(xí)介紹從到學(xué)習(xí)如何自定義從到學(xué)習(xí)介紹從到學(xué)習(xí)如何自定義從到學(xué)習(xí)轉(zhuǎn)換從到學(xué)習(xí)介紹中的從到學(xué)習(xí)中的幾種詳解從到學(xué)習(xí)讀取數(shù)據(jù)寫入到從到學(xué)習(xí)項(xiàng)目如何 Flink 序列化機(jī)制 https://t.zsxq.com/JaQfeMf 博客 1、Flink 從0到1學(xué)習(xí) —— Apache Flink 介紹 2...
閱讀 2335·2021-11-24 09:39
閱讀 3778·2021-11-19 09:40
閱讀 2153·2021-09-27 13:36
閱讀 1897·2019-08-30 15:44
閱讀 390·2019-08-30 13:52
閱讀 2713·2019-08-30 11:13
閱讀 2171·2019-08-29 16:18
閱讀 1755·2019-08-29 15:43