mock平台

postmanLib.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. const { isJson5, json_parse, handleJson, joinPath, safeArray } = require('./utils');
  2. const constants = require('../client/constants/variable.js');
  3. const _ = require('underscore');
  4. const URL = require('url');
  5. const utils = require('./power-string.js').utils;
  6. const HTTP_METHOD = constants.HTTP_METHOD;
  7. const axios = require('axios');
  8. const qs = require('qs');
  9. const CryptoJS = require('crypto-js');
  10. const jsrsasign = require('jsrsasign');
  11. const https = require('https');
  12. const isNode = typeof global == 'object' && global.global === global;
  13. const ContentTypeMap = {
  14. 'application/json': 'json',
  15. 'application/xml': 'xml',
  16. 'text/xml': 'xml',
  17. 'application/html': 'html',
  18. 'text/html': 'html',
  19. other: 'text'
  20. };
  21. const getStorage = async (id)=>{
  22. try{
  23. if(isNode){
  24. let storage = global.storageCreator(id);
  25. let data = await storage.getItem();
  26. return {
  27. getItem: (name)=> data[name],
  28. setItem: (name, value)=>{
  29. data[name] = value;
  30. storage.setItem(name, value)
  31. }
  32. }
  33. }else{
  34. return {
  35. getItem: (name)=> window.localStorage.getItem(name),
  36. setItem: (name, value)=> window.localStorage.setItem(name, value)
  37. }
  38. }
  39. }catch(e){
  40. console.error(e)
  41. return {
  42. getItem: (name)=>{
  43. console.error(name, e)
  44. },
  45. setItem: (name, value)=>{
  46. console.error(name, value, e)
  47. }
  48. }
  49. }
  50. }
  51. async function httpRequestByNode(options) {
  52. function handleRes(response) {
  53. if (!response || typeof response !== 'object') {
  54. return {
  55. res: {
  56. status: 500,
  57. body: isNode
  58. ? '请求出错, 内网服务器自动化测试无法访问到,请检查是否为内网服务器!'
  59. : '请求出错'
  60. }
  61. };
  62. }
  63. return {
  64. res: {
  65. header: response.headers,
  66. status: response.status,
  67. body: response.data
  68. }
  69. };
  70. }
  71. function handleData() {
  72. let contentTypeItem;
  73. if (!options) return;
  74. if (typeof options.headers === 'object' && options.headers) {
  75. Object.keys(options.headers).forEach(key => {
  76. if (/content-type/i.test(key)) {
  77. if (options.headers[key]) {
  78. contentTypeItem = options.headers[key]
  79. .split(';')[0]
  80. .trim()
  81. .toLowerCase();
  82. }
  83. }
  84. if (!options.headers[key]) delete options.headers[key];
  85. });
  86. if (
  87. contentTypeItem === 'application/x-www-form-urlencoded' &&
  88. typeof options.data === 'object' &&
  89. options.data
  90. ) {
  91. options.data = qs.stringify(options.data);
  92. }
  93. }
  94. }
  95. try {
  96. handleData(options);
  97. let response = await axios({
  98. method: options.method,
  99. url: options.url,
  100. headers: options.headers,
  101. timeout: 10000,
  102. maxRedirects: 0,
  103. httpsAgent: new https.Agent({
  104. rejectUnauthorized: false
  105. }),
  106. data: options.data
  107. });
  108. return handleRes(response);
  109. } catch (err) {
  110. if (err.response === undefined) {
  111. return handleRes({
  112. headers: {},
  113. status: null,
  114. data: err.message
  115. });
  116. }
  117. return handleRes(err.response);
  118. }
  119. }
  120. function handleContentType(headers) {
  121. if (!headers || typeof headers !== 'object') return ContentTypeMap.other;
  122. let contentTypeItem = 'other';
  123. try {
  124. Object.keys(headers).forEach(key => {
  125. if (/content-type/i.test(key)) {
  126. contentTypeItem = headers[key]
  127. .split(';')[0]
  128. .trim()
  129. .toLowerCase();
  130. }
  131. });
  132. return ContentTypeMap[contentTypeItem] ? ContentTypeMap[contentTypeItem] : ContentTypeMap.other;
  133. } catch (err) {
  134. return ContentTypeMap.other;
  135. }
  136. }
  137. function checkRequestBodyIsRaw(method, reqBodyType) {
  138. if (
  139. reqBodyType &&
  140. reqBodyType !== 'file' &&
  141. reqBodyType !== 'form' &&
  142. HTTP_METHOD[method].request_body
  143. ) {
  144. return reqBodyType;
  145. }
  146. return false;
  147. }
  148. function checkNameIsExistInArray(name, arr) {
  149. let isRepeat = false;
  150. for (let i = 0; i < arr.length; i++) {
  151. let item = arr[i];
  152. if (item.name === name) {
  153. isRepeat = true;
  154. break;
  155. }
  156. }
  157. return isRepeat;
  158. }
  159. function handleCurrDomain(domains, case_env) {
  160. let currDomain = _.find(domains, item => item.name === case_env);
  161. if (!currDomain) {
  162. currDomain = domains[0];
  163. }
  164. return currDomain;
  165. }
  166. function sandboxByNode(sandbox = {}, script) {
  167. const vm = require('vm');
  168. script = new vm.Script(script);
  169. const context = new vm.createContext(sandbox);
  170. script.runInContext(context, {
  171. timeout: 10000
  172. });
  173. return sandbox;
  174. }
  175. async function sandbox(context = {}, script) {
  176. if (isNode) {
  177. try {
  178. context.context = context;
  179. context.console = console;
  180. context.Promise = Promise;
  181. context.setTimeout = setTimeout;
  182. context = sandboxByNode(context, script);
  183. } catch (err) {
  184. err.message = `Script: ${script}
  185. message: ${err.message}`;
  186. throw err;
  187. }
  188. } else {
  189. context = sandboxByBrowser(context, script);
  190. }
  191. if (context.promise && typeof context.promise === 'object' && context.promise.then) {
  192. try {
  193. await context.promise;
  194. } catch (err) {
  195. err.message = `Script: ${script}
  196. message: ${err.message}`;
  197. throw err;
  198. }
  199. }
  200. return context;
  201. }
  202. function sandboxByBrowser(context = {}, script) {
  203. if (!script || typeof script !== 'string') {
  204. return context;
  205. }
  206. let beginScript = '';
  207. for (var i in context) {
  208. beginScript += `var ${i} = context.${i};`;
  209. }
  210. try {
  211. eval(beginScript + script);
  212. } catch (err) {
  213. let message = `Script:
  214. ----CodeBegin----:
  215. ${beginScript}
  216. ${script}
  217. ----CodeEnd----
  218. `;
  219. err.message = `Script: ${message}
  220. message: ${err.message}`;
  221. throw err;
  222. }
  223. return context;
  224. }
  225. /**
  226. *
  227. * @param {*} defaultOptions
  228. * @param {*} preScript
  229. * @param {*} afterScript
  230. * @param {*} commonContext 负责传递一些业务信息,crossRequest 不关注具体传什么,只负责当中间人
  231. */
  232. async function crossRequest(defaultOptions, preScript, afterScript, commonContext = {}) {
  233. let options = Object.assign({}, defaultOptions);
  234. const taskId = options.taskId || Math.random() + '';
  235. let urlObj = URL.parse(options.url, true),
  236. query = {};
  237. query = Object.assign(query, urlObj.query);
  238. let context = {
  239. isNode,
  240. get href() {
  241. return urlObj.href;
  242. },
  243. set href(val) {
  244. throw new Error('context.href 不能被赋值');
  245. },
  246. get hostname() {
  247. return urlObj.hostname;
  248. },
  249. set hostname(val) {
  250. throw new Error('context.hostname 不能被赋值');
  251. },
  252. get caseId() {
  253. return options.caseId;
  254. },
  255. set caseId(val) {
  256. throw new Error('context.caseId 不能被赋值');
  257. },
  258. method: options.method,
  259. pathname: urlObj.pathname,
  260. query: query,
  261. requestHeader: options.headers || {},
  262. requestBody: options.data,
  263. promise: false,
  264. storage: await getStorage(taskId)
  265. };
  266. Object.assign(context, commonContext)
  267. context.utils = Object.freeze({
  268. _: _,
  269. CryptoJS: CryptoJS,
  270. jsrsasign: jsrsasign,
  271. base64: utils.base64,
  272. md5: utils.md5,
  273. sha1: utils.sha1,
  274. sha224: utils.sha224,
  275. sha256: utils.sha256,
  276. sha384: utils.sha384,
  277. sha512: utils.sha512,
  278. unbase64: utils.unbase64,
  279. axios: axios
  280. });
  281. if (preScript) {
  282. context = await sandbox(context, preScript);
  283. defaultOptions.url = options.url = URL.format({
  284. protocol: urlObj.protocol,
  285. host: urlObj.host,
  286. query: context.query,
  287. pathname: context.pathname
  288. });
  289. defaultOptions.headers = options.headers = context.requestHeader;
  290. defaultOptions.data = options.data = context.requestBody;
  291. }
  292. let data;
  293. if (isNode) {
  294. data = await httpRequestByNode(options);
  295. data.req = options;
  296. } else {
  297. data = await new Promise((resolve, reject) => {
  298. options.error = options.success = function(res, header, data) {
  299. let message = '';
  300. if (res && typeof res === 'string') {
  301. res = json_parse(data.res.body);
  302. data.res.body = res;
  303. }
  304. if (!isNode) message = '请求异常,请检查 chrome network 错误信息... https://juejin.im/post/5c888a3e5188257dee0322af 通过该链接查看教程")';
  305. if (isNaN(data.res.status)) {
  306. reject({
  307. body: res || message,
  308. header,
  309. message
  310. });
  311. }
  312. resolve(data);
  313. };
  314. window.crossRequest(options);
  315. });
  316. }
  317. if (afterScript) {
  318. context.responseData = data.res.body;
  319. context.responseHeader = data.res.header;
  320. context.responseStatus = data.res.status;
  321. context.runTime = data.runTime;
  322. context = await sandbox(context, afterScript);
  323. data.res.body = context.responseData;
  324. data.res.header = context.responseHeader;
  325. data.res.status = context.responseStatus;
  326. data.runTime = context.runTime;
  327. }
  328. return data;
  329. }
  330. function handleParams(interfaceData, handleValue, requestParams) {
  331. let interfaceRunData = Object.assign({}, interfaceData);
  332. function paramsToObjectWithEnable(arr) {
  333. const obj = {};
  334. safeArray(arr).forEach(item => {
  335. if (item && item.name && (item.enable || item.required === '1')) {
  336. obj[item.name] = handleValue(item.value, currDomain.global);
  337. if (requestParams) {
  338. requestParams[item.name] = obj[item.name];
  339. }
  340. }
  341. });
  342. return obj;
  343. }
  344. function paramsToObjectUnWithEnable(arr) {
  345. const obj = {};
  346. safeArray(arr).forEach(item => {
  347. if (item && item.name) {
  348. obj[item.name] = handleValue(item.value, currDomain.global);
  349. if (requestParams) {
  350. requestParams[item.name] = obj[item.name];
  351. }
  352. }
  353. });
  354. return obj;
  355. }
  356. let { case_env, path, env, _id } = interfaceRunData;
  357. let currDomain,
  358. requestBody,
  359. requestOptions = {};
  360. currDomain = handleCurrDomain(env, case_env);
  361. interfaceRunData.req_params = interfaceRunData.req_params || [];
  362. interfaceRunData.req_params.forEach(item => {
  363. let val = handleValue(item.value, currDomain.global);
  364. if (requestParams) {
  365. requestParams[item.name] = val;
  366. }
  367. path = path.replace(`:${item.name}`, val || `:${item.name}`);
  368. path = path.replace(`{${item.name}}`, val || `{${item.name}}`);
  369. });
  370. const urlObj = URL.parse(joinPath(currDomain.domain, path), true);
  371. const url = URL.format({
  372. protocol: urlObj.protocol || 'http',
  373. host: urlObj.host,
  374. pathname: urlObj.pathname,
  375. query: Object.assign(urlObj.query, paramsToObjectWithEnable(interfaceRunData.req_query))
  376. });
  377. let headers = paramsToObjectUnWithEnable(interfaceRunData.req_headers);
  378. requestOptions = {
  379. url,
  380. caseId: _id,
  381. method: interfaceRunData.method,
  382. headers,
  383. timeout: 82400000
  384. };
  385. // 对 raw 类型的 form 处理
  386. try {
  387. if (interfaceRunData.req_body_type === 'raw') {
  388. if (headers && headers['Content-Type']) {
  389. if (headers['Content-Type'].indexOf('application/x-www-form-urlencoded') >= 0) {
  390. interfaceRunData.req_body_type = 'form';
  391. let reqData = json_parse(interfaceRunData.req_body_other);
  392. if (reqData && typeof reqData === 'object') {
  393. interfaceRunData.req_body_form = [];
  394. Object.keys(reqData).forEach(key => {
  395. interfaceRunData.req_body_form.push({
  396. name: key,
  397. type: 'text',
  398. value: JSON.stringify(reqData[key]),
  399. enable: true
  400. });
  401. });
  402. }
  403. } else if (headers['Content-Type'].indexOf('application/json') >= 0) {
  404. interfaceRunData.req_body_type = 'json';
  405. }
  406. }
  407. }
  408. } catch (e) {
  409. console.error('err', e);
  410. }
  411. if (HTTP_METHOD[interfaceRunData.method].request_body) {
  412. if (interfaceRunData.req_body_type === 'form') {
  413. requestBody = paramsToObjectWithEnable(
  414. safeArray(interfaceRunData.req_body_form).filter(item => {
  415. return item.type == 'text';
  416. })
  417. );
  418. } else if (interfaceRunData.req_body_type === 'json') {
  419. let reqBody = isJson5(interfaceRunData.req_body_other);
  420. if (reqBody === false) {
  421. requestBody = interfaceRunData.req_body_other;
  422. } else {
  423. if (requestParams) {
  424. requestParams = Object.assign(requestParams, reqBody);
  425. }
  426. requestBody = handleJson(reqBody, val => handleValue(val, currDomain.global));
  427. }
  428. } else {
  429. requestBody = interfaceRunData.req_body_other;
  430. }
  431. requestOptions.data = requestBody;
  432. if (interfaceRunData.req_body_type === 'form') {
  433. requestOptions.files = paramsToObjectWithEnable(
  434. safeArray(interfaceRunData.req_body_form).filter(item => {
  435. return item.type == 'file';
  436. })
  437. );
  438. } else if (interfaceRunData.req_body_type === 'file') {
  439. requestOptions.file = 'single-file';
  440. }
  441. }
  442. return requestOptions;
  443. }
  444. exports.checkRequestBodyIsRaw = checkRequestBodyIsRaw;
  445. exports.handleParams = handleParams;
  446. exports.handleContentType = handleContentType;
  447. exports.crossRequest = crossRequest;
  448. exports.handleCurrDomain = handleCurrDomain;
  449. exports.checkNameIsExistInArray = checkNameIsExistInArray;