1+ const labelSymbol : unique symbol = Symbol ( 'mockProxyType' ) ;
12const defaultPropsSymbol : unique symbol = Symbol ( 'mockProxyDefaultProps' ) ;
23const overridesSymbol : unique symbol = Symbol ( 'mockProxyOverrides' ) ;
34const proxiesSymbol : unique symbol = Symbol ( 'mockProxyProxies' ) ;
45
56export const MockProxySymbol = {
7+ labelSymbol,
68 defaultProps : defaultPropsSymbol ,
79 overrides : overridesSymbol ,
810 proxies : proxiesSymbol ,
@@ -29,32 +31,53 @@ const mockProxyDefaultProps = {
2931 * The proxy target contains state + configuration for the proxy
3032 */
3133export interface MockProxyTarget < T > {
34+ [ MockProxySymbol . labelSymbol ] : string ;
3235 [ MockProxySymbol . defaultProps ] : typeof mockProxyDefaultProps ;
3336 [ MockProxySymbol . overrides ] : Partial < T > ;
3437 [ MockProxySymbol . proxies ] : Record < keyof T , jest . Mock > ;
3538}
3639
40+ export interface MockProxyConfig {
41+ // Optional label to be assigned to the proxy object's
42+ // `MockProxySymbol.labelSymbol` property.
43+ label ?: string ;
44+
45+ // `ownKeys` has no way to know all of the potential auto proxy keys, but it
46+ // can know auto proxies that have been called / cached. If this flag is true,
47+ // include those in the `ownKeys` result. This is mostly useful for spread
48+ // operations. Alternatively, the `overrides` are can explicitly include any
49+ // proxies to be included in the `ownKeys` result without setting this flag.
50+ // e.g. createMockProxy({ someMethod: jest.fn() }) would include `someMethod`.
51+ includeAutoProxiesInOwnKeys ?: boolean ;
52+ }
53+
3754/**
3855 * Creates a mock object for a type `T` using a Proxy object. Each prop can
3956 * optionally be set via the constructor. Any prop that is not set will be set
4057 * to a jest.fn() instance on first access with the exeption of "then" which
4158 * will not be automatically proxied.
4259 * @param overrides Optional props to explicitly set on the Proxy.
43- * @returns
60+ * @param config Optional configuration for the proxy.
61+ * @returns A mock Proxy object for type `T`.
4462 */
4563export default function createMockProxy < T > (
46- overrides : Partial < T > = { }
64+ overrides : Partial < T > = { } ,
65+ {
66+ label = 'Mock Proxy' ,
67+ includeAutoProxiesInOwnKeys = false ,
68+ } : MockProxyConfig = { }
4769) : T & MockProxyTarget < T > {
4870 const targetDef : MockProxyTarget < T > = {
71+ [ MockProxySymbol . labelSymbol ] : label ,
4972 [ MockProxySymbol . defaultProps ] : mockProxyDefaultProps ,
5073 [ MockProxySymbol . overrides ] : overrides ,
5174 [ MockProxySymbol . proxies ] : { } as Record < keyof T , jest . Mock > ,
5275 } ;
5376
5477 return new Proxy ( targetDef , {
5578 get ( target , name ) {
56- if ( name === Symbol . toStringTag ) {
57- return 'Mock Proxy' ;
79+ if ( name === Symbol . toStringTag || name === MockProxySymbol . labelSymbol ) {
80+ return targetDef [ MockProxySymbol . labelSymbol ] ;
5881 }
5982
6083 // Reserved attributes for the proxy
@@ -92,5 +115,25 @@ export default function createMockProxy<T>(
92115 has ( target , name ) {
93116 return name in target [ MockProxySymbol . overrides ] ;
94117 } ,
118+ // Needed to support the spread (...) operator
119+ getOwnPropertyDescriptor ( _target , _prop ) {
120+ return { configurable : true , enumerable : true } ;
121+ } ,
122+ // Needed to support the spread (...) operator
123+ ownKeys ( target ) {
124+ const autoProxyKeys = includeAutoProxiesInOwnKeys
125+ ? Reflect . ownKeys ( target [ MockProxySymbol . proxies ] )
126+ : [ ] ;
127+
128+ const overridesKeys = Reflect . ownKeys ( target [ MockProxySymbol . overrides ] ) ;
129+
130+ return [
131+ ...new Set < string | symbol > ( [
132+ MockProxySymbol . labelSymbol ,
133+ ...autoProxyKeys ,
134+ ...overridesKeys ,
135+ ] ) ,
136+ ] ;
137+ } ,
95138 } ) as T & typeof targetDef ;
96139}
0 commit comments