├── test ├── .gitkeep └── lib │ ├── core │ ├── test.txt │ ├── mergeHeaders.spec.js │ └── output.spec.js │ └── utils │ └── mixin.spec.js ├── .gitignore ├── .travis.yml ├── index.js ├── .editorconfig ├── lib ├── utils │ ├── checkFunctionReturnFalse.js │ └── mixin.js ├── core │ ├── RequestElement.js │ ├── printError.js │ ├── printHead.js │ ├── request.js │ ├── mergeHeaders.js │ ├── work.js │ ├── filterQueue.js │ ├── outputResult.js │ └── printResult.js ├── const │ └── defaultHeaders.js ├── api │ ├── done.js │ └── push.js └── reqman.js ├── examples ├── getHelloworld.js ├── getGithub.js ├── getSimple.js ├── getOutput.js ├── getInvalid.js ├── post.js └── getValid.js ├── server └── server.js ├── LICENSE ├── package.json ├── Readme_cn.md └── README.md /test/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/lib/core/test.txt: -------------------------------------------------------------------------------- 1 | testtest -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .idea 4 | npm-debug.log 5 | package-lock.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | script: 5 | - npm run test -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Reqman 3 | * Copyright(c) 2018 Lisniuse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict'; 8 | 9 | module.exports = require('./lib/reqman'); -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | charset = utf-8 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /test/lib/core/mergeHeaders.spec.js: -------------------------------------------------------------------------------- 1 | const mergeHeaders = require('../../../lib/core/mergeHeaders') 2 | 3 | console.log(mergeHeaders({ 4 | A: 1 5 | },{ 6 | B: 2 7 | },{ 8 | c: 3 9 | })); -------------------------------------------------------------------------------- /test/lib/utils/mixin.spec.js: -------------------------------------------------------------------------------- 1 | const mixin = require('../../../lib/utils/mixin') 2 | 3 | class Test { 4 | constructor() { 5 | this.a = 1 6 | } 7 | } 8 | 9 | mixin(Test, "public", "b", function () { 10 | return this.a; 11 | }); 12 | let test = new Test(); 13 | console.log(test.b()); -------------------------------------------------------------------------------- /lib/utils/checkFunctionReturnFalse.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | /** 4 | * @description Check that a function returns false. 5 | * @return {Void} 6 | */ 7 | 8 | function checkFunctionReturn(fn) { 9 | return fn.toString().includes('return false'); 10 | } 11 | 12 | module.exports = checkFunctionReturn; 13 | -------------------------------------------------------------------------------- /test/lib/core/output.spec.js: -------------------------------------------------------------------------------- 1 | const outputResult = require('../../../lib/core/outputResult'); 2 | const RequestElement = require('../../../lib/core/RequestElement'); 3 | 4 | let requestElement = new RequestElement({ 5 | name: 'a', 6 | result: 'test' 7 | }) 8 | 9 | outputResult(requestElement, './test.txt') 10 | -------------------------------------------------------------------------------- /lib/core/RequestElement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the request element in the request chain. 3 | */ 4 | 5 | class RequestElement { 6 | constructor(options = {}) { 7 | this.name = options.name || ''; 8 | this.optionsFn = options.optionsFn || ''; 9 | this.options = options.options || {}; 10 | this.result = options.result || {}; 11 | this.output = ''; 12 | } 13 | } 14 | 15 | module.exports = RequestElement; 16 | -------------------------------------------------------------------------------- /lib/const/defaultHeaders.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description This is the default request header, and you can override it in api. 3 | */ 4 | 5 | const defaultHeaders = { 6 | 'Accept-Encoding': 'gzip, deflate, br', 7 | 'Cache-Control': 'max-age=0', 8 | 'Connection': 'keep-alive', 9 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36' 10 | } 11 | 12 | module.exports = defaultHeaders; 13 | -------------------------------------------------------------------------------- /examples/getHelloworld.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const server = require("../server/server"); 4 | const Reqman = require("../lib/reqman"); 5 | 6 | //run server on 3000. 7 | server("Hello World!"); 8 | 9 | //Just need to set up a basic domain 10 | const req = new Reqman({ 11 | baseUrl: "http://127.0.0.1:3000" 12 | }); 13 | 14 | req 15 | .push(function () { 16 | return { 17 | method: "GET", 18 | url: `/` 19 | } 20 | }) 21 | .done(function () { 22 | console.log("exit!"); 23 | process.exit(1); 24 | }) 25 | -------------------------------------------------------------------------------- /examples/getGithub.js: -------------------------------------------------------------------------------- 1 | //Importing Reqman, remember to capitalize the first letter because Reqman is a class. 2 | const Reqman = require('../lib/reqman'); 3 | 4 | //new Reqman,and then the parameter is passed in a base address. 5 | const req = new Reqman({ 6 | baseUrl: "https://github.com" 7 | }); 8 | 9 | //For example, you can use reqman to grab the github address of this project, like this: 10 | req 11 | .push(function () { 12 | return { 13 | method: "GET", 14 | url: `/lisniuse/reqman` 15 | } 16 | }) 17 | .done(); 18 | -------------------------------------------------------------------------------- /lib/core/printError.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | /** 4 | * @description A function that outputs the wrong log. 5 | * @param {Object} result http result 6 | * @return {Void} 7 | */ 8 | 9 | function printError(err, queueElement) { 10 | console.log(chalk.bold(`${chalk.green(">>>")} [${queueElement.name}] ${chalk.bgGreen(queueElement.options.method)}: ${queueElement.options.url}`)) 11 | console.log(chalk.bold(chalk.red('[REQMAN ERROR]: \n\n' + JSON.stringify(err, null, 2)))); 12 | console.log("\n"); 13 | } 14 | 15 | module.exports = printError; 16 | -------------------------------------------------------------------------------- /lib/core/printHead.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const WordTable = require('word-table'); 3 | 4 | /** 5 | * @description Print request time 6 | * @return {Void} 7 | */ 8 | 9 | function printHead(requestQueue) { 10 | let queusNames = requestQueue.map(i => i.name); 11 | let tableHeader = ['Request time', new Date().toLocaleString()]; 12 | let tableBody = [ 13 | ["Request chain", queusNames.join(' → ')] 14 | ]; 15 | let wt = new WordTable(tableHeader, tableBody); 16 | console.log(chalk.bold(chalk.cyan(wt.string()))); 17 | console.log("\n"); 18 | } 19 | 20 | module.exports = printHead; 21 | -------------------------------------------------------------------------------- /lib/core/request.js: -------------------------------------------------------------------------------- 1 | const fly = require('flyio'); 2 | 3 | /** 4 | * @description Used to send http requests. 5 | * @param {Object} options request options. 6 | * @return {Void} 7 | */ 8 | 9 | async function request(options = { 10 | headers: {}, 11 | method: 'GET', 12 | url: '/' 13 | }) { 14 | let method = options.method.toLowerCase(); 15 | let headers = this._mergeHeaders(this._defaultHeader, options.headers); 16 | let promiseResult = fly.request(options.url, options.data, { 17 | method: method, 18 | headers: headers 19 | }); 20 | return promiseResult; 21 | } 22 | 23 | module.exports = request; 24 | -------------------------------------------------------------------------------- /examples/getSimple.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const server = require("../server/server"); 4 | const Reqman = require("../lib/reqman"); 5 | 6 | //run server on 3000. 7 | server(); 8 | 9 | //Just need to set up a basic domain. 10 | const req = new Reqman({ 11 | baseUrl: "http://127.0.0.1:3000" 12 | }); 13 | 14 | req. 15 | push(function (prevElement) { 16 | return { 17 | method: "POST", 18 | url: `/?a=1` 19 | } 20 | }) 21 | .push('name b', function (prevElement) { 22 | return { 23 | method: "GET", 24 | url: `/`, 25 | data: { 26 | a: '2' 27 | } 28 | } 29 | }) 30 | .done(function () { 31 | process.exit(1); 32 | }) 33 | -------------------------------------------------------------------------------- /lib/utils/mixin.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | /** 4 | * @description Mix a method or parameter into an object. 5 | * @param {Function} callback callback 6 | * @return {Function} 7 | */ 8 | 9 | function mixin(targetClass, descriptor, propertyName, propertyValue) { 10 | Object.defineProperty(targetClass.prototype, propertyName, { 11 | enumerable: descriptor === 'public' ? true : false, 12 | configurable: true, 13 | writable: true, 14 | value: propertyValue 15 | }); 16 | return function (descriptor, propertyName, propertyValue) { 17 | return mixin(targetClass, descriptor, propertyName, propertyValue); 18 | }; 19 | } 20 | 21 | module.exports = mixin; 22 | -------------------------------------------------------------------------------- /examples/getOutput.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const server = require("../server/server"); 4 | const Reqman = require("../lib/reqman"); 5 | 6 | //run server on 3000. 7 | server(); 8 | 9 | //Just need to set up a basic domain. 10 | const req = new Reqman({ 11 | output: './result.txt', 12 | baseUrl: "http://127.0.0.1:3000" 13 | }); 14 | 15 | req. 16 | push(function (prevElement) { 17 | return { 18 | method: "POST", 19 | url: `/?a=1` 20 | } 21 | }) 22 | .push('name b', function (prevElement) { 23 | return { 24 | method: "GET", 25 | url: `/`, 26 | data: { 27 | a: '2' 28 | } 29 | } 30 | }) 31 | .done(function () { 32 | process.exit(1); 33 | }) 34 | -------------------------------------------------------------------------------- /lib/core/mergeHeaders.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | /** 4 | * @description Used to merge request header information. 5 | * @param {Array} args As an array of headers. 6 | * @return {Object} Return a new headers object. 7 | */ 8 | 9 | function mergeHeaders(...args) { 10 | let _args = []; 11 | args.forEach(headers => { 12 | if (_.isObject(headers)) { 13 | let _headers = {}; 14 | for (const key in headers) { 15 | if (headers.hasOwnProperty(key)) { 16 | const value = headers[key]; 17 | _headers[key.toLowerCase()] = value; 18 | } 19 | } 20 | _args.push(_headers); 21 | } 22 | }); 23 | return _.merge(..._args); 24 | } 25 | 26 | module.exports = mergeHeaders; 27 | -------------------------------------------------------------------------------- /examples/getInvalid.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const server = require("../server/server"); 4 | const Reqman = require("../lib/reqman"); 5 | 6 | //run server on 3000. 7 | server(); 8 | 9 | //Just need to set up a basic domain. 10 | const req = new Reqman({ 11 | baseUrl: "http://127.0.0.1:3000", 12 | specList: { 13 | type: 'invalid', //invalid or valid 14 | list: ['bob'] //Only jack exists. 15 | }, 16 | }); 17 | 18 | req. 19 | push('bob', function (prevElement) { 20 | return { 21 | method: "POST", 22 | url: `/?name=bob` 23 | } 24 | }) 25 | .push('jack', function (prevElement) { 26 | return { 27 | method: "GET", 28 | url: `/`, 29 | data: { 30 | name: 'jack' 31 | } 32 | } 33 | }) 34 | .done(function () { 35 | process.exit(1); 36 | }) 37 | -------------------------------------------------------------------------------- /examples/post.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const server = require("../server/server"); 4 | const Reqman = require("../lib/reqman"); 5 | 6 | //run server on 3000. 7 | server(); 8 | 9 | //Just need to set up a basic domain 10 | const req = new Reqman({ 11 | baseUrl: "http://127.0.0.1:3000" 12 | }); 13 | 14 | req 15 | .push(function () { 16 | return { 17 | method: "POST", 18 | headers: { 19 | "content-type": "application/json" 20 | }, 21 | url: `/`, 22 | data: { 23 | bar: 'foo' 24 | } 25 | } 26 | }) 27 | .push(function () { 28 | return { 29 | method: "POST", 30 | url: `/`, 31 | data: { 32 | bar: 'foo' 33 | } 34 | } 35 | }) 36 | .done(function () { 37 | console.log("exit!"); 38 | process.exit(1); 39 | }) 40 | -------------------------------------------------------------------------------- /examples/getValid.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const server = require("../server/server"); 4 | const Reqman = require("../lib/reqman"); 5 | 6 | //run server on 3000. 7 | server(); 8 | 9 | //Just need to set up a basic domain. 10 | const req = new Reqman({ 11 | baseUrl: "http://127.0.0.1:3000", 12 | specList: { 13 | type: 'valid', //invalid or valid 14 | list: ['bob'] //Only bob exists. 15 | }, 16 | }); 17 | 18 | //Request chain. 19 | req. 20 | push('bob', function (prevElement) { 21 | return { 22 | method: "POST", 23 | url: `/?name=bob` 24 | } 25 | }) 26 | .push('jack', function (prevElement) { 27 | return { 28 | method: "GET", 29 | url: `/`, 30 | data: { 31 | name: 'jack' 32 | } 33 | } 34 | }) 35 | .done(function () { 36 | process.exit(1); 37 | }) 38 | -------------------------------------------------------------------------------- /lib/api/done.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description This is the last step in the callback function, which is used to do some closing work. 3 | * @param {Function} callback callback 4 | * @return {Void} 5 | */ 6 | 7 | async function done(callback) { 8 | let requestQueue = this._filterQueue(); 9 | let len = requestQueue.length; 10 | this._printHead(requestQueue); 11 | for (let index = 0; index < len; index++) { 12 | let item = requestQueue[index]; 13 | const next = async () => { 14 | let prev = len > 1 && index > 0 ? requestQueue[index - 1] : null; 15 | item.options = item.optionsFn.call(this, prev); 16 | await this._work(item); 17 | } 18 | await next(); 19 | } 20 | if (callback && callback.constructor === Function) { 21 | callback.call(this); 22 | } else { 23 | process.exit(1); 24 | } 25 | } 26 | 27 | module.exports = done; 28 | -------------------------------------------------------------------------------- /lib/api/push.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const checkFunctionReturnFalse = require('../utils/checkFunctionReturnFalse'); 3 | const RequestElement = require('../core/RequestElement'); 4 | 5 | /** 6 | * @description Insert request into request chain 7 | * @param {Function} options request options 8 | * @return {this} 9 | */ 10 | 11 | function push(requestName, optionsFn = false) { 12 | let name = requestName; 13 | if (_.isFunction(name)) { 14 | optionsFn = requestName; 15 | name = this.requestQueue.length.toString(); 16 | } 17 | if (this.hasOnly) { 18 | return this; 19 | } 20 | if (!_.isFunction(optionsFn)) { 21 | return this; 22 | } 23 | if (checkFunctionReturnFalse(optionsFn)) { 24 | return this; 25 | } 26 | this.requestQueue.push(new RequestElement({ 27 | name: name, 28 | optionsFn: optionsFn 29 | })); 30 | return this; 31 | } 32 | 33 | module.exports = push; 34 | -------------------------------------------------------------------------------- /lib/core/work.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const chalk = require('chalk'); 3 | 4 | /** 5 | * @description Element-by-element request processing. 6 | * @param {Object} queueElement Queue element. 7 | * @return {Void} 8 | */ 9 | 10 | async function work(queueElement) { 11 | let options = queueElement.options; 12 | let baseUrl = options.baseUrl || this.options.baseUrl; 13 | let outputPath = options.output || this.options.output; 14 | options.url = baseUrl + options.url; 15 | let result = ""; 16 | try { 17 | result = await this._request(options); 18 | } catch (err) { 19 | this._printError(err, queueElement); 20 | process.exit(1); 21 | } 22 | queueElement.result = result; 23 | this._printResult(queueElement); 24 | if (outputPath) { 25 | this._outputResult(queueElement, outputPath); 26 | } 27 | if (_.isFunction(options.complete)) { 28 | options.complete.call(this, queueElement); 29 | } 30 | } 31 | 32 | module.exports = work; 33 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Koa = require('koa'); 4 | const app = new Koa(); 5 | 6 | module.exports = async function (resStr) { 7 | app.use(async ctx => { 8 | if (resStr) { 9 | ctx.body = resStr; 10 | return; 11 | } 12 | async function getBody() { 13 | return new Promise(function (reslove, reject) { 14 | let postdata = ""; 15 | ctx.req.addListener('data', (data) => { 16 | postdata += data; 17 | }) 18 | ctx.req.addListener("end", function () { 19 | reslove(postdata); 20 | }); 21 | }); 22 | } 23 | let bodyData = await getBody(); 24 | bodyData = bodyData !== "" ? bodyData : 'no body'; 25 | let response = ` 26 | Log for koa services:\n 27 | [header]: \n ${JSON.stringify(ctx.header)}\n 28 | [query data]: \n ${JSON.stringify(ctx.query)}\n 29 | [body data]: \n ${bodyData} 30 | `; 31 | ctx.body = response; 32 | }); 33 | app.listen(3000); 34 | } 35 | -------------------------------------------------------------------------------- /lib/core/filterQueue.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | /** 4 | * @description Function is to filter out the queue. 5 | * @return {Array} queue array. 6 | */ 7 | 8 | function filterQueue() { 9 | let newQueue = []; 10 | let requestQueue = this.requestQueue; 11 | let specList = this.options.specList; 12 | let hasSpecList = _.isObject(specList) && specList.list.length > 0; 13 | let len = requestQueue.length; 14 | 15 | for (let index = 0; index < len; index++) { 16 | let item = requestQueue[index]; 17 | const push = () => { 18 | newQueue.push(item); 19 | } 20 | if (hasSpecList) { 21 | if (specList.type === 'invalid' || !specList.type) { 22 | if (!_.includes(specList.list, item.name)) { 23 | push(); 24 | } 25 | } else if (specList.type === 'valid') { 26 | if (_.includes(specList.list, item.name)) { 27 | push(); 28 | } 29 | } 30 | } else { 31 | push(); 32 | } 33 | } 34 | 35 | return newQueue; 36 | } 37 | 38 | module.exports = filterQueue; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present ZhiBing 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lib/core/outputResult.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const WordTable = require('word-table'); 3 | 4 | /** 5 | * @description Output content to a file 6 | * @param {QueueElement} QueueElement QueueElement object 7 | * @param {String} filePath file output path 8 | * @return {Void} 9 | */ 10 | 11 | function outputResult(queueElement, filePath) { 12 | let request = queueElement.result.request; 13 | let response = queueElement.result.response; 14 | let options = queueElement.options; 15 | let content = ""; 16 | let tableHeader = ['Name', 'Method', 'Url', 'Data']; 17 | let tableBody = [ 18 | [queueElement.name, request.method, request.url, JSON.stringify(options.data) || 'None'] 19 | ]; 20 | // basic usage 21 | let wt = new WordTable(tableHeader, tableBody); 22 | content += wt.string(); 23 | content += '\n[*response body*] \n'; 24 | let res = typeof response.body === "object" ? JSON.stringify(response.body) : response.body; 25 | res = res.replace(/(^\s*)|(\s*$)/g, ""); 26 | if (res[0] === "{") { 27 | res = JSON.parse(res); 28 | content += JSON.stringify(res, null, 2); 29 | } else { 30 | content += res; 31 | } 32 | content += "\n"; 33 | fs.appendFileSync(filePath, content, 'utf-8'); 34 | } 35 | 36 | module.exports = outputResult; 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reqman", 3 | "description": "Reqman is a tool that can quickly help back-end engineers with api testing, as well as a nodejs-based crawler tool.", 4 | "version": "0.9.3", 5 | "author": "ZhiBing <17560235@qq.com>", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/lisniuse/reqman.git" 10 | }, 11 | "homepage": "https://github.com/lisniuse/reqman", 12 | "keywords": [ 13 | "test", 14 | "apitest", 15 | "request", 16 | "web", 17 | "restful", 18 | "api", 19 | "backend" 20 | ], 21 | "dependencies": { 22 | "chalk": "^2.4.1", 23 | "flyio": "^0.6.13", 24 | "lodash": "^4.17.10", 25 | "opencollective-postinstall": "^2.0.2", 26 | "word-table": "^1.0.3" 27 | }, 28 | "devDependencies": { 29 | "eslint": "^5.2.0", 30 | "koa": "^2.5.2" 31 | }, 32 | "engines": { 33 | "node": ">= 8.0.0" 34 | }, 35 | "files": [ 36 | "LICENSE", 37 | "README.md", 38 | "Readme_cn.md", 39 | "index.js", 40 | "lib/" 41 | ], 42 | "scripts": { 43 | "test": "", 44 | "postinstall": "opencollective-postinstall || true" 45 | }, 46 | "collective": { 47 | "type": "opencollective", 48 | "url": "https://opencollective.com/reqman" 49 | } 50 | } -------------------------------------------------------------------------------- /lib/reqman.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mixin = require('./utils/mixin'); 4 | const apiPush = require('./api/push'); 5 | const apiDone = require('./api/done'); 6 | const corePrintError = require('./core/printError'); 7 | const coreOutputResult = require('./core/outputResult'); 8 | const corePrintResult = require('./core/printResult'); 9 | const corePrintHead = require('./core/printHead'); 10 | const coreFilterQueue = require('./core/filterQueue'); 11 | const coreRequest = require('./core/request'); 12 | const coreWork = require('./core/work'); 13 | const coreMergeHeaders = require('./core/mergeHeaders'); 14 | const constDefaultHeader = require('./const/defaultHeaders'); 15 | 16 | /** 17 | * Main class 18 | */ 19 | class Reqman { 20 | 21 | /** 22 | * @description Necessary parameters, usually only need one basic domain 23 | * @param {Object} options options 24 | */ 25 | 26 | constructor(options = { 27 | baseUrl: '', //Basic request address. 28 | output: '', //Outputs all results to a file with the specified path. 29 | specList: false, //Special list. 30 | }) { 31 | //Initialization parameter. 32 | this.options = options; 33 | this.store = {}; 34 | this.requestQueue = []; 35 | this._defaultHeader = constDefaultHeader; 36 | } 37 | 38 | } 39 | 40 | mixin(Reqman, "public", "push", apiPush) 41 | ("public", "done", apiDone) 42 | ("private", "_work", coreWork) 43 | ("private", "_printError", corePrintError) 44 | ("private", "_outputResult", coreOutputResult) 45 | ("private", "_printResult", corePrintResult) 46 | ("private", "_printHead", corePrintHead) 47 | ("private", "_mergeHeaders", coreMergeHeaders) 48 | ("private", "_filterQueue", coreFilterQueue) 49 | ("private", "_request", coreRequest); 50 | 51 | module.exports = Reqman; 52 | -------------------------------------------------------------------------------- /lib/core/printResult.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const WordTable = require('word-table'); 3 | const _ = require('lodash'); 4 | 5 | /** 6 | * @description Add a line number to the text 7 | * @param {String} str Source string 8 | * @return {String} String with line number 9 | */ 10 | 11 | function addLineNumber(str) { 12 | 13 | function prefixInteger(num, n) { 14 | let lineNumber = (Array(n).join(0) + num).slice(-n); 15 | return chalk.white(lineNumber + ' | '); 16 | } 17 | 18 | let strArr = str.split("\n"); 19 | let newStr = ""; 20 | let zeroLen = strArr.length.toString().split('').length; 21 | strArr.forEach((s, index) => { 22 | newStr += `${prefixInteger(index, zeroLen)}${s} \n`; 23 | }); 24 | return newStr; 25 | } 26 | 27 | /** 28 | * @description Log output function. 29 | * @param {Object} result http result 30 | * @return {Void} 31 | */ 32 | 33 | function printResult(queueElement) { 34 | let request = queueElement.result.request; 35 | let response = queueElement.result.response; 36 | let options = queueElement.options; 37 | let tableHeader = ['Name', 'Method', 'Url', 'Data']; 38 | let tableBody = [ 39 | [queueElement.name, request.method, request.url, JSON.stringify(options.data) || 'None'] 40 | ]; 41 | let wt = new WordTable(tableHeader, tableBody); 42 | console.log(chalk.green(wt.string())); 43 | console.log(chalk.bold(chalk.yellow('[ response body ] \n'))); 44 | if ( options.showInfo === false ) { 45 | console.log(chalk.gray('[hidden]')); 46 | } else { 47 | let res = typeof response.body === "object" ? JSON.stringify(response.body) : response.body; 48 | res = res.replace(/(^\s*)|(\s*$)/g, ""); 49 | if (res[0] === "{") { 50 | res = JSON.parse(res); 51 | console.log(chalk.gray(addLineNumber(JSON.stringify(res, null, 2)))); 52 | } else { 53 | console.log(chalk.gray(addLineNumber(res))); 54 | } 55 | } 56 | console.log("\n"); 57 | } 58 | 59 | module.exports = printResult; 60 | -------------------------------------------------------------------------------- /Readme_cn.md: -------------------------------------------------------------------------------- 1 | [English](./README.md) | [简体中文](./Readme_cn.md) 2 | 3 | # Reqman 4 | 5 | Reqman是一个可以快速帮助后端工程师进行api测试的工具,同时也是一个基于nodejs的爬虫工具。 6 | 7 | [![NPM version](https://badge.fury.io/js/reqman.svg)](http://badge.fury.io/js/reqman) 8 | 9 | [![npm](https://nodei.co/npm/reqman.png)](https://www.npmjs.com/package/reqman) 10 | 11 | ## 安装 12 | 13 | 这是一个通过 [npm registry](https://www.npmjs.com/) 提供的 [Node.js](https://nodejs.org/en/) 模块。 14 | 15 | 在安装之前,下载并安装Node.js。需要[Node.js 8.0](https://nodejs.org/en/download/)或更高版本。 16 | 17 | 使用[`npm install`](https://docs.npmjs.com/getting-started/installing-npm-packages-locally)命令完成安装: 18 | 19 | ```bash 20 | $ npm install reqman 21 | ``` 22 | 23 | ## 引入使用 24 | 25 | ```javascript 26 | // 使用的是 Node.js `require()` 27 | const Reqman = require('reqman'); 28 | 29 | // Using ES6 imports 30 | import Reqman from 'reqman'; 31 | ``` 32 | 33 | ## 特性 34 | 35 | * ✔︎ 链式API 36 | * ✔︎ 开箱即用 37 | * ✔︎ 可爬虫、可模拟请求 38 | * ✔︎ 适用于复杂强耦合场景 39 | * ✔︎ 基于nodejs强大请求库 [request](https://github.com/request/request) 40 | 41 | ## 超简单的入门教程 42 | 43 | Reqman被设计成像 [request](https://github.com/request/request) 一样,是进行http调用的最简单的方式。它支持https,默认情况下遵循重定向。 44 | 45 | 你只需要在返回一个``object`` 的 ``push`` 方法的参数中编写一个匿名函数,返回你的请求参数即可。 46 | 47 | ### 例子1:单个请求 48 | 49 | [点击这里查看这个例子源码](./examples/getGithub.js) 50 | 51 | ```javascript 52 | //引入Reqman,记得第一个字母要大写,因为是Reqman是一个类。 53 | const Reqman = require('reqman'); 54 | 55 | //new Reqman,然后参数传入一个基地址。 56 | const req = new Reqman({ 57 | baseUrl: "https://github.com" 58 | }); 59 | 60 | //举个例子,你可以用reqman抓取本项目的github地址,像这样: 61 | req 62 | .push(function () { 63 | return { 64 | method: "GET", 65 | url: `/lisniuse/reqman` 66 | } 67 | }) 68 | .done(); 69 | 70 | ``` 71 | 72 | 如果你指定showInfo参数为false,那么就不会打印结果到屏幕上,像这样: 73 | 74 | ```javascript 75 | req 76 | .push(function () { 77 | return { 78 | method: "GET", 79 | url: `/lisniuse/reqman`, 80 | showInfo: false 81 | } 82 | }) 83 | .done(); 84 | ``` 85 | 86 | ### 例子2: 链式API 87 | 88 | 链API中,第一个请求的结果作为第二个请求的参数。就像这样: 89 | 90 | ```javascript 91 | const Reqman = require('reqman'); 92 | 93 | //需要设置一个请求基地址 94 | const req = new Reqman({ 95 | baseUrl: "http://127.0.0.1:3000" 96 | }); 97 | 98 | //登陆的用户账号密码 99 | const user = { 100 | username: "admin", 101 | password: "admin" 102 | } 103 | 104 | req 105 | //请求登陆地址 106 | .push(function () { 107 | return { 108 | method: "POST", 109 | url: `/api/login`, 110 | data: user, //传入刚刚定义的user对象到data 111 | complete: function (selfElement) { //返回该队列的requestElement对象 112 | let response = selfElement.result.response; 113 | let body = JSON.parse(response.body); 114 | //注意:建议不要把公共变量直接挂到reqman的实例上,可能会覆盖requman的属性和方法,reqman提供了一个store对象用于存储请求过程中需要存储的公共变量。 115 | this.store.userToken = body.data.token; //拿到用户token 116 | } 117 | } 118 | }) 119 | //然后我们以登陆之后的获取到的token来更新用户的信息。 120 | .push(function () { 121 | return { 122 | method: "POST", 123 | url: `/api/user/updateInfo`, 124 | headers: { 125 | 'Authorization': this.store.userToken //之后的请求中可以直接使用store里的变量 126 | }, 127 | data: { 128 | nickname: "jack ma" //更新昵称为 jack ma 129 | } 130 | } 131 | }) 132 | .done()//just do it. 133 | 134 | ``` 135 | 136 | ### 例字3:完整体现reqman的api和特性的例子 137 | 138 | ```javascript 139 | 'use strict' 140 | 141 | const req = new Reqman({ 142 | baseUrl: "http://127.0.0.1:3000", 143 | output: "./request-result.txt", //将请求后返回的结果同时追加写入到指定的文件路径 144 | specList: { 145 | type: 'valid', //参数:invalid 或 valid。定义有效或者无效的请求。 146 | list: ['bob'] //让名为bob的请求有效,其余的请求失效。如果type为invalid,则相反。 147 | } 148 | }); 149 | 150 | //请求链 151 | req. 152 | //定义一个名为bob的请求,prevElement参数表示上一个请求的requestElement对象。 153 | push('bob', function (prevElement) { 154 | return { 155 | method: "POST", 156 | url: `/?name=bob`, 157 | headers: { //附带自定义headers 158 | 'content-type': 'application/octet-stream' 159 | }, 160 | complete: function (selfElement) { //请求完毕后的回调函数 161 | 162 | } 163 | } 164 | }) 165 | //定义一个名为jack的请求,prevElement参数表示上一个请求的requestElement对象。 166 | .push('jack', function (prevElement) { 167 | return { 168 | baseUrl: 'http://127.0.0.1:4000', //为该请求自定义基地址 169 | output: "./jack-result.txt", //为该请求自定义输出文件路径 170 | method: "GET", 171 | url: `/`, 172 | data: { 173 | name: 'jack' 174 | }, 175 | showInfo: false, //不将返回的body信息打印出来 176 | complete: function (selfElement) { //请求完毕后的回调函数 177 | //do something... 178 | } 179 | } 180 | }) 181 | .done(function () { 182 | //退出程序 183 | process.exit(1); 184 | }) 185 | 186 | ``` 187 | 188 | ### 更多例子 189 | 190 | 更多例子在项目的 [examples](./examples) 文件夹中,你可以直接运行: 191 | 192 | ```bash 193 | node getHelloworld.js 194 | ``` 195 | 196 | ## License 197 | 198 | The MIT License (MIT) 199 | 200 | Copyright (c) 2015-present ZhiBing \<17560235@qq.com> 201 | 202 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 203 | 204 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 205 | 206 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 207 | 208 | [back to top](#reqman) 209 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English](./README.md) | [简体中文](./Readme_cn.md) 2 | 3 | # Reqman 4 | 5 | Reqman is a tool that can quickly help back-end engineers with api testing, as well as a nodejs-based crawler tool. 6 | 7 | [![Financial Contributors on Open Collective](https://opencollective.com/reqman/all/badge.svg?label=financial+contributors)](https://opencollective.com/reqman) [![NPM version](https://badge.fury.io/js/reqman.svg)](http://badge.fury.io/js/reqman) 8 | 9 | [![npm](https://nodei.co/npm/reqman.png)](https://www.npmjs.com/package/reqman) 10 | 11 | ## Installation 12 | 13 | This is a [Node.js](https://nodejs.org/en/) module available through the 14 | [npm registry](https://www.npmjs.com/). 15 | 16 | Before installing, [download and install Node.js](https://nodejs.org/en/download/). 17 | Node.js 8.0 or higher is required. 18 | 19 | Installation is done using the 20 | [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): 21 | 22 | ```bash 23 | $ npm install reqman 24 | ``` 25 | 26 | ## Importing 27 | 28 | ```javascript 29 | // Using Node.js `require()` 30 | const Reqman = require('reqman'); 31 | 32 | // Using ES6 imports 33 | import Reqman from 'reqman'; 34 | ``` 35 | 36 | ## Features 37 | 38 | * ✔︎ Chained API 39 | * ✔︎ Out of the box 40 | * ✔︎ Crawler, can simulate requests 41 | * ✔︎ Suitable for complex and strong coupling scenarios 42 | * ✔︎ Powerful request library [request](https://github.com/request/request) based on nodejs 43 | 44 | ## Super simple to use 45 | 46 | Reqman is designed to be like [request](https://github.com/request/request) and is the easiest way to make http calls. It supports https, to follow redirection by default. 47 | 48 | All you have to do is write an anonymous function in the parameters of the ``push`` method that returns a ``object`` and return your request parameters. 49 | 50 | ### Example 1: a single request 51 | 52 | [Click here to view this example source code](./examples/getGithub.js) 53 | 54 | ```javascript 55 | //Importing Reqman, remember to capitalize the first letter because Reqman is a class. 56 | const Reqman = require('reqman'); 57 | 58 | //new Reqman,and then the parameter is passed in a base address. 59 | const req = new Reqman({ 60 | baseUrl: "https://github.com" 61 | }); 62 | 63 | //For example, you can use reqman to grab the github address of this project, like this: 64 | req 65 | .push(function () { 66 | return { 67 | method: "GET", 68 | url: `/lisniuse/reqman` 69 | } 70 | }) 71 | .done(); 72 | 73 | ``` 74 | 75 | If you specify the showInfo parameter as false, the results will not be printed to the screen, like this: 76 | 77 | ```javascript 78 | req 79 | .push(function () { 80 | return { 81 | method: "GET", 82 | url: `/lisniuse/reqman`, 83 | showInfo: false 84 | } 85 | }) 86 | .done(); 87 | 88 | ``` 89 | 90 | ### Example 2: Chained API 91 | 92 | In the chained API, the result of the first request is used as the argument to the second request. like this: 93 | 94 | ```javascript 95 | const Reqman = require('reqman'); 96 | 97 | const req = new Reqman({ 98 | baseUrl: "http://127.0.0.1:3000" 99 | }); 100 | 101 | //Define account password 102 | const user = { 103 | username: "admin", 104 | password: "admin" 105 | } 106 | 107 | req 108 | //Request a login api 109 | .push(function () { 110 | return { 111 | method: "POST", 112 | url: `/api/login`, 113 | data: user, //Pass in the user object just defined to data 114 | complete: function (selfElement) { //Return the requestElement object of the queue 115 | let response = selfElement.result.response; 116 | let body = JSON.parse(response.body); 117 | //Note: It is recommended not to hang public variables directly on the reqman instance, which may override the requman properties and methods. Reqman provides a store object to store the public variables that need to be stored during the request process. 118 | this.store.userToken = body.data.token; //Get the user token 119 | } 120 | } 121 | }) 122 | //Then we update the user's information with the token obtained after login. 123 | .push(function () { 124 | return { 125 | method: "POST", 126 | url: `/api/user/updateInfo`, 127 | headers: { 128 | 'Authorization': this.store.userToken //The variables in the store can be directly used in subsequent requests. 129 | }, 130 | data: { 131 | nickname: "jack ma" //Update nickname jack ma 132 | } 133 | } 134 | }) 135 | .done()//just do it. 136 | 137 | ``` 138 | 139 | ### Example 3: Example of a complete representation of reqman's api and features 140 | 141 | ```javascript 142 | 'use strict' 143 | 144 | const req = new Reqman({ 145 | baseUrl: "http://127.0.0.1:3000", 146 | output: "./request-result.txt", //Append the result returned after the request to the specified file path at the same time. 147 | specList: { 148 | type: 'valid', //Parameters: invalid or valid. Define valid or invalid requests. 149 | list: ['bob'] //Let the request named bob be valid and the rest of the requests invalid. If type is invalid, the opposite is true. 150 | } 151 | }); 152 | 153 | req. 154 | //Define a request named bob, and the prevElement parameter represents the requestElement object of the previous request. 155 | push('bob', function (prevElement) { 156 | return { 157 | method: "POST", 158 | url: `/?name=bob`, 159 | headers: { //With custom headers 160 | 'content-type': 'application/octet-stream' 161 | }, 162 | complete: function (selfElement) { //Callback function after request 163 | 164 | } 165 | } 166 | }) 167 | //Define a request named jack, and the prevElement parameter represents the requestElement object of the previous request. 168 | .push('jack', function (prevElement) { 169 | return { 170 | baseUrl: 'http://127.0.0.1:4000', //Customize the base address for this request 171 | output: "./jack-result.txt", //Customize the output file path for this request 172 | method: "GET", 173 | url: `/`, 174 | data: { 175 | name: 'jack' 176 | }, 177 | showInfo: false, //Do not print the returned body information 178 | complete: function (selfElement) { //Callback function after request 179 | //do something... 180 | } 181 | } 182 | }) 183 | .done(function () { 184 | //exit the program 185 | process.exit(1); 186 | }) 187 | 188 | ``` 189 | 190 | ### More examples 191 | 192 | More [examples](./examples) in the projects folder of the project, you can run this command directly: 193 | 194 | ```bash 195 | node getHelloworld.js 196 | ``` 197 | 198 | ## Contributors 199 | 200 | ### Code Contributors 201 | 202 | This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. 203 | 204 | 205 | ### Financial Contributors 206 | 207 | Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/reqman/contribute)] 208 | 209 | #### Individuals 210 | 211 | 212 | 213 | #### Organizations 214 | 215 | Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/reqman/contribute)] 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | ## License 229 | 230 | The MIT License (MIT) 231 | 232 | Copyright (c) 2015-present ZhiBing \<17560235@qq.com> 233 | 234 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 235 | 236 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 237 | 238 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 239 | 240 | [back to top](#reqman) 241 | --------------------------------------------------------------------------------