airbridge-react-native-sdk
Version:
Airbridge SDK for React Native
113 lines (95 loc) • 3.68 kB
text/typescript
type ArrowFunction<Return = any, Parameters extends any[] = any[]> = (...args: Parameters) => Return
type And<Condition0 extends boolean, Condition1 extends boolean> = Condition0 extends true ? (Condition1 extends true ? true : false) : false
type Not<Condition extends boolean> = Condition extends true ? false : true
type IsAny<Type> = (any extends Type ? true : false) extends true ? true : false
type Is<Type, Expect> = Not<IsAny<Type>> extends true ? (Type extends Expect ? true : false) : false
type Opt<Type, Condition extends boolean> = Condition extends true ? Type : never
type Cast<Type, Condition> = Type extends Condition ? Type : never
type PropertyValue<Type> = {
[Key in keyof Type as Opt<Key, Not<Is<Type[Key], object | ArrowFunction>>>]: Type[Key]
}
type PropertyObject<Type> = {
[Key in keyof Type as Opt<Key, And<Is<Type[Key], object>, Not<Is<Type[Key], ArrowFunction>>>>]: Type[Key]
}
type Interface<Type> = {
[Key in keyof Type as Opt<Key, Is<Type[Key], ArrowFunction>>]: Cast<Type[Key], ArrowFunction>
}
type MockPropertyValue<Type> = PropertyValue<Type> & {
readonly [Key in 'getMock' as Opt<Key, Is<keyof PropertyValue<Type>, string>>]: {
readonly [Key in keyof PropertyValue<Type>]: jest.Mock<PropertyValue<Type>[Key], []>
}
} & {
readonly [Key in 'setMock' as Opt<Key, Is<keyof PropertyValue<Type>, string>>]: {
readonly [Key in keyof PropertyValue<Type>]: jest.Mock<void, [PropertyValue<Type>[Key]]>
}
}
type MockPropertyObject<Type> = {
readonly [Key in keyof PropertyObject<Type>]: Mock<PropertyObject<Type>[Key]>
}
type MockInterface<Type> = {
readonly [Key in keyof Interface<Type>]: jest.Mock<ReturnType<Interface<Type>[Key]>, Parameters<Interface<Type>[Key]>>
}
export type Mock<Type> = MockPropertyValue<Type> & MockPropertyObject<Type> & MockInterface<Type>
export const createMock = <Type>(): Mock<Type> => {
const values: Record<string, any> = {}
const mocks: Record<string, any> = {}
const prepareValue = (name: string) => {
if (!(name in values)) {
values[name] = {
value: undefined,
getMock: jest.fn(() => {
return values[name].value
}),
setMock: jest.fn((value) => {
values[name].value = value
}),
}
}
}
const getMock = new Proxy({}, {
get: (_, name: string) => {
prepareValue(name)
return values[name].getMock
},
set: () => {
return false
},
})
const setMock = new Proxy({}, {
get: (_, name: string) => {
prepareValue(name)
return values[name].setMock
},
set: () => {
return false
},
})
return new Proxy(jest.fn(), {
apply: (call: any, _, argument) => {
return call(...argument)
},
get: (call: any, name: string) => {
if (name in call || name === 'calls') {
return call[name]
}
if (name in values) {
return values[name].getMock()
}
if (name === 'getMock') {
return getMock
}
if (name === 'setMock') {
return setMock
}
if (!(name in mocks)) {
mocks[name] = createMock()
}
return mocks[name]
},
set: (_, name: string, value: any) => {
prepareValue(name)
values[name].setMock(value)
return true
},
}) as any as Mock<Type>
}