Skip to content

Commit 1181011

Browse files
Merge pull request #81 from huw/main
feat(zod-mock): Make union (incl. discriminated), enum (incl. native), and regex generation deterministic
2 parents 1dcbb03 + cceb675 commit 1181011

File tree

2 files changed

+52
-16
lines changed

2 files changed

+52
-16
lines changed

packages/zod-mock/src/lib/zod-mock.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,37 @@ describe('zod-mock', () => {
537537
expect(first).toEqual(second);
538538
});
539539

540+
it('Options seed value will return the same union & enum members', () => {
541+
enum NativeEnum {
542+
a = 1,
543+
b = 2,
544+
}
545+
546+
const schema = z.object({
547+
theme: z.enum([`light`, `dark`]),
548+
nativeEnum: z.nativeEnum(NativeEnum),
549+
union: z.union([z.literal('a'), z.literal('b')]),
550+
discriminatedUnion: z.discriminatedUnion('discriminator', [
551+
z.object({ discriminator: z.literal('a'), a: z.boolean() }),
552+
z.object({ discriminator: z.literal('b'), b: z.string() }),
553+
]),
554+
});
555+
const seed = 123;
556+
const first = generateMock(schema, { seed });
557+
const second = generateMock(schema, { seed });
558+
expect(first).toEqual(second);
559+
});
560+
561+
it('Options seed value will return the same generated regex values', () => {
562+
const schema = z.object({
563+
data: z.string().regex(/^[A-Z0-9+_.-]+@[A-Z0-9.-]+$/),
564+
});
565+
const seed = 123;
566+
const first = generateMock(schema, { seed });
567+
const second = generateMock(schema, { seed });
568+
expect(first).toEqual(second);
569+
});
570+
540571
// it.only('Can use my own version of faker', () => {
541572
// const schema = z.object({
542573
// name: z.string(),

packages/zod-mock/src/lib/zod-mock.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ function parseString(
119119
const regexCheck = checks.find((check) => check.kind === 'regex');
120120
if (regexCheck && 'regex' in regexCheck) {
121121
const generator = new randExp(regexCheck.regex);
122+
generator.randInt = (min: number, max: number) =>
123+
fakerInstance.datatype.number({ min, max });
122124
const max = checks.find((check) => check.kind === 'max');
123125
if (max && 'value' in max && typeof max.value === 'number') {
124126
generator.max = max.value;
@@ -319,30 +321,34 @@ function parseMap(zodRef: z.ZodMap<never>, options?: GenerateMockOptions) {
319321
return results;
320322
}
321323

322-
function parseEnum(zodRef: z.ZodEnum<never> | z.ZodNativeEnum<never>) {
324+
function parseEnum(
325+
zodRef: z.ZodEnum<never> | z.ZodNativeEnum<never>,
326+
options?: GenerateMockOptions
327+
) {
328+
const fakerInstance = options?.faker || faker;
323329
const values = zodRef._def.values as Array<z.infer<typeof zodRef>>;
324-
const pick = Math.floor(Math.random() * values.length);
325-
return values[pick];
330+
return fakerInstance.helpers.arrayElement(values);
326331
}
327332

328333
function parseDiscriminatedUnion(
329334
zodRef: z.ZodDiscriminatedUnion<never, any>,
330335
options?: GenerateMockOptions
331336
) {
337+
const fakerInstance = options?.faker || faker;
332338
// Map the options to various possible union cases
333339
const potentialCases = [...zodRef._def.options.values()];
334-
const pick = Math.floor(Math.random() * potentialCases.length);
335-
336-
const mocked = potentialCases[pick];
337-
340+
const mocked = fakerInstance.helpers.arrayElement(potentialCases);
338341
return generateMock(mocked, options);
339342
}
340343

341-
function parseNativeEnum(zodRef: z.ZodNativeEnum<never>) {
344+
function parseNativeEnum(
345+
zodRef: z.ZodNativeEnum<never>,
346+
options?: GenerateMockOptions
347+
) {
348+
const fakerInstance = options?.faker || faker;
342349
const { values } = zodRef._def;
343-
const pick = Math.floor(Math.random() * Object.values(values).length);
344-
const key = Array.from(Object.keys(values))[pick];
345-
return values[values[key]];
350+
const value = fakerInstance.helpers.arrayElement(values);
351+
return values[value];
346352
}
347353

348354
function parseLiteral(zodRef: z.ZodLiteral<any>) {
@@ -369,10 +375,9 @@ function parseUnion(
369375
) {
370376
const fakerInstance = options?.faker || faker;
371377
// Map the options to various possible mock values
372-
const mockOptions = zodRef._def.options.map((option) =>
373-
generateMock(option, options)
374-
);
375-
return fakerInstance.helpers.arrayElement(mockOptions);
378+
const potentialCases = [...zodRef._def.options.values()];
379+
const mocked = fakerInstance.helpers.arrayElement(potentialCases);
380+
return generateMock(mocked, options);
376381
}
377382

378383
function parseZodIntersection(
@@ -509,7 +514,7 @@ export interface GenerateMockOptions {
509514
throwOnUnknownType?: boolean;
510515

511516
/**
512-
* Set a seed for random generation within the mock library
517+
* Set a seed for random generation
513518
*/
514519
seed?: number | number[];
515520

0 commit comments

Comments
 (0)