UNPKG

typemoq

Version:

A simple mocking library for TypeScript

204 lines (167 loc) 7.26 kB
import * as _ from "lodash"; import * as common from "../Common/_all"; import * as error from "../Error/_all"; import { Consts } from "../Consts"; import { IProxy } from "./IProxy"; import { ICallContext } from "./ICallContext"; import { ICallInterceptor } from "./ICallInterceptor"; import { PropertyInfo, MethodInfo, MethodInvocation, MethodGetterInvocation, MethodSetterInvocation, ValueGetterInvocation, ValueSetterInvocation } from "./Invocation"; export class ProxyES5<T> implements IProxy { readonly ___id = Consts.IPROXY_ID_VALUE; private constructor(target: T, interceptor: ICallInterceptor) { this.check(target); let that = this; let props = common.PropertyRetriever.getOwnAndPrototypeEnumerablesAndNonenumerables(target); _.each(props, (prop: { name: string; desc: common.PropDescriptor }) => { if (_.isFunction(prop.desc.value)) { let propDesc: common.PropDescriptor = { configurable: prop.desc.configurable, enumerable: prop.desc.enumerable, writable: prop.desc.writable }; this.defineMethodProxy(that, interceptor, target, prop.name, propDesc); } else { let propDesc: common.PropDescriptor = { configurable: prop.desc.configurable, enumerable: prop.desc.enumerable }; if (prop.desc.value !== undefined) this.defineValuePropertyProxy(that, interceptor, target, prop.name, prop.desc.value, propDesc); else this.defineGetSetPropertyProxy(that, interceptor, target, prop.name, prop.desc.get, prop.desc.set, propDesc); } }); } static of<U>(target: U, interceptor: ICallInterceptor): ProxyES5<U> { ProxyES5.check(target); let result: any; if (_.isFunction(target)) { let funcName = common.Utils.functionName(target); result = ProxyES5.methodProxyValue(undefined, interceptor, target, funcName, null); } else { result = new ProxyES5(target, interceptor); } return result; } static isProxy(obj: any): boolean { if (!_.isNil(obj) && !_.isUndefined(obj[Consts.IPROXY_ID_NAME]) && obj[Consts.IPROXY_ID_NAME] === Consts.IPROXY_ID_VALUE) return true; else return false; } private static check<U>(target: U): void { ProxyES5.checkNotNullOrUndefined(target); // allow only primitive objects and functions let ok = false; if (_.isFunction(target) || (_.isObject(target) && !ProxyES5.isPrimitiveObject(target))) ok = true; if (!ok) throw new error.MockException(error.MockExceptionReason.InvalidArg, target, `'${target}'; proxy argument should be a function or a non primitive object`); } private check<U>(target: U): void { ProxyES5.checkNotNullOrUndefined(target); // allow only non primitive objects let ok = false; if (!_.isFunction(target) && (_.isObject(target) && !ProxyES5.isPrimitiveObject(target))) ok = true; if (!ok) throw new error.MockException(error.MockExceptionReason.InvalidArg, target, `'${target}'; proxy argument should be a non primitive object`); } private static checkNotNullOrUndefined<U>(instance: U): void { if (_.isNil(instance)) throw new error.MockException(error.MockExceptionReason.InvalidArg, instance, `'${instance}'; proxy argument is required`); } private static isPrimitiveObject(obj: Object): boolean { let result = false; if (_.isFunction(obj) || _.isArray(obj) || _.isDate(obj) || _.isNull(obj)) result = true; return result; } private defineMethodProxy( that: Object, interceptor: ICallInterceptor, target: T, propName: string, propDesc: common.PropDescriptor = { configurable: true, enumerable: true, writable: false }) { propDesc.value = ProxyES5.methodProxyValue(that, interceptor, target, propName, propDesc); this.defineProperty(that, propName, propDesc); } private static methodProxyValue<U>( that: Object, interceptor: ICallInterceptor, target: U, propName: string, propDesc: common.PropDescriptor): () => any { function proxy() { let method = new MethodInfo(target, propName, propDesc); let invocation: ICallContext = new MethodInvocation(that, method, arguments); interceptor.intercept(invocation); return invocation.returnValue; } return proxy; } private defineValuePropertyProxy( that: Object, interceptor: ICallInterceptor, target: T, propName: string, propValue: any, propDesc: common.PropDescriptor = { configurable: true, enumerable: true }) { function getProxy(): any { let method = new PropertyInfo(target, propName); let invocation: ICallContext = new ValueGetterInvocation(method, propValue); interceptor.intercept(invocation); return invocation.returnValue; } propDesc.get = getProxy; function setProxy(v: any): void { let method = new PropertyInfo(target, propName); let invocation: ICallContext = new ValueSetterInvocation(method, arguments); interceptor.intercept(invocation); } propDesc.set = setProxy; this.defineProperty(that, propName, propDesc); } private defineGetSetPropertyProxy( that: Object, interceptor: ICallInterceptor, target: T, propName: string, get?: () => any, set?: (v: any) => void, propDesc: common.PropDescriptor = { configurable: true, enumerable: true }) { function getProxy(): any { let method = new PropertyInfo(target, propName); let invocation: ICallContext = new MethodGetterInvocation(method, get); interceptor.intercept(invocation); return invocation.returnValue; } propDesc.get = getProxy; function setProxy(v: any): void { let method = new PropertyInfo(target, propName); let invocation: ICallContext = new MethodSetterInvocation(method, set, arguments); interceptor.intercept(invocation); } propDesc.set = setProxy; this.defineProperty(that, propName, propDesc); } private defineProperty(obj: Object, name: string, desc: common.PropDescriptor) { try { Object.defineProperty(obj, name, desc); } catch (e) { console.log(e.message); } } }