-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(class-mock): add faker all api to decorator
- Loading branch information
1 parent
342a123
commit 083710d
Showing
10 changed files
with
416 additions
and
252 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -138,6 +138,7 @@ | |
"jsdelivr", | ||
"jsfiddle", | ||
"loglevel", | ||
"mersenne", | ||
"messageoption", | ||
"Metadatas", | ||
"Metas", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export const fakeProps = [ | ||
'mersenne', | ||
'random', | ||
'helpers', | ||
'datatype', | ||
'address', | ||
'animal', | ||
'commerce', | ||
'company', | ||
'database', | ||
'date', | ||
'finance', | ||
'git', | ||
'hacker', | ||
'image', | ||
'internet', | ||
'lorem', | ||
'music', | ||
'name', | ||
'phone', | ||
'system', | ||
'time', | ||
'vehicle', | ||
'word' | ||
] as const |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import {mergeConfig, getTarget} from '@/utils/common' | ||
import {MetadataStorage} from '@/utils/meta-storage' | ||
import { | ||
MockPropertyDecoratorConfig, | ||
MockPropertyMetadata, | ||
MockPropertyDecorator, | ||
ArrayConfig, | ||
MockPropertyDecoratorProps | ||
} from '@/utils/types-helper' | ||
|
||
export function ConfigDecorator<T extends MockPropertyDecoratorConfig = MockPropertyDecoratorConfig>( | ||
config: T = <T>{} | ||
): MockPropertyDecorator<T> { | ||
const createConfigDecorator = (config: T = <T>{}) => { | ||
const decorator = (target: any, propertyKey: string | symbol) => { | ||
const propertyName = propertyKey as string | ||
const _target = getTarget(target) | ||
const preMetadata = MetadataStorage.instance.findMockMetadata(_target, propertyName) | ||
const metadata: MockPropertyMetadata = mergeConfig(preMetadata, { | ||
target: _target, | ||
propertyName, | ||
...config | ||
}) | ||
|
||
MetadataStorage.instance.addMockMetadata(metadata) | ||
} | ||
|
||
const createMergeConfigDecorator = (newConfig: T = <T>{}) => createConfigDecorator(mergeConfig(config, newConfig)) | ||
|
||
const decoratorProto: MockPropertyDecoratorProps<T> = { | ||
config: (_config: T = <T>{}) => createMergeConfigDecorator(_config), | ||
isPartial: () => createMergeConfigDecorator(<T>{partial: 'partial'}), | ||
isInclude: () => createMergeConfigDecorator(<T>{partial: 'include'}), | ||
isExclude: () => createMergeConfigDecorator(<T>{partial: 'exclude'}), | ||
isAlwaysRandom: () => createMergeConfigDecorator(<T>{alwaysRandom: true}), | ||
isNotAlwaysRandom: () => createMergeConfigDecorator(<T>{alwaysRandom: false}), | ||
isArray: (arrayConfig?: ArrayConfig) => createMergeConfigDecorator(<T>{array: true, ...arrayConfig}), | ||
isNotArray: () => createMergeConfigDecorator(<T>{array: false}), | ||
groups: (groups: string[]) => createMergeConfigDecorator(<T>{groups}) | ||
} | ||
|
||
Object.assign(decorator, decoratorProto) | ||
|
||
return decorator as MockPropertyDecorator<T> | ||
} | ||
|
||
return createConfigDecorator(config) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,52 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import {MetadataStorage} from './../meta-storage' | ||
import {FakerPropertyDecoratorConfig, FakerPropertyMetadata, Fn, MockPropertyDecorator} from './../utils/types-helper' | ||
import {fakeProps} from '@/constants/faker.constants' | ||
import {Fn, MockPropertyDecorator, MockPropertyDecoratorConfig} from '@/utils/types-helper' | ||
import faker, {Faker} from '@faker-js/faker' | ||
import {MockDecorator} from './mock.decorator' | ||
|
||
export function FakerDecorator<T extends Fn>(fakerFn: T, ...fakerParams: Parameters<T>) { | ||
function decorator(target: any, propertyKey: string, config: FakerPropertyDecoratorConfig = {}) { | ||
const metadata: FakerPropertyMetadata<T> = { | ||
target: target instanceof Function ? target : target.constructor, | ||
propertyName: propertyKey, | ||
fakerFn, | ||
fakerParams, | ||
...config | ||
} | ||
export type FakeProp = typeof fakeProps[number] | ||
export type MockFaker = Pick<Faker, FakeProp> | ||
const createFakeProxy = <T extends keyof MockFaker, KeyFns extends keyof MockFaker[T]>(fakeKey: T) => { | ||
const mockFaker = faker as MockFaker | ||
|
||
MetadataStorage.instance.addFakerMetadata(metadata) | ||
} | ||
// fix Parameter params only allow function | ||
type MockFnParameters<T> = T extends (...args: infer P) => any ? P : never | ||
|
||
decorator.config = | ||
(config: FakerPropertyDecoratorConfig = {}) => | ||
(target: any, propertyKey: string) => | ||
decorator(target, propertyKey, config) | ||
// return's type | ||
type MockProxy = { | ||
[key in KeyFns]: ( | ||
...params: MockFnParameters<MockFaker[T][key]> | ||
) => MockPropertyDecorator<MockPropertyDecoratorConfig> | ||
} | ||
|
||
return decorator as MockPropertyDecorator<FakerPropertyDecoratorConfig> | ||
return new Proxy(faker[fakeKey], { | ||
get(target, targetKey: string) { | ||
const mockFn = mockFaker[fakeKey][targetKey as KeyFns] as unknown as Fn | ||
return (...params: any[]) => MockDecorator(mockFn, ...params) | ||
} | ||
}) as unknown as MockProxy | ||
} | ||
|
||
export const Mersenne = createFakeProxy('mersenne') | ||
export const Random = createFakeProxy('random') | ||
export const Helpers = createFakeProxy('helpers') | ||
export const Datatype = createFakeProxy('datatype') | ||
export const Address = createFakeProxy('address') | ||
export const Animal = createFakeProxy('animal') | ||
export const Commerce = createFakeProxy('commerce') | ||
export const Company = createFakeProxy('company') | ||
export const Database = createFakeProxy('database') | ||
export const Date = createFakeProxy('date') | ||
export const Finance = createFakeProxy('finance') | ||
export const Git = createFakeProxy('git') | ||
export const Hacker = createFakeProxy('hacker') | ||
export const Image = createFakeProxy('image') | ||
export const Internet = createFakeProxy('internet') | ||
export const Lorem = createFakeProxy('lorem') | ||
export const Music = createFakeProxy('music') | ||
export const Name = createFakeProxy('name') | ||
export const Phone = createFakeProxy('phone') | ||
export const System = createFakeProxy('system') | ||
export const Time = createFakeProxy('time') | ||
export const Vehicle = createFakeProxy('vehicle') | ||
export const Word = createFakeProxy('word') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import {MockPropertyMetadata, Fn, MockPropertyDecorator, MockPropertyDecoratorConfig} from '@/utils/types-helper' | ||
import {ConfigDecorator} from './config.decorator' | ||
|
||
export function MockDecorator<T extends Fn>( | ||
mockFn: T, | ||
...mockParams: Parameters<T> | ||
): MockPropertyDecorator<MockPropertyDecoratorConfig> { | ||
return ConfigDecorator<MockPropertyDecoratorConfig>({ | ||
mockFn, | ||
mockParams | ||
} as MockPropertyMetadata<T>) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,39 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import 'reflect-metadata' | ||
import faker from '@faker-js/faker' | ||
import {MetadataStorage} from './meta-storage' | ||
import {FakerDecorator} from './decorators/faker.decorator' | ||
import {valueIsFakerMeta} from './utils/common' | ||
import {Random, Name, Phone, Address} from './decorators/faker.decorator' | ||
import {createMock} from './utils/create-mock' | ||
|
||
faker.setLocale('zh_CN') | ||
|
||
class User { | ||
@FakerDecorator(faker.name.firstName, 'male').config({ | ||
partial: true | ||
}) | ||
@Name.firstName() | ||
name!: string | ||
|
||
@FakerDecorator(faker.random.number, {min: 0, max: 100}).config({ | ||
alwaysRandom: true | ||
}) | ||
@Random.number({min: 0, max: 100}) | ||
age!: number | ||
} | ||
|
||
class Student extends User { | ||
@FakerDecorator(faker.random.words, 5).config({ | ||
array: true, | ||
length: 3 | ||
}) | ||
favorites!: string[] | ||
} | ||
@Address.streetAddress().isArray({length: 3}) | ||
address!: string[] | ||
|
||
function createMock(Entity: any) { | ||
const metas = MetadataStorage.instance.getClassMetadatas(Entity) | ||
const entity = new Entity() | ||
metas.map(meta => { | ||
const {array = false, length, min, max, propertyName} = meta | ||
if (valueIsFakerMeta(meta)) { | ||
const {fakerFn, fakerParams} = meta | ||
let propertyValue: any | ||
if (!array) { | ||
propertyValue = fakerFn(...fakerParams) | ||
} else { | ||
const arrayLength = | ||
(length ?? min ?? max ?? undefined) === undefined | ||
? 10 | ||
: length ?? faker.random.number({min: min ?? 0, max: max ?? 100}) | ||
propertyValue = Array.from({length: arrayLength}, () => fakerFn(...fakerParams)) | ||
} | ||
entity[propertyName] = propertyValue | ||
} | ||
}) | ||
return entity | ||
@Phone.phoneNumber('188########') | ||
tel!: number | ||
} | ||
|
||
const mockStudent = createMock(Student) | ||
|
||
console.log('mockStudent: ', mockStudent) | ||
|
||
// 比如提供一个 webpack 插件或者 vite 插件,配置一下,axios 或 fetch 访问该 url 就可以自动响应 mock 数据了 | ||
// const serverPluginConfig = [ | ||
// { | ||
// url: '/api/users', | ||
// res: () => createMock(Student, {array: true}) | ||
// }, | ||
// { | ||
// url: '/api/user/:id', | ||
// res: () => createMock(Student, {array: false}) | ||
// } | ||
// ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,36 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import {FakerPropertyMetadata} from './types-helper' | ||
import {ArrayConfig, BasePropertyConfig, MetadataTarget} from './types-helper' | ||
|
||
export function valueIsFakerMeta(value: any): value is FakerPropertyMetadata { | ||
return value && typeof value.fakerFn === 'function' | ||
export function getTarget(target: any): MetadataTarget { | ||
return target instanceof Function ? target : target.constructor | ||
} | ||
|
||
export function randomNumber(min: number, max: number): number { | ||
if (min === max) return min | ||
return Math.floor(Math.random() * (max - min + 1)) + min | ||
} | ||
|
||
export interface getGenerateArrayLengthOptions extends ArrayConfig { | ||
defaultLength?: number | ||
defaultMax?: number | ||
defaultMin?: number | ||
} | ||
|
||
export function getGenerateArrayLength(options: getGenerateArrayLengthOptions): number { | ||
const {length, min, max, defaultLength = 10, defaultMax = 50, defaultMin = 0} = options | ||
const arrayLength = | ||
(length ?? min ?? max ?? undefined) === undefined | ||
? defaultLength | ||
: length ?? randomNumber(min ?? defaultMin, max ?? defaultMax) | ||
return arrayLength | ||
} | ||
|
||
export function mergeConfig<Config extends BasePropertyConfig>( | ||
oldMeta: Config | undefined, | ||
newMeta: Config | undefined | ||
) { | ||
return { | ||
...(oldMeta || {}), | ||
...(newMeta || {}) | ||
} as Config | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import {getGenerateArrayLength} from './common' | ||
import {MetadataStorage} from './meta-storage' | ||
|
||
export function createMock(Entity: any) { | ||
const metas = MetadataStorage.instance.getClassMetadatas(Entity) | ||
const entity = new Entity() | ||
metas.map(meta => { | ||
const {array = false, length, min, max, propertyName, mockFn, mockParams = []} = meta | ||
if (typeof mockFn === 'function') { | ||
let propertyValue: unknown | ||
if (!array) { | ||
propertyValue = mockFn(...mockParams) | ||
} else { | ||
const arrayLength = getGenerateArrayLength({length, min, max}) | ||
propertyValue = Array.from({length: arrayLength}, () => mockFn(...mockParams)) | ||
} | ||
entity[propertyName] = propertyValue | ||
} | ||
}) | ||
return entity | ||
} |
Oops, something went wrong.