mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-04-05 11:18:54 +08:00
feat: 新增mock插件
This commit is contained in:
parent
c29f705bd8
commit
5d2271bcd3
@ -41,6 +41,9 @@
|
||||
"vue-loader": "^16.1.2",
|
||||
"webpack-bundle-analyzer": "4.3.0",
|
||||
"cli-highlight": "^2.1.4",
|
||||
"webpack-chain": "6.5.1"
|
||||
"webpack-chain": "6.5.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"mockjs": "^1.1.0"
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,10 @@ export default function () {
|
||||
require.resolve('./plugins/commands/dev'),
|
||||
require.resolve('./plugins/commands/help'),
|
||||
require.resolve('./plugins/commands/info'),
|
||||
require.resolve('./plugins/commands/webpack')
|
||||
require.resolve('./plugins/commands/webpack'),
|
||||
|
||||
// mock
|
||||
require.resolve('./plugins/commands/mock')
|
||||
]
|
||||
};
|
||||
}
|
||||
|
174
packages/fes-preset-built-in/src/plugins/commands/mock/index.js
Normal file
174
packages/fes-preset-built-in/src/plugins/commands/mock/index.js
Normal file
@ -0,0 +1,174 @@
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { chokidar, lodash } from '@umijs/utils';
|
||||
import bodyParser from 'body-parser';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import Mock from 'mockjs';
|
||||
|
||||
|
||||
export default (api) => {
|
||||
let mockFlag = false; // mock 开关flag
|
||||
let mockPrefix = '/'; // mock 过滤前缀
|
||||
let mockFile = ''; // mock 文件
|
||||
let loadMock = ''; // mock 对象
|
||||
|
||||
api.describe({
|
||||
key: 'mock',
|
||||
config: {
|
||||
schema(joi) {
|
||||
return joi.alternatives(joi.boolean(), joi.object());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const createMock = () => {
|
||||
// 判断是否为 Object,仅 {}
|
||||
function isObject(value) {
|
||||
return Object.prototype.toString.call(value) === '[object Object]';
|
||||
}
|
||||
// 对 array、object 遍历处理
|
||||
function traversalHandler(val, callback) {
|
||||
if (lodash.isArray(val)) {
|
||||
val.forEach(callback);
|
||||
}
|
||||
if (isObject(val)) {
|
||||
Object.keys(val).forEach((key) => { callback(val[key], key); });
|
||||
}
|
||||
}
|
||||
// 根据参数个数获取配置
|
||||
function getOption(arg) {
|
||||
const len = arg.length;
|
||||
// 默认配置
|
||||
const option = {
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache'
|
||||
},
|
||||
statusCode: 200,
|
||||
cookies: [],
|
||||
timeout: 0
|
||||
};
|
||||
if (len === 0) return option;
|
||||
if (len === 1) {
|
||||
const newOption = arg[0];
|
||||
if (isObject(newOption)) {
|
||||
traversalHandler(newOption, (value, key) => {
|
||||
if (key === 'headers') {
|
||||
traversalHandler(newOption.headers, (headervalue, headerkey) => {
|
||||
option.headers[headerkey] = newOption.headers[headerkey];
|
||||
});
|
||||
} else {
|
||||
option[key] = newOption[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
option.url = arg[0];
|
||||
option.result = arg[1];
|
||||
}
|
||||
return option;
|
||||
}
|
||||
// 把基于 cgiMockfile 的相对绝对转成绝对路径
|
||||
function parsePath(value) {
|
||||
const PROJECT_DIR = process.env.PWD || process.cwd();
|
||||
return resolve(PROJECT_DIR, value);
|
||||
}
|
||||
|
||||
const requestList = [];
|
||||
const cgiMock = (...arg) => {
|
||||
const option = getOption(arg);
|
||||
if (!option.url || !option.result) return;
|
||||
requestList.push(option);
|
||||
};
|
||||
cgiMock.file = function (file) {
|
||||
return readFileSync(parsePath(file));
|
||||
};
|
||||
|
||||
// mock是否打开
|
||||
mockFlag = isObject(api.config.mock) ? true : api.config.mock;
|
||||
if (!mockFlag) return;
|
||||
// mock打开情况下,配置的过滤前缀
|
||||
mockPrefix = api.config.mock.prefix || mockPrefix;
|
||||
// mock文件处理
|
||||
mockFile = parsePath('./mock.js');
|
||||
if (!existsSync(mockFile)) {
|
||||
api.logger.info('mock.js File does not exist, please check'); return;
|
||||
}
|
||||
// 清除require的缓存,保证 mock 文件修改后拿到最新的 mock.js
|
||||
if (require.cache[mockFile]) {
|
||||
delete require.cache[mockFile];
|
||||
}
|
||||
const projectMock = require(mockFile);
|
||||
if (!lodash.isFunction(projectMock)) {
|
||||
api.logger.info('mock.js should export Function'); return;
|
||||
}
|
||||
// mock对象与 mock.js 结合
|
||||
projectMock(cgiMock, Mock);
|
||||
|
||||
return (req, res, next) => {
|
||||
// 如果请求不是以 cgiMock.prefix 开头,直接 next `${mockPrefix}/`
|
||||
if (!req.path.startsWith(`${mockPrefix}/`)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// 请求以 cgiMock.prefix 开头,匹配处理
|
||||
const matchRequet = requestList.filter(item => req.path.search(item.url) !== -1)[0];
|
||||
if (!matchRequet) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// set header
|
||||
res.set(matchRequet.headers);
|
||||
// set Content-Type
|
||||
matchRequet.type && res.type(matchRequet.type);
|
||||
// set status code
|
||||
res.status(matchRequet.statusCode);
|
||||
// set cookie
|
||||
traversalHandler(matchRequet.cookies, (item) => {
|
||||
const name = item.name;
|
||||
const value = item.value;
|
||||
delete item.name;
|
||||
delete item.value;
|
||||
res.cookie(name, value, item);
|
||||
});
|
||||
|
||||
// do result
|
||||
if (lodash.isFunction(matchRequet.result)) {
|
||||
matchRequet.result(req, res);
|
||||
} else if (
|
||||
lodash.isArray(matchRequet.result) || isObject(matchRequet.result)
|
||||
) {
|
||||
!matchRequet.type && res.type('json');
|
||||
res.json(matchRequet.result);
|
||||
} else {
|
||||
!matchRequet.type && res.type('text');
|
||||
res.send(matchRequet.result.toString());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
api.onStart(() => {
|
||||
loadMock = createMock();
|
||||
if (!mockFlag) return;
|
||||
|
||||
chokidar.watch(mockFile, {
|
||||
ignoreInitial: true
|
||||
}).on('change', () => {
|
||||
api.logger.info('mock.js changed,reload');
|
||||
loadMock = createMock();
|
||||
});
|
||||
});
|
||||
api.addBeforeMiddlewares(() => {
|
||||
if (!mockFlag) return [];
|
||||
return [
|
||||
bodyParser.json(),
|
||||
bodyParser.urlencoded({
|
||||
extended: false
|
||||
}),
|
||||
cookieParser()
|
||||
];
|
||||
});
|
||||
api.addBeforeMiddlewares(() => (req, res, next) => {
|
||||
if (!mockFlag) return next();
|
||||
loadMock(req, res, next);
|
||||
});
|
||||
};
|
@ -12,6 +12,15 @@ export default {
|
||||
admin: ["/", "/onepiece"]
|
||||
}
|
||||
},
|
||||
mock: {
|
||||
prefix: '/v2'
|
||||
},
|
||||
proxy: {
|
||||
'/v2': {
|
||||
'target': 'https://api.douban.com/',
|
||||
'changeOrigin': true,
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
title: "Fes.js",
|
||||
footer: 'Created by MumbelFe',
|
||||
|
130
packages/fes-template/mock.js
Normal file
130
packages/fes-template/mock.js
Normal file
@ -0,0 +1,130 @@
|
||||
module.exports = (cgiMock, Mock) => {
|
||||
const { Random } = Mock;
|
||||
|
||||
// 测试 proxy 与 mock 用例集合
|
||||
cgiMock('/movie/in_theaters_mock', (req, res) => {
|
||||
res.send(JSON.stringify({
|
||||
code: '0',
|
||||
msg: '',
|
||||
result: {
|
||||
text: 'movie: movie/in_theaters_mock ~~~~~'
|
||||
}
|
||||
}));
|
||||
});
|
||||
cgiMock('/movie/test_mock', (req, res) => {
|
||||
res.send(JSON.stringify({
|
||||
code: '0',
|
||||
msg: '',
|
||||
result: {
|
||||
text: 'mock: movie/test_mock'
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
// 测试用例: mock.js change,重现请求,需要能拉最新的数据
|
||||
cgiMock('/watchtest', (req, res) => {
|
||||
res.send(JSON.stringify({
|
||||
code: '0',
|
||||
msg: '',
|
||||
result: {
|
||||
text: '通过 register 测试 mock watch: 初始状态'
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
// 返回一个数字
|
||||
// cgiMock('/number', 666);
|
||||
cgiMock('/number', 999);
|
||||
|
||||
// 返回一个json
|
||||
cgiMock({
|
||||
url: '/json',
|
||||
result: {
|
||||
code: '400101', msg: "不合法的请求:Missing cookie 'wb_app_id' for method parameter of type String", transactionTime: '20170309171146', success: false
|
||||
}
|
||||
});
|
||||
|
||||
// 利用 mock.js 产生随机文本
|
||||
cgiMock('/text', Random.cparagraph());
|
||||
|
||||
// 返回一个字符串 利用 mock.js 产生随机字符
|
||||
cgiMock('/random', Mock.mock({
|
||||
'string|1-10': '★'
|
||||
}));
|
||||
|
||||
// 正则匹配url, 返回一个字符串
|
||||
cgiMock(/\/abc|\/xyz/, 'regexp test!');
|
||||
|
||||
// option.result 参数如果是一个函数, 可以实现自定义返回内容, 接收的参数是是经过 express 封装的 req 和 res 对象.
|
||||
cgiMock(/\/function$/, (req, res) => {
|
||||
res.send('function test');
|
||||
});
|
||||
|
||||
// 返回文本 readFileSync
|
||||
cgiMock('/file', cgiMock.file('./package.json'));
|
||||
|
||||
// 更复杂的规则配置
|
||||
cgiMock({
|
||||
url: /\/who/,
|
||||
method: 'GET',
|
||||
result(req, res) {
|
||||
if (req.query.name === 'kwan') {
|
||||
res.json({ kwan: '孤独患者' });
|
||||
} else {
|
||||
res.send('Nooooooooooo');
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
'Content-Length': '123',
|
||||
ETag: '12345'
|
||||
},
|
||||
cookies: [
|
||||
{
|
||||
name: 'myname', value: 'kwan', maxAge: 900000, httpOnly: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 携带参数的请求
|
||||
cgiMock('/v2/audit/list', (req, res) => {
|
||||
const {
|
||||
currentPage, pageSize, isAudited
|
||||
} = req.body;
|
||||
res.send({
|
||||
code: '0',
|
||||
msg: '',
|
||||
data: {
|
||||
currentPage,
|
||||
pageSize,
|
||||
totalPage: 2,
|
||||
totalCount: 12,
|
||||
pageData: Array.from({ length: pageSize }, () => ({
|
||||
title: Random.title(),
|
||||
authorName: Random.cname(),
|
||||
authorId: Random.name(),
|
||||
createTime: Date.now(),
|
||||
updateTime: Date.now(),
|
||||
readCount: Random.integer(60, 1000),
|
||||
favoriteCount: Random.integer(1, 50),
|
||||
postId: '12323',
|
||||
serviceTag: '业务类型',
|
||||
productTag: '产品类型',
|
||||
requestTag: '需求类型',
|
||||
handleTag: '已采纳',
|
||||
postType: 'voice',
|
||||
postStatus: isAudited ? 'pass' : 'auditing',
|
||||
auditStatus: 'audit1'
|
||||
}))
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// multipart/form-data 类型
|
||||
cgiMock('/v2/upload', (req, res) => {
|
||||
res.send({
|
||||
code: '0',
|
||||
msg: '文件上传成功'
|
||||
});
|
||||
});
|
||||
};
|
@ -54,6 +54,7 @@
|
||||
"@webank/fes-plugin-enums": "^2.0.0-alpha.0",
|
||||
"@webank/fes-plugin-jest": "^2.0.0-alpha.0",
|
||||
"@webank/fes-plugin-vuex": "^2.0.0-alpha.0",
|
||||
"@webank/fes-plugin-request": "2.0.0-alpha.1",
|
||||
"ant-design-vue": "2.0.0-rc.3",
|
||||
"vue": "3.0.5",
|
||||
"vuex": "^4.0.0-rc.2"
|
||||
|
@ -22,11 +22,9 @@
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
import {
|
||||
access, useAccess, useRouter, useI18n, locale, enums
|
||||
access, useAccess, useRouter, useI18n, locale, enums, request
|
||||
} from '@webank/fes';
|
||||
|
||||
console.log(__DEV__);
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const fes = ref('fes upgrade to vue3');
|
||||
@ -81,6 +79,25 @@ export default {
|
||||
accessId.value = '11';
|
||||
}, 4000);
|
||||
// router.push('/onepiece');
|
||||
|
||||
console.log('测试 mock!!');
|
||||
request('/v2/file').then((data) => {
|
||||
console.log(data);
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
request('/v2/movie/in_theaters_mock').then((data) => {
|
||||
console.log(data);
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
console.log('测试 proxy!!');
|
||||
request('/v2/movie/in_theaters_proxy').then((resp) => {
|
||||
console.log(resp);
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
});
|
||||
return {
|
||||
accessId,
|
||||
|
Loading…
x
Reference in New Issue
Block a user