mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 11:20:11 +00:00
test: add ut for render-core/utils/data-helper.ts
This commit is contained in:
parent
bf280c6fa1
commit
171b1013b2
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-len */
|
||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret';
|
import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret';
|
||||||
@ -154,8 +155,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
|
|||||||
this.__showPlaceholder = false;
|
this.__showPlaceholder = false;
|
||||||
return resolve({});
|
return resolve({});
|
||||||
}
|
}
|
||||||
this.__dataHelper
|
this.__dataHelper.getInitData()
|
||||||
.getInitData()
|
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
this.__showPlaceholder = false;
|
this.__showPlaceholder = false;
|
||||||
if (isEmpty(res)) {
|
if (isEmpty(res)) {
|
||||||
@ -286,8 +286,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
|
|||||||
// this.__showPlaceholder = false;
|
// this.__showPlaceholder = false;
|
||||||
return resolve({});
|
return resolve({});
|
||||||
}
|
}
|
||||||
this.__dataHelper
|
this.__dataHelper.getInitData()
|
||||||
.getInitData()
|
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
// this.__showPlaceholder = false;
|
// this.__showPlaceholder = false;
|
||||||
if (isEmpty(res)) {
|
if (isEmpty(res)) {
|
||||||
|
|||||||
@ -118,7 +118,7 @@ export function isJSSlot(obj: any): obj is JSSlot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compatible with the old protocol JSBlock
|
// Compatible with the old protocol JSBlock
|
||||||
return ([EXPRESSION_TYPE.JSSLOT, EXPRESSION_TYPE.JSBLOCK].includes(obj.type));
|
return [EXPRESSION_TYPE.JSSLOT, EXPRESSION_TYPE.JSBLOCK].includes(obj.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
/* eslint-disable max-len */
|
||||||
/* eslint-disable object-curly-newline */
|
/* eslint-disable object-curly-newline */
|
||||||
import { isJSFunction } from '@alilc/lowcode-types';
|
import { isJSFunction } from '@alilc/lowcode-types';
|
||||||
import { transformArrayToMap, transformStringToFunction, clone } from './common';
|
import { transformArrayToMap, transformStringToFunction } from './common';
|
||||||
import { jsonp, request, get, post } from './request';
|
import { jsonp, request, get, post } from './request';
|
||||||
import { DataSource, DataSourceItem } from '../types';
|
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
|
import { DataSource, DataSourceItem, IRendererAppHelper } from '../types';
|
||||||
|
|
||||||
const DS_STATUS = {
|
const DS_STATUS = {
|
||||||
INIT: 'init',
|
INIT: 'init',
|
||||||
@ -12,22 +14,77 @@ const DS_STATUS = {
|
|||||||
ERROR: 'error',
|
ERROR: 'error',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DataSourceType = 'fetch' | 'jsonp';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do request for standard DataSourceType
|
||||||
|
* @param {DataSourceType} type type of DataSourceItem
|
||||||
|
* @param {any} options
|
||||||
|
*/
|
||||||
|
export function doRequest(type: DataSourceType, options: any) {
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let { uri, url, method = 'GET', headers, params, ...otherProps } = options;
|
||||||
|
otherProps = otherProps || {};
|
||||||
|
if (type === 'jsonp') {
|
||||||
|
return jsonp(uri, params, otherProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'fetch') {
|
||||||
|
switch (method.toUpperCase()) {
|
||||||
|
case 'GET':
|
||||||
|
return get(uri, params, headers, otherProps);
|
||||||
|
case 'POST':
|
||||||
|
return post(uri, params, headers, otherProps);
|
||||||
|
default:
|
||||||
|
return request(uri, method, params, headers, otherProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log(`Engine default dataSource does not support type:[${type}] dataSource request!`, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: according to protocol, we should implement errorHandler/shouldFetch/willFetch/requestHandler and isSync controll.
|
||||||
export class DataHelper {
|
export class DataHelper {
|
||||||
|
/**
|
||||||
|
* host object that will be "this" object when excuting dataHandler
|
||||||
|
*
|
||||||
|
* @type {*}
|
||||||
|
* @memberof DataHelper
|
||||||
|
*/
|
||||||
host: any;
|
host: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* data source config
|
||||||
|
*
|
||||||
|
* @type {DataSource}
|
||||||
|
* @memberof DataHelper
|
||||||
|
*/
|
||||||
config: DataSource;
|
config: DataSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a parser function which will be called to process config data
|
||||||
|
* which eventually will call common/utils.processData() to process data
|
||||||
|
* (originalConfig) => parsedConfig
|
||||||
|
* @type {*}
|
||||||
|
* @memberof DataHelper
|
||||||
|
*/
|
||||||
parser: any;
|
parser: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* config.list
|
||||||
|
*
|
||||||
|
* @type {any[]}
|
||||||
|
* @memberof DataHelper
|
||||||
|
*/
|
||||||
ajaxList: any[];
|
ajaxList: any[];
|
||||||
|
|
||||||
ajaxMap: any;
|
ajaxMap: any;
|
||||||
|
|
||||||
dataSourceMap: any;
|
dataSourceMap: any;
|
||||||
|
|
||||||
appHelper: any;
|
appHelper: IRendererAppHelper;
|
||||||
|
|
||||||
constructor(comp: any, config: DataSource, appHelper: any, parser: any) {
|
constructor(comp: any, config: DataSource, appHelper: IRendererAppHelper, parser: any) {
|
||||||
this.host = comp;
|
this.host = comp;
|
||||||
this.config = config || {};
|
this.config = config || {};
|
||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
@ -37,15 +94,6 @@ export class DataHelper {
|
|||||||
this.appHelper = appHelper;
|
this.appHelper = appHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置config,dataSourceMap状态会被重置;
|
|
||||||
resetConfig(config = {}) {
|
|
||||||
this.config = config as DataSource;
|
|
||||||
this.ajaxList = (config as DataSource)?.list || [];
|
|
||||||
this.ajaxMap = transformArrayToMap(this.ajaxList, 'id');
|
|
||||||
this.dataSourceMap = this.generateDataSourceMap();
|
|
||||||
return this.dataSourceMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新config,只会更新配置,状态保存;
|
// 更新config,只会更新配置,状态保存;
|
||||||
updateConfig(config = {}) {
|
updateConfig(config = {}) {
|
||||||
this.config = config as DataSource;
|
this.config = config as DataSource;
|
||||||
@ -79,6 +127,7 @@ export class DataHelper {
|
|||||||
res[item.id] = {
|
res[item.id] = {
|
||||||
status: DS_STATUS.INIT,
|
status: DS_STATUS.INIT,
|
||||||
load: (...args: any) => {
|
load: (...args: any) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return this.getDataSource(item.id, ...args);
|
return this.getDataSource(item.id, ...args);
|
||||||
},
|
},
|
||||||
@ -93,41 +142,54 @@ export class DataHelper {
|
|||||||
this.dataSourceMap[id].status = error ? DS_STATUS.ERROR : DS_STATUS.LOADED;
|
this.dataSourceMap[id].status = error ? DS_STATUS.ERROR : DS_STATUS.LOADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
getInitData() {
|
/**
|
||||||
const initSyncData = this.parser(this.ajaxList).filter((item: DataSourceItem) => {
|
* get all dataSourceItems which marked as isInit === true
|
||||||
if (item.isInit) {
|
* @private
|
||||||
|
* @returns
|
||||||
|
* @memberof DataHelper
|
||||||
|
*/
|
||||||
|
getInitDataSourseConfigs() {
|
||||||
|
const initConfigs = this.parser(this.ajaxList).filter((item: DataSourceItem) => {
|
||||||
|
// according to [spec](https://lowcode-engine.cn/lowcode), isInit should be boolean true to be working
|
||||||
|
if (item.isInit === true) {
|
||||||
this.dataSourceMap[item.id].status = DS_STATUS.LOADING;
|
this.dataSourceMap[item.id].status = DS_STATUS.LOADING;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
return initConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* process all dataSourceItems which marked as isInit === true, and get dataSource request results.
|
||||||
|
* @public
|
||||||
|
* @returns
|
||||||
|
* @memberof DataHelper
|
||||||
|
*/
|
||||||
|
getInitData() {
|
||||||
|
const initSyncData = this.getInitDataSourseConfigs();
|
||||||
// 所有 datasource 的 datahandler
|
// 所有 datasource 的 datahandler
|
||||||
return this.asyncDataHandler(initSyncData).then((res) => {
|
return this.asyncDataHandler(initSyncData).then((res) => {
|
||||||
let { dataHandler } = this.config;
|
const { dataHandler } = this.config;
|
||||||
if (isJSFunction(dataHandler)) {
|
return this.handleData(null, dataHandler, res, null);
|
||||||
dataHandler = transformStringToFunction(dataHandler.value);
|
|
||||||
}
|
|
||||||
if (!dataHandler || typeof dataHandler !== 'function') return res;
|
|
||||||
try {
|
|
||||||
return (dataHandler as any).call(this.host, res);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('请求数据处理函数运行出错', e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getDataSource(id: string, params: any, otherOptions: any, callback: any) {
|
getDataSource(id: string, params: any, otherOptions: any, callback: any) {
|
||||||
const req = this.parser(this.ajaxMap[id]);
|
const req = this.parser(this.ajaxMap[id]);
|
||||||
const options = req.options || {};
|
const options = req.options || {};
|
||||||
|
let callbackFn = callback;
|
||||||
|
let otherOptionsObj = otherOptions;
|
||||||
if (typeof otherOptions === 'function') {
|
if (typeof otherOptions === 'function') {
|
||||||
callback = otherOptions;
|
callbackFn = otherOptions;
|
||||||
otherOptions = {};
|
otherOptionsObj = {};
|
||||||
}
|
}
|
||||||
const { headers, ...otherProps } = otherOptions || {};
|
const { headers, ...otherProps } = otherOptionsObj || {};
|
||||||
if (!req) {
|
if (!req) {
|
||||||
console.warn(`getDataSource API named ${id} not exist`);
|
console.warn(`getDataSource API named ${id} not exist`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.asyncDataHandler([
|
return this.asyncDataHandler([
|
||||||
{
|
{
|
||||||
...req,
|
...req,
|
||||||
@ -151,168 +213,97 @@ export class DataHelper {
|
|||||||
])
|
])
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
try {
|
try {
|
||||||
callback && callback(res && res[id]);
|
callbackFn && callbackFn(res && res[id]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('load请求回调函数报错', e);
|
console.error('load请求回调函数报错', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res && res[id];
|
return res && res[id];
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
try {
|
try {
|
||||||
callback && callback(null, err);
|
callbackFn && callbackFn(null, err);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('load请求回调函数报错', e);
|
console.error('load请求回调函数报错', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncDataHandler(asyncDataList: any[]) {
|
asyncDataHandler(asyncDataList: any[]) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const allReq = [];
|
const allReq: any[] = [];
|
||||||
const doserReq: Array<{name: string; package: string; params: any }> = [];
|
|
||||||
const doserList: string[] = [];
|
|
||||||
const beforeRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.beforeRequest;
|
|
||||||
const afterRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.afterRequest;
|
|
||||||
const csrfInput = document.getElementById('_csrf_token');
|
|
||||||
const _tb_token_ = (csrfInput as any)?.value;
|
|
||||||
asyncDataList.forEach((req) => {
|
asyncDataList.forEach((req) => {
|
||||||
const { id, type, options } = req;
|
const { id, type } = req;
|
||||||
if (!id || !type || type === 'legao') return;
|
// TODO: need refactoring to remove 'legao' related logic
|
||||||
if (type === 'doServer') {
|
if (!id || !type || type === 'legao') {
|
||||||
const { uri, params } = options || {};
|
return;
|
||||||
if (!uri) return;
|
|
||||||
doserList.push(id);
|
|
||||||
doserReq.push({ name: uri, package: 'cms', params });
|
|
||||||
} else {
|
|
||||||
allReq.push(req);
|
|
||||||
}
|
}
|
||||||
|
allReq.push(req);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (doserReq.length > 0) {
|
if (allReq.length === 0) {
|
||||||
allReq.push({
|
resolve({});
|
||||||
type: 'doServer',
|
|
||||||
options: {
|
|
||||||
uri: '/nrsService.do',
|
|
||||||
cors: true,
|
|
||||||
method: 'POST',
|
|
||||||
params: {
|
|
||||||
data: JSON.stringify(doserReq),
|
|
||||||
_tb_token_,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (allReq.length === 0) resolve({});
|
|
||||||
const res: any = {};
|
const res: any = {};
|
||||||
// todo:
|
|
||||||
Promise.all(
|
Promise.all(
|
||||||
allReq.map((item: any) => {
|
allReq.map((item: any) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((innerResolve) => {
|
||||||
const { type, id, dataHandler, options } = item;
|
const { type, id, dataHandler, options } = item;
|
||||||
const doFetch = (type: string, options: any) => {
|
|
||||||
this.fetchOne(type as any, options)
|
|
||||||
?.then((data: any) => {
|
|
||||||
if (afterRequest) {
|
|
||||||
this.appHelper.utils.afterRequest(item, data, undefined, (data: any, error: any) => {
|
|
||||||
fetchHandler(data, error);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
fetchHandler(data, undefined);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err: Error) => {
|
|
||||||
if (afterRequest) {
|
|
||||||
// 必须要这么调用,否则beforeRequest中的this会丢失
|
|
||||||
this.appHelper.utils.afterRequest(item, undefined, err, (data: any, error: any) => {
|
|
||||||
fetchHandler(data, error);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
fetchHandler(undefined, err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const fetchHandler = (data: any, error: any) => {
|
const fetchHandler = (data: any, error: any) => {
|
||||||
if (type === 'doServer') {
|
res[id] = this.handleData(id, dataHandler, data, error);
|
||||||
if (!Array.isArray(data)) {
|
|
||||||
data = [data];
|
|
||||||
}
|
|
||||||
doserList.forEach((id, idx) => {
|
|
||||||
const req: any = this.ajaxMap[id];
|
|
||||||
if (req) {
|
|
||||||
res[id] = this.dataHandler(id, req.dataHandler, data && data[idx], error);
|
|
||||||
this.updateDataSourceMap(id, res[id], error);
|
this.updateDataSourceMap(id, res[id], error);
|
||||||
}
|
innerResolve({});
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res[id] = this.dataHandler(id, dataHandler, data, error);
|
|
||||||
this.updateDataSourceMap(id, res[id], error);
|
|
||||||
}
|
|
||||||
resolve({});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type === 'doServer') {
|
const doFetch = (innerType: string, innerOptions: any) => {
|
||||||
doserList.forEach((item) => {
|
doRequest(innerType as any, innerOptions)
|
||||||
this.dataSourceMap[item].status = DS_STATUS.LOADING;
|
?.then((data: any) => {
|
||||||
|
fetchHandler(data, undefined);
|
||||||
|
})
|
||||||
|
.catch((err: Error) => {
|
||||||
|
fetchHandler(undefined, err);
|
||||||
});
|
});
|
||||||
} else {
|
};
|
||||||
|
|
||||||
this.dataSourceMap[id].status = DS_STATUS.LOADING;
|
this.dataSourceMap[id].status = DS_STATUS.LOADING;
|
||||||
}
|
|
||||||
// 请求切片
|
|
||||||
if (beforeRequest) {
|
|
||||||
// 必须要这么调用,否则beforeRequest中的this会丢失
|
|
||||||
this.appHelper.utils.beforeRequest(item, clone(options), (options: any) => doFetch(type, options));
|
|
||||||
} else {
|
|
||||||
doFetch(type, options);
|
doFetch(type, options);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
)
|
).then(() => {
|
||||||
.then(() => {
|
|
||||||
resolve(res);
|
resolve(res);
|
||||||
})
|
}).catch((e) => {
|
||||||
.catch((e) => {
|
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// dataHandler todo:
|
/**
|
||||||
dataHandler(id: string, dataHandler: any, data: any, error: any) {
|
* process data using dataHandler
|
||||||
|
*
|
||||||
|
* @param {(string | null)} id request id, will be used in error message, can be null
|
||||||
|
* @param {*} dataHandler
|
||||||
|
* @param {*} data
|
||||||
|
* @param {*} error
|
||||||
|
* @returns
|
||||||
|
* @memberof DataHelper
|
||||||
|
*/
|
||||||
|
handleData(id: string | null, dataHandler: any, data: any, error: any) {
|
||||||
|
let dataHandlerFun = dataHandler;
|
||||||
if (isJSFunction(dataHandler)) {
|
if (isJSFunction(dataHandler)) {
|
||||||
dataHandler = transformStringToFunction(dataHandler.value);
|
dataHandlerFun = transformStringToFunction(dataHandler.value);
|
||||||
|
}
|
||||||
|
if (!dataHandlerFun || typeof dataHandlerFun !== 'function') {
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
if (!dataHandler || typeof dataHandler !== 'function') return data;
|
|
||||||
try {
|
try {
|
||||||
return dataHandler.call(this.host, data, error);
|
return dataHandlerFun.call(this.host, data, error);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (id) {
|
||||||
console.error(`[${id}]单个请求数据处理函数运行出错`, e);
|
console.error(`[${id}]单个请求数据处理函数运行出错`, e);
|
||||||
|
} else {
|
||||||
|
console.error('请求数据处理函数运行出错', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchOne(type: DataSourceType, options: any) {
|
|
||||||
// eslint-disable-next-line prefer-const
|
|
||||||
let { uri, url, method = 'GET', headers, params, ...otherProps } = options;
|
|
||||||
otherProps = otherProps || {};
|
|
||||||
if (type === 'jsonp') {
|
|
||||||
return jsonp(uri, params, otherProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'fetch') {
|
|
||||||
switch (method.toUpperCase()) {
|
|
||||||
case 'GET':
|
|
||||||
return get(uri, params, headers, otherProps);
|
|
||||||
case 'POST':
|
|
||||||
return post(uri, params, headers, otherProps);
|
|
||||||
default:
|
|
||||||
return request(uri, method, params, headers, otherProps);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(`Engine default dataSource not support type:[${type}] dataSource request!`, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type DataSourceType = 'fetch' | 'jsonp';
|
|
||||||
|
|||||||
569
packages/renderer-core/tests/utils/data-helper.test.ts
Normal file
569
packages/renderer-core/tests/utils/data-helper.test.ts
Normal file
@ -0,0 +1,569 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
const mockJsonp = jest.fn();
|
||||||
|
const mockRequest = jest.fn();
|
||||||
|
const mockGet = jest.fn();
|
||||||
|
const mockPost = jest.fn();
|
||||||
|
jest.mock('../../src/utils/request', () => {
|
||||||
|
return {
|
||||||
|
jsonp: (uri, params, headers, otherProps) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(mockJsonp(uri, params, headers, otherProps));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
request: (uri, params, headers, otherProps) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(mockRequest(uri, params, headers, otherProps));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
get: (uri, params, headers, otherProps) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(mockGet(uri, params, headers, otherProps));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
post: (uri, params, headers, otherProps) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(mockPost(uri, params, headers, otherProps));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
import { DataHelper, doRequest } from '../../src/utils/data-helper';
|
||||||
|
import { parseData } from '../../src/utils/common';
|
||||||
|
|
||||||
|
describe('test DataHelper ', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules();
|
||||||
|
})
|
||||||
|
it('can be inited', () => {
|
||||||
|
const mockHost = {};
|
||||||
|
let mockDataSourceConfig = {};
|
||||||
|
const mockAppHelper = {};
|
||||||
|
const mockParser = (config: any) => parseData(config);
|
||||||
|
let dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
|
||||||
|
expect(dataHelper).toBeTruthy();
|
||||||
|
expect(dataHelper.host).toBe(mockHost);
|
||||||
|
expect(dataHelper.config).toBe(mockDataSourceConfig);
|
||||||
|
expect(dataHelper.appHelper).toBe(mockAppHelper);
|
||||||
|
expect(dataHelper.parser).toBe(mockParser);
|
||||||
|
|
||||||
|
|
||||||
|
dataHelper = new DataHelper(mockHost, undefined, mockAppHelper, mockParser);
|
||||||
|
expect(dataHelper.config).toStrictEqual({});
|
||||||
|
expect(dataHelper.ajaxList).toStrictEqual([]);
|
||||||
|
|
||||||
|
mockDataSourceConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'ds1',
|
||||||
|
}, {
|
||||||
|
id: 'ds2',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
expect(dataHelper.config).toBe(mockDataSourceConfig);
|
||||||
|
expect(dataHelper.ajaxList.length).toBe(2);
|
||||||
|
expect(dataHelper.ajaxMap.ds1).toStrictEqual({
|
||||||
|
id: 'ds1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should handle generateDataSourceMap properly in constructor', () => {
|
||||||
|
const mockHost = {};
|
||||||
|
let mockDataSourceConfig = {};
|
||||||
|
const mockAppHelper = {};
|
||||||
|
const mockParser = (config: any) => parseData(config);
|
||||||
|
let dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
|
||||||
|
// test generateDataSourceMap logic
|
||||||
|
mockDataSourceConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'getInfo',
|
||||||
|
isInit: true,
|
||||||
|
type: 'fetch', // fetch/mtop/jsonp/custom
|
||||||
|
options: {
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
method: 'GET',
|
||||||
|
params: { a: 1 },
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
id: 'postInfo',
|
||||||
|
isInit: true,
|
||||||
|
type: 'fetch',
|
||||||
|
options: {
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
method: 'POST',
|
||||||
|
params: { a: 1 },
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
expect(Object.keys(dataHelper.dataSourceMap).length).toBe(2);
|
||||||
|
expect(dataHelper.dataSourceMap.getInfo.status).toBe('init');
|
||||||
|
expect(typeof dataHelper.dataSourceMap.getInfo.load).toBe('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getInitDataSourseConfigs should work', () => {
|
||||||
|
const mockHost = {};
|
||||||
|
let mockDataSourceConfig = {};
|
||||||
|
const mockAppHelper = {};
|
||||||
|
const mockParser = (config: any) => parseData(config);
|
||||||
|
|
||||||
|
// test generateDataSourceMap logic
|
||||||
|
mockDataSourceConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'getInfo',
|
||||||
|
isInit: true,
|
||||||
|
type: 'fetch', // fetch/mtop/jsonp/custom
|
||||||
|
options: {
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
method: 'GET',
|
||||||
|
params: { a: 1 },
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'postInfo',
|
||||||
|
isInit: false,
|
||||||
|
type: 'fetch',
|
||||||
|
options: {
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
method: 'POST',
|
||||||
|
params: { a: 1 },
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'getInfoLater',
|
||||||
|
isInit: false,
|
||||||
|
type: 'fetch',
|
||||||
|
options: {
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
method: 'POST',
|
||||||
|
params: { a: 1 },
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'getInfoLater2',
|
||||||
|
isInit: 'not a valid boolean',
|
||||||
|
type: 'fetch',
|
||||||
|
options: {
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
method: 'POST',
|
||||||
|
params: { a: 1 },
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
expect(dataHelper.getInitDataSourseConfigs().length).toBe(1);
|
||||||
|
expect(dataHelper.getInitDataSourseConfigs()[0].id).toBe('getInfo');
|
||||||
|
});
|
||||||
|
it('util function doRequest should work', () => {
|
||||||
|
doRequest('jsonp', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockJsonp).toBeCalled();
|
||||||
|
|
||||||
|
// test GET
|
||||||
|
doRequest('fetch', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
method: 'get',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockGet).toBeCalled();
|
||||||
|
|
||||||
|
mockGet.mockClear();
|
||||||
|
doRequest('fetch', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
method: 'Get',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockGet).toBeCalled();
|
||||||
|
|
||||||
|
mockGet.mockClear();
|
||||||
|
doRequest('fetch', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
method: 'GET',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockGet).toBeCalled();
|
||||||
|
|
||||||
|
mockGet.mockClear();
|
||||||
|
|
||||||
|
// test POST
|
||||||
|
doRequest('fetch', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
method: 'post',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockPost).toBeCalled();
|
||||||
|
mockPost.mockClear();
|
||||||
|
|
||||||
|
doRequest('fetch', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
method: 'POST',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockPost).toBeCalled();
|
||||||
|
mockPost.mockClear();
|
||||||
|
doRequest('fetch', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
method: 'Post',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockPost).toBeCalled();
|
||||||
|
mockPost.mockClear();
|
||||||
|
|
||||||
|
// test default
|
||||||
|
doRequest('fetch', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
method: 'whatever',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockRequest).toBeCalled();
|
||||||
|
mockRequest.mockClear();
|
||||||
|
mockGet.mockClear();
|
||||||
|
|
||||||
|
// method will be GET when not provided
|
||||||
|
doRequest('fetch', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockRequest).toBeCalledTimes(0);
|
||||||
|
expect(mockGet).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
mockRequest.mockClear();
|
||||||
|
mockGet.mockClear();
|
||||||
|
mockPost.mockClear();
|
||||||
|
mockJsonp.mockClear();
|
||||||
|
|
||||||
|
doRequest('someOtherType', {
|
||||||
|
uri: 'https://www.baidu.com',
|
||||||
|
params: { a: 1 },
|
||||||
|
otherStuff1: 'aaa',
|
||||||
|
});
|
||||||
|
expect(mockRequest).toBeCalledTimes(0);
|
||||||
|
expect(mockGet).toBeCalledTimes(0);
|
||||||
|
expect(mockPost).toBeCalledTimes(0);
|
||||||
|
expect(mockJsonp).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
|
it('updateDataSourceMap should work', () => {
|
||||||
|
const mockHost = {};
|
||||||
|
const mockDataSourceConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'ds1',
|
||||||
|
}, {
|
||||||
|
id: 'ds2',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const mockAppHelper = {};
|
||||||
|
const mockParser = (config: any) => parseData(config);
|
||||||
|
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
dataHelper.updateDataSourceMap('ds1', { a: 1 }, null);
|
||||||
|
expect(dataHelper.dataSourceMap['ds1']).toBeTruthy();
|
||||||
|
expect(dataHelper.dataSourceMap['ds1'].data).toStrictEqual({ a: 1 });
|
||||||
|
expect(dataHelper.dataSourceMap['ds1'].error).toBeUndefined();
|
||||||
|
expect(dataHelper.dataSourceMap['ds1'].status).toBe('loaded');
|
||||||
|
dataHelper.updateDataSourceMap('ds2', { b: 2 }, new Error());
|
||||||
|
expect(dataHelper.dataSourceMap['ds2']).toBeTruthy();
|
||||||
|
expect(dataHelper.dataSourceMap['ds2'].data).toStrictEqual({ b: 2 });
|
||||||
|
expect(dataHelper.dataSourceMap['ds2'].status).toBe('error');
|
||||||
|
expect(dataHelper.dataSourceMap['ds2'].error).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handleData should work', () => {
|
||||||
|
const mockHost = { stateA: 'aValue'};
|
||||||
|
const mockDataSourceConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'fullConfigGet',
|
||||||
|
isInit: true,
|
||||||
|
options: {
|
||||||
|
params: {},
|
||||||
|
method: 'GET',
|
||||||
|
isCors: true,
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {},
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
},
|
||||||
|
shouldFetch: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function() { return true; }',
|
||||||
|
},
|
||||||
|
dataHandler: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(res) { return res.data; }',
|
||||||
|
},
|
||||||
|
errorHandler: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(error) {}',
|
||||||
|
},
|
||||||
|
willFetch: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(options) { return options; }',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const mockAppHelper = {};
|
||||||
|
const mockParser = (config: any) => parseData(config);
|
||||||
|
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
// test valid case
|
||||||
|
let mockDataHandler = {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(res) { return res.data + \'+\' + this.stateA; }',
|
||||||
|
};
|
||||||
|
let result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null);
|
||||||
|
expect(result).toBe('mockDataValue+aValue');
|
||||||
|
|
||||||
|
// test invalid datahandler
|
||||||
|
mockDataHandler = {
|
||||||
|
type: 'not a JSFunction',
|
||||||
|
value: 'function(res) { return res.data + \'+\' + this.stateA; }',
|
||||||
|
};
|
||||||
|
result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null);
|
||||||
|
expect(result).toStrictEqual({ data: 'mockDataValue' });
|
||||||
|
|
||||||
|
// test exception
|
||||||
|
const mockError = jest.fn();
|
||||||
|
const orginalConsole = global.console;
|
||||||
|
global.console = { error: mockError };
|
||||||
|
|
||||||
|
// exception with id
|
||||||
|
mockDataHandler = {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(res) { return res.data + \'+\' + JSON.parse({a:1}); }',
|
||||||
|
};
|
||||||
|
result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
expect(mockError).toBeCalledWith('[fullConfigGet]单个请求数据处理函数运行出错', expect.anything());
|
||||||
|
|
||||||
|
// exception without id
|
||||||
|
mockDataHandler = {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(res) { return res.data + \'+\' + JSON.parse({a:1}); }',
|
||||||
|
};
|
||||||
|
result = dataHelper.handleData(null, mockDataHandler, { data: 'mockDataValue' }, null);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
expect(mockError).toBeCalledWith('请求数据处理函数运行出错', expect.anything());
|
||||||
|
|
||||||
|
global.console = orginalConsole;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('updateConfig should work', () => {
|
||||||
|
const mockHost = { stateA: 'aValue'};
|
||||||
|
const mockDataSourceConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'ds1',
|
||||||
|
}, {
|
||||||
|
id: 'ds2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fullConfigGet',
|
||||||
|
isInit: true,
|
||||||
|
options: {
|
||||||
|
params: {},
|
||||||
|
method: 'GET',
|
||||||
|
isCors: true,
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {},
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
},
|
||||||
|
shouldFetch: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function() { return true; }',
|
||||||
|
},
|
||||||
|
dataHandler: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(res) { return res.data; }',
|
||||||
|
},
|
||||||
|
errorHandler: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(error) {}',
|
||||||
|
},
|
||||||
|
willFetch: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(options) { return options; }',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const mockAppHelper = {};
|
||||||
|
const mockParser = (config: any) => parseData(config);
|
||||||
|
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
|
||||||
|
expect(dataHelper.ajaxList.length).toBe(3);
|
||||||
|
|
||||||
|
let updatedConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'ds2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fullConfigGet',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
dataHelper.updateConfig(updatedConfig);
|
||||||
|
|
||||||
|
expect(dataHelper.ajaxList.length).toBe(2);
|
||||||
|
expect(dataHelper.dataSourceMap.ds1).toBeUndefined();
|
||||||
|
|
||||||
|
updatedConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'ds2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fullConfigGet',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ds3',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
dataHelper.updateConfig(updatedConfig);
|
||||||
|
expect(dataHelper.ajaxList.length).toBe(3);
|
||||||
|
expect(dataHelper.dataSourceMap.ds3).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getInitData should work', () => {
|
||||||
|
const mockHost = { stateA: 'aValue'};
|
||||||
|
const mockDataSourceConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'ds1',
|
||||||
|
}, {
|
||||||
|
id: 'ds2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fullConfigGet',
|
||||||
|
isInit: true,
|
||||||
|
type: 'fetch',
|
||||||
|
options: {
|
||||||
|
params: {},
|
||||||
|
method: 'GET',
|
||||||
|
isCors: true,
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {
|
||||||
|
headerA: 1,
|
||||||
|
},
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
},
|
||||||
|
shouldFetch: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function() { return true; }',
|
||||||
|
},
|
||||||
|
dataHandler: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(res) { return 123; }',
|
||||||
|
},
|
||||||
|
errorHandler: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(error) {}',
|
||||||
|
},
|
||||||
|
willFetch: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(options) { return options; }',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const mockAppHelper = {};
|
||||||
|
const mockParser = (config: any) => parseData(config);
|
||||||
|
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
|
||||||
|
expect(dataHelper.ajaxList.length).toBe(3);
|
||||||
|
expect(mockGet).toBeCalledTimes(0);
|
||||||
|
dataHelper.getInitData().then(res => {
|
||||||
|
expect(mockGet).toBeCalledTimes(1);
|
||||||
|
expect(mockGet).toBeCalledWith('mock/info.json', {}, {
|
||||||
|
headerA: 1,
|
||||||
|
}, expect.anything());
|
||||||
|
mockGet.mockClear();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getDataSource should work', () => {
|
||||||
|
const mockHost = { stateA: 'aValue'};
|
||||||
|
const mockDataSourceConfig = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: 'ds1',
|
||||||
|
}, {
|
||||||
|
id: 'ds2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fullConfigGet',
|
||||||
|
isInit: true,
|
||||||
|
type: 'fetch',
|
||||||
|
options: {
|
||||||
|
params: {},
|
||||||
|
method: 'GET',
|
||||||
|
isCors: true,
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {
|
||||||
|
headerA: 1,
|
||||||
|
},
|
||||||
|
uri: 'mock/info.json',
|
||||||
|
},
|
||||||
|
shouldFetch: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function() { return true; }',
|
||||||
|
},
|
||||||
|
dataHandler: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(res) { return 123; }',
|
||||||
|
},
|
||||||
|
errorHandler: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(error) {}',
|
||||||
|
},
|
||||||
|
willFetch: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(options) { return options; }',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const mockAppHelper = {};
|
||||||
|
const mockParser = (config: any) => parseData(config);
|
||||||
|
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||||
|
|
||||||
|
expect(dataHelper.ajaxList.length).toBe(3);
|
||||||
|
expect(mockGet).toBeCalledTimes(0);
|
||||||
|
const callbackFn = jest.fn();
|
||||||
|
dataHelper.getDataSource('fullConfigGet', { param1: 'value1' }, {}, callbackFn).then(res => {
|
||||||
|
expect(mockGet).toBeCalledTimes(1);
|
||||||
|
expect(mockGet).toBeCalledWith('mock/info.json', { param1: 'value1' }, {
|
||||||
|
headerA: 1,
|
||||||
|
}, expect.anything());
|
||||||
|
mockGet.mockClear();
|
||||||
|
expect(callbackFn).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user