Merge pull request 'Optimize Storage Modular' (#16) from dev-mrkbear into master

Reviewed-on: http://git.mrkbear.com/MrKBear/mini-dlpu-v3/pulls/16
This commit is contained in:
MrKBear 2022-01-03 13:36:55 +08:00
commit de67ab0d68
8 changed files with 269 additions and 373 deletions

View File

@ -2,9 +2,9 @@
掌上教务处作为工业大学的社区开源项目自从2017年开始已有近5年岁月在无数同学的贡献之下为大家打造便捷的校园服务。 掌上教务处作为工业大学的社区开源项目自从2017年开始已有近5年岁月在无数同学的贡献之下为大家打造便捷的校园服务。
__*!!!注意!!!*__ __*!!!警告!!!*__
请在主仓库提交代码,而非镜像仓库! 请在主仓库提交代码,而非镜像仓库!在镜像仓库提交的代码将会在同步时被覆盖!
主仓库: http://git.mrkbear.com/MrKBear/mini-dlpu-v3 主仓库: http://git.mrkbear.com/MrKBear/mini-dlpu-v3

View File

@ -1,8 +1,9 @@
import { IAppAPIParam } from "./core/Api"; import { IAppAPIParam } from "./core/Api";
import { IAppStorageParam, Storage, IStorageData } from "./core/Storage";
import { Logger, LevelLogLabel, LifeCycleLogLabel } from "./core/Logger"; import { Logger, LevelLogLabel, LifeCycleLogLabel } from "./core/Logger";
App<IAppAPIParam>({ App<IAppAPIParam & IAppStorageParam>({
/** /**
* API * API
@ -16,7 +17,7 @@ App<IAppAPIParam>({
/** /**
* *
*/ */
// storageCache: new Set<string>(), storage: new Map<string, Storage<IStorageData>>(),
/** /**
* *

View File

@ -2,27 +2,18 @@ export type EventType = string | symbol;
// An event handler can take an optional event argument // An event handler can take an optional event argument
// and should not return a value // and should not return a value
export type Handler<T = unknown> = (event: T) => void; export type Handler<T = any> = (event: T) => void;
export type WildcardHandler<T = Record<string, unknown>> = (
type: keyof T,
event: T[keyof T]
) => void;
// An array of all currently registered event handlers for a type // An array of all currently registered event handlers for a type
export type EventHandlerList<T = unknown> = Array<Handler<T>>; export type EventHandlerList<T = any> = Array<Handler<T>>;
export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;
// A map of event types and their corresponding event handlers. // A map of event types and their corresponding event handlers.
export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map< export type EventHandlerMap<Events extends Record<EventType, any>> = Map<
keyof Events | '*', keyof Events,
EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events> EventHandlerList<Events[keyof Events]>
>; >;
type GenericEventHandler<Events extends Record<EventType, unknown>> = export class Emitter<Events extends Record<EventType, any>> {
| Handler<Events[keyof Events]>
| WildcardHandler<Events>;
export class Emitter<Events extends Record<EventType, unknown>> {
/** /**
* A Map of event names to registered handler functions. * A Map of event names to registered handler functions.
@ -41,17 +32,14 @@ export class Emitter<Events extends Record<EventType, unknown>> {
this.all!.set(type, [] as EventHandlerList<Events[keyof Events]>); this.all!.set(type, [] as EventHandlerList<Events[keyof Events]>);
} }
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
on(type: '*', handler: WildcardHandler<Events>): void;
/** /**
* Register an event handler for the given type. * Register an event handler for the given type.
* @param {string|symbol} type Type of event to listen for, or `'*'` for all events * @param {string|symbol} type Type of event to listen for
* @param {Function} handler Function to call in response to given event * @param {Function} handler Function to call in response to given event
* @memberOf mitt * @memberOf mitt
*/ */
public on<Key extends keyof Events>(type: Key, handler: GenericEventHandler<Events>) { public on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>) {
const handlers: Array<GenericEventHandler<Events>> | undefined = this.all!.get(type); const handlers: Array<Handler<Events[Key]>> | undefined = this.all!.get(type);
if (handlers) { if (handlers) {
handlers.push(handler); handlers.push(handler);
} }
@ -60,18 +48,15 @@ export class Emitter<Events extends Record<EventType, unknown>> {
} }
} }
off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
off(type: '*', handler: WildcardHandler<Events>): void;
/** /**
* Remove an event handler for the given type. * Remove an event handler for the given type.
* If `handler` is omitted, all handlers of the given type are removed. * If `handler` is omitted, all handlers of the given type are removed.
* @param {string|symbol} type Type of event to unregister `handler` from, or `'*'` * @param {string|symbol} type Type of event to unregister `handler` from
* @param {Function} [handler] Handler function to remove * @param {Function} [handler] Handler function to remove
* @memberOf mitt * @memberOf mitt
*/ */
public off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler<Events>) { public off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>) {
const handlers: Array<GenericEventHandler<Events>> | undefined = this.all!.get(type); const handlers: Array<Handler<Events[Key]>> | undefined = this.all!.get(type);
if (handlers) { if (handlers) {
if (handler) { if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1); handlers.splice(handlers.indexOf(handler) >>> 0, 1);
@ -82,20 +67,14 @@ export class Emitter<Events extends Record<EventType, unknown>> {
} }
} }
emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
/** /**
* Invoke all handlers for the given type. * Invoke all handlers for the given type.
* If present, `'*'` handlers are invoked after type-matched handlers.
*
* Note: Manually firing '*' handlers is not supported.
* *
* @param {string|symbol} type The event type to invoke * @param {string|symbol} type The event type to invoke
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberOf mitt * @memberOf mitt
*/ */
emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) { emit<Key extends keyof Events>(type: Key, evt: Events[Key]) {
let handlers = this.all!.get(type); let handlers = this.all!.get(type);
if (handlers) { if (handlers) {
(handlers as EventHandlerList<Events[keyof Events]>) (handlers as EventHandlerList<Events[keyof Events]>)
@ -104,14 +83,5 @@ export class Emitter<Events extends Record<EventType, unknown>> {
handler(evt!); handler(evt!);
}); });
} }
handlers = this.all!.get('*');
if (handlers) {
(handlers as WildCardEventHandlerList<Events>)
.slice()
.map((handler) => {
handler(type, evt!);
});
}
} }
} }

View File

@ -1,5 +1,5 @@
import { Emitter, EventType } from "./Emitter"; import { Emitter } from "./Emitter";
import { Logger, LogLabel, LogStyle, LevelLogLabel } from "./Logger"; import { Logger, LogLabel, colorRadio, LevelLogLabel } from "./Logger";
/** /**
* *
@ -50,6 +50,149 @@ type Depends<M extends Manager<AnyWXContext>> = {
[x:string]: Modular<M, Depends<M>>; [x:string]: Modular<M, Depends<M>>;
}; };
/**
*
*/
class WXInstanceMethods<
E extends IAnyTypeObject = IAnyTypeObject,
W extends AnyWXContext = AnyWXContext
>
extends Emitter<E>
implements InstanceMethods<W["data"]> {
public superContext: W;
public constructor(context: W) {
super();
this.superContext = context;
}
public setData(data: Partial<W["data"]> & WechatMiniprogram.IAnyObject, callback?: () => void): void {
return this.superContext.setData(data, callback);
}
public hasBehavior(behavior: string): void {
return this.superContext.hasBehavior(behavior);
}
public triggerEvent<DetailType = any>(name: string, detail?: DetailType, options?: WechatMiniprogram.Component.TriggerEventOption): void {
return this.superContext.triggerEvent(name, detail, options)
}
public createSelectorQuery(): WechatMiniprogram.SelectorQuery {
return this.superContext.createSelectorQuery();
}
public createIntersectionObserver(options: WechatMiniprogram.CreateIntersectionObserverOption): WechatMiniprogram.IntersectionObserver {
return this.superContext.createIntersectionObserver(options);
}
public selectComponent(selector: string): WechatMiniprogram.Component.TrivialInstance {
return this.superContext.selectComponent(selector);
}
public selectAllComponents(selector: string): WechatMiniprogram.Component.TrivialInstance[] {
return this.superContext.selectAllComponents(selector);
}
public selectOwnerComponent(): WechatMiniprogram.Component.TrivialInstance {
return this.superContext.selectOwnerComponent();
}
public getRelationNodes(relationKey: string): WechatMiniprogram.Component.TrivialInstance[] {
return this.superContext.getRelationNodes(relationKey);
}
public groupSetData(callback?: () => void): void {
return this.superContext.groupSetData(callback);
}
public getTabBar(): WechatMiniprogram.Component.TrivialInstance {
return this.superContext.getTabBar();
}
public getPageId(): string {
return this.superContext.getPageId();
}
public animate(selector: string, keyFrames: WechatMiniprogram.Component.KeyFrame[], duration: number, callback?: () => void): void;
public animate(selector: string, keyFrames: WechatMiniprogram.Component.ScrollTimelineKeyframe[], duration: number,
scrollTimeline: WechatMiniprogram.Component.ScrollTimelineOption): void;
public animate(selector: any, keyFrames: any, duration: any, scrollTimeline?: any): void {
return this.superContext.animate(selector, keyFrames, duration, scrollTimeline);
}
public clearAnimation(selector: string, callback: () => void): void;
public clearAnimation(selector: string, options?: WechatMiniprogram.Component.ClearAnimationOptions, callback?: () => void): void;
public clearAnimation(selector: any, options?: any, callback?: any): void {
return this.superContext.clearAnimation(selector, options, callback);
}
public getOpenerEventChannel(): WechatMiniprogram.EventChannel {
return this.superContext.getOpenerEventChannel();
}
}
/**
*
*/
class WXInstanceProperties<
E extends IAnyTypeObject = IAnyTypeObject,
W extends AnyWXContext = AnyWXContext
>
extends WXInstanceMethods<E, W>
implements InstanceProperties {
public override superContext: W;
constructor(context: W) {
super(context);
this.superContext = context;
}
public get is(): string { return this.superContext.is };
public get route(): string { return this.superContext.route };
public get options(): Record<string, string | undefined> { return this.superContext.options };
}
class WXILifetime<
E extends IAnyTypeObject = IAnyTypeObject,
W extends AnyWXContext = AnyWXContext
>
extends WXInstanceProperties<E, W>
implements ILifetime {
public onLoad(query: Record<string, string | undefined>): void | Promise<void> {};
public onShow(): void | Promise<void> {};
public onReady(): void | Promise<void> {};
public onHide(): void | Promise<void> {};
public onUnload(): void | Promise<void> {};
public onPullDownRefresh(): void | Promise<void> {};
public onReachBottom(): void | Promise<void> {};
public onShareAppMessage(options: WechatMiniprogram.Page.IShareAppMessageOption): void | WechatMiniprogram.Page.ICustomShareContent {};
public onShareTimeline(): void | WechatMiniprogram.Page.ICustomTimelineContent {};
public onPageScroll(options: WechatMiniprogram.Page.IPageScrollOption): void | Promise<void> {};
public onTabItemTap(options: WechatMiniprogram.Page.ITabItemTapOption): void | Promise<void> {};
public onResize(options: WechatMiniprogram.Page.IResizeOption): void | Promise<void> {};
public onAddToFavorites(options: WechatMiniprogram.Page.IAddToFavoritesOption): WechatMiniprogram.Page.IAddToFavoritesContent {
return {};
};
}
/** /**
* *
* @template M Manager * @template M Manager
@ -60,9 +203,10 @@ type Depends<M extends Manager<AnyWXContext>> = {
class Modular< class Modular<
M extends Manager<AnyWXContext> = Manager<AnyWXContext>, M extends Manager<AnyWXContext> = Manager<AnyWXContext>,
DEP extends Depends<M> = Depends<M>, DEP extends Depends<M> = Depends<M>,
E extends Record<EventType, unknown> = Record<EventType, unknown>, E extends IAnyTypeObject = IAnyTypeObject,
TD extends IAnyTypeObject = IAnyTypeObject> TD extends IAnyTypeObject = IAnyTypeObject
extends Emitter<E> >
extends WXILifetime<E, M["context"]>
implements WXContext<TD, IAnyTypeObject> { implements WXContext<TD, IAnyTypeObject> {
// [x:string]: any; // [x:string]: any;
@ -95,7 +239,7 @@ implements WXContext<TD, IAnyTypeObject> {
public functionList:Set<string>; public functionList:Set<string>;
/** /**
* 使 * 使
*/ */
public paramList:Set<string>; public paramList:Set<string>;
@ -103,13 +247,6 @@ implements WXContext<TD, IAnyTypeObject> {
* *
*/ */
public nameSpace:string; public nameSpace:string;
// 映射主上下文属性
public get is():string { return this.context.is };
public get route():string { return this.context.route };
public get options():Record<string, string | undefined> {
return this.context.options;
};
/** /**
* *
@ -120,7 +257,7 @@ implements WXContext<TD, IAnyTypeObject> {
*/ */
public constructor(manager:M, nameSpace:string, depend?: DEP) { public constructor(manager:M, nameSpace:string, depend?: DEP) {
super(); super(manager.context);
// 保存微信上下文 // 保存微信上下文
this.manager = manager; this.manager = manager;
@ -134,7 +271,7 @@ implements WXContext<TD, IAnyTypeObject> {
this.nameSpace = nameSpace; this.nameSpace = nameSpace;
} }
public setData(data:Partial<TD>, callback?: () => void):void { public override setData(data:Partial<TD>, callback?: () => void):void {
if(this.data === void 0) { if(this.data === void 0) {
this.data = {} as TD; this.data = {} as TD;
@ -162,76 +299,6 @@ implements WXContext<TD, IAnyTypeObject> {
(this.context as IAnyTypeObject) (this.context as IAnyTypeObject)
[`${ this.nameSpace }$${ name }`] = fn.bind(this); [`${ this.nameSpace }$${ name }`] = fn.bind(this);
} }
//#region 映射微信的继承函数
public hasBehavior(behavior: string): void {
return this.context.hasBehavior(behavior);
}
public triggerEvent<DetailType>(name: string, detail?: DetailType,
options?: WechatMiniprogram.Component.TriggerEventOption): void {
return this.context.triggerEvent<DetailType>(name, detail, options);
}
public createSelectorQuery(): WechatMiniprogram.SelectorQuery {
return this.context.createSelectorQuery();
}
public createIntersectionObserver(options: WechatMiniprogram.CreateIntersectionObserverOption):
WechatMiniprogram.IntersectionObserver {
return this.context.createIntersectionObserver(options);
}
public selectComponent(selector: string): WechatMiniprogram.Component.TrivialInstance {
return this.context.selectComponent(selector);
}
public selectAllComponents(selector: string): WechatMiniprogram.Component.TrivialInstance[] {
return this.context.selectAllComponents(selector);
}
public selectOwnerComponent(): WechatMiniprogram.Component.TrivialInstance {
return this.context.selectOwnerComponent();
}
public getRelationNodes(relationKey: string): WechatMiniprogram.Component.TrivialInstance[] {
return this.context.getRelationNodes(relationKey);
}
public groupSetData(callback?: () => void): void {
return this.context.groupSetData(callback);
}
public getTabBar(): WechatMiniprogram.Component.TrivialInstance {
return this.context.getTabBar();
}
public getPageId(): string {
return this.context.getPageId();
}
public animate(selector: string, keyFrames: WechatMiniprogram.Component.KeyFrame[],
duration: number, callback?: () => void): void;
public animate(selector: string, keyFrames: WechatMiniprogram.Component.ScrollTimelineKeyframe[],
duration: number, scrollTimeline: WechatMiniprogram.Component.ScrollTimelineOption): void;
public animate(selector: any, keyFrames: any, duration: any, scrollTimeline?: any): void {
return this.context.animate(selector, keyFrames, duration, scrollTimeline);
}
public clearAnimation(selector: string, callback: () => void): void;
public clearAnimation(selector: string, options?: WechatMiniprogram.Component.ClearAnimationOptions,
callback?: () => void): void;
public clearAnimation(selector: any, options?: any, callback?: any): void {
return this.context.clearAnimation(selector, options, callback);
}
public getOpenerEventChannel(): WechatMiniprogram.EventChannel {
return this.context.getOpenerEventChannel();
}
//#endregion
} }
/** /**
@ -244,19 +311,9 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
* *
*/ */
static readonly WxLifeCycle:(keyof ILifetime)[] = [ static readonly WxLifeCycle:(keyof ILifetime)[] = [
"onShow", "onShow", "onReady", "onHide", "onUnload", "onPullDownRefresh", "onReachBottom",
"onReady", "onShareAppMessage", "onShareTimeline","onAddToFavorites","onPageScroll", "onResize", "onTabItemTap"
"onHide", ];
"onUnload",
"onPullDownRefresh",
"onReachBottom",
"onShareAppMessage",
"onShareTimeline",
"onAddToFavorites",
"onPageScroll",
"onResize",
"onTabItemTap"
]
/** /**
* *
@ -288,9 +345,10 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
* @param depend * @param depend
* @returns * @returns
*/ */
public addModule<DEP extends Depends<this>, M extends Modular<this, DEP>> public addModule<DEP extends Depends<this>, M extends Modular<this, DEP>> (
(mode: new (manager:Manager<WXC>, nameSpace:string, depend?:DEP) => M, mode: new (manager:this, nameSpace:string, depend?:DEP) => M,
nameSpace:string, depend?:DEP):M { nameSpace:string, depend?:DEP
):M {
let mod = new mode(this, nameSpace, depend); let mod = new mode(this, nameSpace, depend);
this.modules.push(mod); this.modules.push(mod);
return mod; return mod;
@ -300,14 +358,14 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
* *
* @param key * @param key
*/ */
public creatHooks(key:keyof ILifetime):(...arg: any[]) => Promise<any> { public creatHooks<Key extends keyof ILifetime>(key: Key): ILifetime[Key] {
return async (...arg: any[]) => { let hook = (async (...arg: any[]) => {
let hooks:Promise<any>[] = []; let hooks:Promise<any>[] = [];
for(let i = 0; i < this.modules.length; i++) { for(let i = 0; i < this.modules.length; i++) {
let fn:Function = (this.modules[i] as IAnyTypeObject)[key]; let fn:Function = this.modules[i][key];
if(fn === void 0) continue; if(fn === void 0) continue;
let res: Promise<any> | any = fn.apply(this.modules[i], arg); let res: Promise<any> | any = fn.apply(this.modules[i], arg);
@ -340,7 +398,10 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
} }
return Promise.all(hooks); return Promise.all(hooks);
} });
// TODO: 此处为,关键位置,容易出错,请再次检查
return (hook as any);
} }
/** /**
@ -383,7 +444,7 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
this.context.setData(this.context.data); this.context.setData(this.context.data);
// 调用全部模块的 onLoad 周期 // 调用全部模块的 onLoad 周期
let res = this.creatHooks("onLoad")(query as any); let res = this.creatHooks("onLoad")(query);
// 打印每个模块的键值对使用情况 // 打印每个模块的键值对使用情况
for(let i = 0; i < this.modules.length; i++) { for(let i = 0; i < this.modules.length; i++) {
@ -411,9 +472,9 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
/** /**
* *
*/ */
public static readonly AddModuleLabel = new LogLabel("addModule", public static readonly AddModuleLabel = new LogLabel(
new LogStyle().setBorder("4px", `1px solid #8600FF`) "addModule",
.setColor("#FF00FF", "rgba(54, 0, 255, .2)").setBlank("0 5px") colorRadio(54, 0, 255)
) )
/** /**

View File

@ -1,5 +1,13 @@
import { Logger, LogLabel, LevelLogLabel, colorRadio } from "./Logger"; import { Logger, LogLabel, LevelLogLabel, colorRadio } from "./Logger";
interface IAppStorageParam {
/**
* storage
*/
storage: Map<string, Storage<IStorageData>>
}
/** /**
* *
*/ */
@ -81,7 +89,8 @@ type IStorageData = {
* 1. wxStorage * 1. wxStorage
* 2. * 2.
* 3. 使 * 3. 使
* 4. * 4. storage
*
*/ */
class Storage<T extends IStorageData> { class Storage<T extends IStorageData> {
@ -103,18 +112,41 @@ class Storage<T extends IStorageData> {
/** /**
* *
*/ */
private cache:T; private _cache: T = {} as T;
private set cache(data: T) {
if (this.cacheStorage) {
for (const key in data) {
this.cacheStorage.cache[key] = data[key];
}
} else {
for (const key in data) {
this._cache[key] = data[key];
}
}
}
private get cache():T {
if (this.cacheStorage) return this.cacheStorage.cache;
else return this._cache;
}
/** /**
* cache storage * cache storage
*/ */
private saveWaiter:Waiter = new Waiter(); private saveWaiter:Waiter = new Waiter();
/**
*
*/
private cacheStorage:Storage<T> | undefined;
/** /**
* cache storage * cache storage
*/ */
public async save():Promise<void> { public async save():Promise<void> {
// 如果存在链接的实例
if (this.cacheStorage) return this.cacheStorage.save();
// 如果没有开始存储 // 如果没有开始存储
// 发起一次异步读取 // 发起一次异步读取
if(this.saveWaiter.state === StorageState.DONE) if(this.saveWaiter.state === StorageState.DONE)
@ -150,6 +182,24 @@ class Storage<T extends IStorageData> {
}); });
} }
/**
*
*/
private findStorageCache(): boolean {
let { storage: storageMap } = getApp<IAppStorageParam>();
// 查找缓存
let storage = storageMap.get(this.key);
if (storage) {
this.cacheStorage = storage as Storage<T>;
return true;
};
// 缓存此实例
storageMap.set(this.key, this);
return false;
}
/** /**
* *
*/ */
@ -164,13 +214,25 @@ class Storage<T extends IStorageData> {
/** /**
* @param defaultData * @param defaultData
*/ */
public constructor(key:string, defaultData:T) { public constructor(key:string, defaultData?:T) {
this.key = key; this.key = key;
this.defaultData = defaultData; this.defaultData = defaultData ?? {} as T;
this.StorageLogLabel = new LogLabel( this.StorageLogLabel = new LogLabel(
`Storage:${ this.key }`, colorRadio(34, 230, 258) `Storage:${ this.key }`, colorRadio(34, 230, 258)
); );
// 如果已找到其他实力,将此实例链接到目标实例
if (this.findStorageCache()) {
// 设置默认值
for (const key in this.defaultData) {
if (this.cache[key] === void 0) {
this.set(key, this.defaultData[key]);
}
}
return;
};
// 读取数据到缓存 // 读取数据到缓存
this.cache = wx.getStorageSync<T>(this.key); this.cache = wx.getStorageSync<T>(this.key);
@ -205,4 +267,4 @@ class Storage<T extends IStorageData> {
} }
export default Storage; export default Storage;
export { Storage, StorageState, Waiter }; export { Storage, StorageState, Waiter, IAppStorageParam, IStorageData };

View File

@ -1,205 +0,0 @@
// import { Logger } from "../logger/Logger";
import { LogStyle, LogLabel } from "./Logger";
/**
*
*/
class TestResult {
/**
*
*/
public caseName:string;
/**
*
*/
public result:boolean;
/**
*
*/
public message:string;
/**
*
*/
public attach:string;
/**
*
* @param caseName
*/
constructor(caseName:string) {
this.caseName = caseName;
this.result = false;
this.message = "";
this.attach = "";
}
/**
*
*/
public setResult(result:boolean, message?:string, attach?:string) {
this.result = result;
this.message = message ?? (result ? "success!" : "failed!");
this.attach = attach ?? this.attach;
return this;
}
}
/**
*
*/
type TestFunction = () => TestResult | Promise<TestResult>;
/**
*
*/
class CaseCollect {
/**
*
*/
public key:string;
/**
*
*/
public caseFunction:TestFunction;
/**
*
*/
result: Promise<TestResult> | undefined;
/**
* @param key
* @param caseFunction
*/
public constructor(key:string, caseFunction:TestFunction) {
this.key = key;
this.caseFunction = caseFunction;
}
/**
*
*/
public async runTestCase():Promise<CaseCollect> {
let result = this.caseFunction();
if(result instanceof Promise) {
this.result = result;
} else {
this.result = Promise.resolve(result);
}
return this;
}
public static readonly baseStyle = new LogStyle().setBlank();
public static readonly successLabel:LogLabel = new LogLabel("√",
new LogStyle().setBlank("0 4px").setBorder("1000px", "1px solid green")
);
/**
*
* @param current
* @param total
*/
public printResult(current?:number, total?:number) {
// 如果测试没有运行,先运行它
if(this.result === void 0) this.runTestCase();
this.result?.then((res) => {
if(res.result) {
console.log(
`%c√%c %c1/1%c %c${ this.key }%c ` + res.message,
"padding:0 4px; border-radius:1000px; border:1px solid green; color:green",
"", "padding:0 4px; border-radius:4px; border:1px solid green; color:green",
"", "padding:0 4px; border-radius:4px; border:1px solid #979797; color:#979797",
""
)
} else {
console.log(
`%c√%c %c1/1%c %c${ this.key }%c ` + res.message,
"padding:0 4px; border-radius:1000px; border:1px solid red; color:red",
"", "padding:0 4px; border-radius:4px; border:1px solid red; color:red",
"", "padding:0 4px; border-radius:4px; border:1px solid #979797; color:#979797",
""
)
}
console.log(res)
})
}
/**
*
* @param testCaseClass
*/
public static collectCase(testCaseClass:ITestCase):CaseCollect[] {
// 获取静态方法 key
let key = Object.getOwnPropertyNames(testCaseClass);
// 过滤掉通用的方法和属性
key = key.filter((key) => !/(length|name|prototype)/.test(key) );
// 生成 CaseCollect
let caseCollect = [];
for (let i = 0; i < key.length; i++) {
caseCollect.push(new CaseCollect(key[i], testCaseClass[key[i]]))
}
return caseCollect;
}
/**
*
*/
public static async runCollectCase(cases:CaseCollect[]):Promise<CaseCollect[]> {
let running:Promise<CaseCollect>[] = [];
for(let i = 0; i < cases.length; i++) {
running.push(cases[i].runTestCase());
}
return Promise.all(running);
}
/**
*
*/
public static runUnitTest(testCaseClass:ITestCase) {
let caseCollect = this.collectCase(testCaseClass);
CaseCollect.runCollectCase(caseCollect).then((caseCollect:CaseCollect[]) => {
for(let i = 0; i < caseCollect.length; i++) {
caseCollect[i].printResult()
}
})
}
}
/**
*
*/
interface ITestCase {
/**
*
*/
[key:string]:TestFunction;
}
export default ITestCase;
export { ITestCase, TestResult, TestFunction, CaseCollect };

View File

@ -4,7 +4,7 @@ import { Logger, LogLabel, LevelLogLabel, LifeCycleLogLabel, NormalStyle } from
/** /**
* UI * UI
*/ */
type DisplayData = { interface IDisplayData {
/** /**
* *
@ -20,8 +20,8 @@ type DisplayData = {
/** /**
* *
*/ */
type StatusBarEvent = { interface StatusBarEvent {
m: DisplayData m: IDisplayData
}; };
/** /**

View File

@ -8,13 +8,22 @@ import { Storage } from "../../core/Storage";
class TestCore<M extends Manager> extends Modular<M> class TestCore<M extends Manager> extends Modular<M>
implements Partial<ILifetime> { implements Partial<ILifetime> {
public onLoad() { public override onLoad() {
let s = new Storage("test", { let s = new Storage("test", {
a: new Date(), a: new Date(),
be: 2 be: 2
}); });
let s2 = new Storage("test", {
be: 1,
aa: "abc"
});
s2.set("be", 4);
console.log(s, s2);
setTimeout(() => { setTimeout(() => {
s.set("be", 12); s.set("be", 12);
}, 1000) }, 1000)
@ -55,9 +64,7 @@ implements Partial<ILifetime> {
} }
}).request().wait({ }).request().wait({
success: (d) => console.log(d) success: (d) => console.log(d)
}).wait({ })
success: (d) => console.log(d)
});
} }
} }