Skip to content

Commit 1f8c00b

Browse files
MH4GFardeois
andauthored
feat: add generatorLibrary options and allow faker (#93)
Add a new option `generateLibrary` to make faker available in addition to the existing casual. Co-authored-by: Corentin Ardeois <[email protected]>
1 parent 701d67f commit 1f8c00b

File tree

12 files changed

+485
-21
lines changed

12 files changed

+485
-21
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ When disabled, underscores will be retained for type names when the case is chan
137137
138138
When enabled, values will be generated dynamically when the mock function is called rather than statically when the mock function is generated. The values are generated consistently from a [casual seed](https://github.com/boo1ean/casual#seeding) that can be manually configured using the generated `seedMocks(seed: number)` function, as shown in [this test](https://github.com/JimmyPaolini/graphql-codegen-typescript-mock-data/blob/dynamic-mode/tests/dynamicValues/spec.ts#L13).
139139
140+
### generateLibrary (`'casual' | 'faker'`, defaultValue: `'casual'`)
141+
142+
Select a library to generate mock values. The default is [casual](https://github.com/boo1ean/casual), Other options include [faker](https://github.com/faker-js/faker).
143+
casual dependents on Node API and cannot be executed in a browser. faker is useful when you want to use a mock function with the dynamicValues option enabled in the browser.
144+
140145
## Examples of usage
141146
142147
**codegen.yml**

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"fakes"
2323
],
2424
"dependencies": {
25+
"@faker-js/faker": "^7.5.0",
2526
"@graphql-codegen/plugin-helpers": "^2.4.1",
2627
"casual": "^1.6.2",
2728
"indefinite": "^2.4.1",
@@ -36,7 +37,6 @@
3637
"devDependencies": {
3738
"@auto-it/conventional-commits": "^10.33.0",
3839
"@graphql-codegen/testing": "^1.17.7",
39-
"@types/faker": "^5.5.9",
4040
"@types/jest": "^27.0.2",
4141
"@typescript-eslint/eslint-plugin": "^5.1.0",
4242
"@typescript-eslint/parser": "^5.1.0",

src/index.ts

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { pascalCase } from 'pascal-case';
55
import { upperCase } from 'upper-case';
66
import { sentenceCase } from 'sentence-case';
77
import a from 'indefinite';
8+
import { setupFunctionTokens, setupMockValueGenerator } from './mockValueGenerator';
89

910
type NamingConvention = 'upper-case#upperCase' | 'pascal-case#pascalCase' | 'keep';
1011

@@ -23,6 +24,7 @@ type Options<T = TypeNode> = {
2324
transformUnderscore: boolean;
2425
listElementCount: number;
2526
dynamicValues: boolean;
27+
generateLibrary: 'casual' | 'faker';
2628
};
2729

2830
const convertName = (value: string, fn: (v: string) => string, transformUnderscore: boolean): string => {
@@ -104,22 +106,24 @@ const getNamedType = (opts: Options<NamedTypeNode>): string | number | boolean =
104106
return '';
105107
}
106108

107-
if (!opts.dynamicValues) casual.seed(hashedString(opts.typeName + opts.fieldName));
109+
const mockValueGenerator = setupMockValueGenerator({
110+
generateLibrary: opts.generateLibrary,
111+
dynamicValues: opts.dynamicValues,
112+
});
113+
if (!opts.dynamicValues) mockValueGenerator.seed(hashedString(opts.typeName + opts.fieldName));
108114
const name = opts.currentType.name.value;
109115
const casedName = createNameConverter(opts.typenamesConvention, opts.transformUnderscore)(name);
110116
switch (name) {
111117
case 'String':
112-
return opts.dynamicValues ? `casual.word` : `'${casual.word}'`;
118+
return mockValueGenerator.word();
113119
case 'Float':
114-
return opts.dynamicValues
115-
? `Math.round(casual.double(0, 10) * 100) / 100`
116-
: Math.round(casual.double(0, 10) * 100) / 100;
120+
return mockValueGenerator.float();
117121
case 'ID':
118-
return opts.dynamicValues ? `casual.uuid` : `'${casual.uuid}'`;
122+
return mockValueGenerator.uuid();
119123
case 'Boolean':
120-
return opts.dynamicValues ? `casual.boolean` : casual.boolean;
124+
return mockValueGenerator.boolean();
121125
case 'Int':
122-
return opts.dynamicValues ? `casual.integer(0, 9999)` : casual.integer(0, 9999);
126+
return mockValueGenerator.integer();
123127
default: {
124128
const foundType = opts.types.find((enumType: TypeItem) => enumType.name === name);
125129
if (foundType) {
@@ -151,11 +155,9 @@ const getNamedType = (opts: Options<NamedTypeNode>): string | number | boolean =
151155
// mapping for this particular scalar
152156
if (!customScalar || !customScalar.generator) {
153157
if (foundType.name === 'Date') {
154-
return opts.dynamicValues
155-
? `new Date(casual.unix_time).toISOString()`
156-
: `'${new Date(casual.unix_time).toISOString()}'`;
158+
return mockValueGenerator.date();
157159
}
158-
return opts.dynamicValues ? `casual.word` : `'${casual.word}'`;
160+
return mockValueGenerator.word();
159161
}
160162

161163
// If there is a mapping to a `casual` type, then use it and make sure
@@ -332,6 +334,7 @@ export interface TypescriptMocksPluginConfig {
332334
transformUnderscore?: boolean;
333335
listElementCount?: number;
334336
dynamicValues?: boolean;
337+
generateLibrary?: 'casual' | 'faker';
335338
}
336339

337340
interface TypeItem {
@@ -372,6 +375,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
372375
const transformUnderscore = config.transformUnderscore ?? true;
373376
const listElementCount = config.listElementCount > 0 ? config.listElementCount : 1;
374377
const dynamicValues = !!config.dynamicValues;
378+
const generateLibrary = config.generateLibrary || 'casual';
375379
// List of types that are enums
376380
const types: TypeItem[] = [];
377381
const visitor: VisitorType = {
@@ -416,6 +420,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
416420
transformUnderscore,
417421
listElementCount,
418422
dynamicValues,
423+
generateLibrary,
419424
});
420425

421426
return ` ${fieldName}: overrides && overrides.hasOwnProperty('${fieldName}') ? overrides.${fieldName}! : ${value},`;
@@ -446,6 +451,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
446451
transformUnderscore,
447452
listElementCount,
448453
dynamicValues,
454+
generateLibrary,
449455
});
450456

451457
return ` ${field.name.value}: overrides && overrides.hasOwnProperty('${field.name.value}') ? overrides.${field.name.value}! : ${value},`;
@@ -541,13 +547,14 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
541547
.filter((mockFn: () => string) => !!mockFn)
542548
.map((mockFn: () => string) => mockFn())
543549
.join('\n');
550+
const functionTokens = setupFunctionTokens(generateLibrary);
544551

545552
let mockFile = '';
546-
if (dynamicValues) mockFile += "import casual from 'casual';\n";
553+
if (dynamicValues) mockFile += `${functionTokens.import}\n`;
547554
mockFile += typesFileImport;
548-
if (dynamicValues) mockFile += '\ncasual.seed(0);\n';
555+
if (dynamicValues) mockFile += `\n${functionTokens.seed}\n`;
549556
mockFile += mockFns;
550-
if (dynamicValues) mockFile += '\n\nexport const seedMocks = (seed: number) => casual.seed(seed);';
557+
if (dynamicValues) mockFile += `\n\n${functionTokens.seedFunction}`;
551558
mockFile += '\n';
552559
return mockFile;
553560
};

src/mockValueGenerator.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { faker } from '@faker-js/faker';
2+
import casual from 'casual';
3+
4+
interface MockValueGenerator {
5+
dynamicValues: boolean;
6+
word: () => string;
7+
uuid: () => string;
8+
boolean: () => boolean | string;
9+
integer: () => number | string;
10+
float: () => number | string;
11+
date: () => string;
12+
seed: (seed: number) => void;
13+
}
14+
15+
type MockValueGeneratorOptions = {
16+
dynamicValues: boolean;
17+
};
18+
19+
type FunctionTokens = Record<'import' | 'seed' | 'seedFunction', string>;
20+
21+
type SetupMockValueGeneratorOptions = {
22+
generateLibrary: 'casual' | 'faker';
23+
dynamicValues: boolean;
24+
};
25+
26+
class CasualMockValueGenerator implements MockValueGenerator {
27+
dynamicValues: boolean;
28+
29+
constructor(opts: MockValueGeneratorOptions) {
30+
this.dynamicValues = opts.dynamicValues;
31+
}
32+
33+
word = () => (this.dynamicValues ? `casual.word` : `'${casual.word}'`);
34+
uuid = () => (this.dynamicValues ? `casual.uuid` : `'${casual.uuid}'`);
35+
boolean = () => (this.dynamicValues ? `casual.boolean` : casual.boolean);
36+
integer = () => (this.dynamicValues ? `casual.integer(0, 9999)` : `${casual.integer(0, 9999)}`);
37+
float = () =>
38+
this.dynamicValues
39+
? `Math.round(casual.double(0, 10) * 100) / 100`
40+
: `${Math.round(casual.double(0, 10) * 100) / 100}`;
41+
date = () =>
42+
this.dynamicValues
43+
? `new Date(casual.unix_time).toISOString()`
44+
: `'${new Date(casual.unix_time).toISOString()}'`;
45+
seed = (seed: number) => casual.seed(seed);
46+
}
47+
48+
const casualFunctionTokens: FunctionTokens = {
49+
import: `import casual from 'casual';`,
50+
seed: 'casual.seed(0);',
51+
seedFunction: 'export const seedMocks = (seed: number) => casual.seed(seed);',
52+
};
53+
54+
class FakerMockValueGenerator implements MockValueGenerator {
55+
dynamicValues: boolean;
56+
57+
constructor(opts: MockValueGeneratorOptions) {
58+
this.dynamicValues = opts.dynamicValues;
59+
}
60+
61+
word = () => (this.dynamicValues ? `faker.lorem.word()` : `'${faker.lorem.word()}'`);
62+
uuid = () => (this.dynamicValues ? `faker.datatype.uuid()` : `'${faker.datatype.uuid()}'`);
63+
boolean = () => (this.dynamicValues ? `faker.datatype.boolean()` : faker.datatype.boolean());
64+
integer = () =>
65+
this.dynamicValues
66+
? `faker.datatype.number({ min: 0, max: 9999 })`
67+
: faker.datatype.number({ min: 0, max: 9999 });
68+
float = () =>
69+
this.dynamicValues
70+
? `faker.datatype.float({ min: 0, max: 10, precision: 0.1 })`
71+
: faker.datatype.float({ min: 0, max: 10, precision: 0.1 });
72+
date = () =>
73+
this.dynamicValues
74+
? `faker.date.past().toISOString(1, new Date(2022, 0))`
75+
: `'${faker.date.past(1, new Date(2022, 0)).toISOString()}'`;
76+
seed = (seed: number) => faker.seed(seed);
77+
}
78+
79+
const fakerFunctionTokens: FunctionTokens = {
80+
import: `import { faker } from '@faker-js/faker';`,
81+
seed: 'faker.seed(0);',
82+
seedFunction: 'export const seedMocks = (seed: number) => faker.seed(seed);',
83+
};
84+
85+
export const setupMockValueGenerator = ({
86+
generateLibrary,
87+
dynamicValues,
88+
}: SetupMockValueGeneratorOptions): MockValueGenerator => {
89+
switch (generateLibrary) {
90+
case 'casual':
91+
return new CasualMockValueGenerator({ dynamicValues });
92+
case 'faker':
93+
return new FakerMockValueGenerator({ dynamicValues });
94+
}
95+
};
96+
97+
export const setupFunctionTokens = (generateLibrary: 'casual' | 'faker'): FunctionTokens => {
98+
switch (generateLibrary) {
99+
case 'casual':
100+
return casualFunctionTokens;
101+
case 'faker':
102+
return fakerFunctionTokens;
103+
}
104+
};

0 commit comments

Comments
 (0)