Skip to content
Merged
46 changes: 41 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ Changes the case of the enums. Accepts `upper-case#upperCase`, `pascal-case#pasc

Allows you to define mappings for your custom scalars. Allows you to map any GraphQL Scalar to a
[casual](https://github.com/boo1ean/casual#embedded-generators) embedded generator (string or
function key) with optional arguments
function key) with optional arguments, or a or [faker](https://fakerjs.dev/api/) generator with optional arguments

Examples using **casual**

Examples
**With arguments**

```
```yaml
plugins:
- typescript-mock-data:
scalars:
Expand All @@ -60,7 +61,7 @@ plugins:

**With multiple arguments**

```
```yaml
plugins:
- typescript-mock-data:
scalars:
Expand All @@ -73,13 +74,48 @@ plugins:

**Shorthand if you don't have arguments**

```
```yaml
plugins:
- typescript-mock-data:
scalars:
Date: date # gets translated to casual.date()
```

Examples using **faker**

**With arguments**

```yaml
plugins:
- typescript-mock-data:
scalars:
Date: # gets translated to faker.date.past(10)
generator: date.past
arguments: 10
```

**With multiple arguments**

```yaml
plugins:
- typescript-mock-data:
scalars:
Description: # gets translated to faker.lorem.paragraphs(3, '\n')
generator: lorem.paragraphs
arguments:
- 3
- '\n'
```

**Shorthand if you don't have arguments**

```yaml
plugins:
- typescript-mock-data:
scalars:
Date: date.past # gets translated to faker.date.past()
```

**Custom value generator**

```yaml
Expand Down
62 changes: 61 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ASTKindToNode, ListTypeNode, NamedTypeNode, parse, printSchema, TypeNode } from 'graphql';
import { faker } from '@faker-js/faker';
import casual from 'casual';
import { PluginFunction, oldVisit } from '@graphql-codegen/plugin-helpers';
import { pascalCase } from 'pascal-case';
Expand Down Expand Up @@ -101,7 +102,7 @@ const getScalarDefinition = (value: ScalarDefinition | ScalarGeneratorName): Sca
return value;
};

const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
const getCasualCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
// If there is a mapping to a `casual` type, then use it and make sure
// to call it if it's a function
const embeddedGenerator = casual[customScalar.generator];
Expand All @@ -128,6 +129,65 @@ const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<Name
return value;
};

const getFakerGenerators = (generatorName: ScalarGeneratorName) => {
let embeddedGenerator: unknown = faker;
let dynamicGenerator = 'faker';

if (typeof generatorName === 'string') {
const generatorPath = generatorName.split('.');
for (const key of generatorPath) {
if (typeof embeddedGenerator === 'object' && key in embeddedGenerator) {
embeddedGenerator = embeddedGenerator[key];
dynamicGenerator = `${dynamicGenerator}['${key}']`;
}
}
}

// If the faker generator is not a function, we can assume the path is wrong
if (typeof embeddedGenerator === 'function') {
return { embeddedGenerator, dynamicGenerator };
}

return { embeddedGenerator: null, dynamicGenerator: null };
};

const getFakerCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
// If there is a mapping to a `faker` type, then use it
const { embeddedGenerator, dynamicGenerator } = getFakerGenerators(customScalar.generator);
if (!embeddedGenerator && customScalar.generator) {
return customScalar.generator;
}

const generatorArgs: unknown[] = Array.isArray(customScalar.arguments)
? customScalar.arguments
: [customScalar.arguments];
if (opts.dynamicValues) {
return `${dynamicGenerator}(...${JSON.stringify(generatorArgs)})`;
}
const value = embeddedGenerator(...generatorArgs);

if (typeof value === 'string') {
return `'${value}'`;
}
if (typeof value === 'object') {
return `${JSON.stringify(value)}`;
}
return value;
};

const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
if (opts.generateLibrary === 'casual') {
return getCasualCustomScalarValue(customScalar, opts);
}


if (opts.generateLibrary === 'faker') {
return getFakerCustomScalarValue(customScalar, opts);
}

throw `Unknown generator library: ${opts.generateLibrary}`;
};

const getNamedType = (opts: Options<NamedTypeNode>): string | number | boolean => {
if (!opts.currentType) {
return '';
Expand Down
75 changes: 74 additions & 1 deletion tests/scalars/__snapshots__/spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should generate custom scalars for native and custom types 1`] = `
exports[`should generate custom scalars for native and custom types using casual 1`] = `
"
export const anA = (overrides?: Partial<A>): A => {
return {
Expand All @@ -20,3 +20,76 @@ export const aB = (overrides?: Partial<B>): B => {
};
"
`;

exports[`should generate custom scalars for native and custom types using faker 1`] = `
"
export const anA = (overrides?: Partial<A>): A => {
return {
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 83,
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : 'Corrupti qui incidunt eius consequatur qui.',
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : 'Orlando64@gmail.com',
};
};

export const aB = (overrides?: Partial<B>): B => {
return {
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : -93,
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : -24.51,
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
};
};
"
`;

exports[`should generate dynamic custom scalars for native and custom types using casual 1`] = `
"import casual from 'casual';

casual.seed(0);

export const anA = (overrides?: Partial<A>): A => {
return {
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : casual['integer'](...[1,100]),
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : casual['string'],
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : casual['email'],
};
};

export const aB = (overrides?: Partial<B>): B => {
return {
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : casual['integer'](...[-100,0]),
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : casual['double'](...[-100,0]),
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
};
};

export const seedMocks = (seed: number) => casual.seed(seed);
"
`;

exports[`should generate dynamic custom scalars for native and custom types using faker 1`] = `
"import { faker } from '@faker-js/faker';

faker.seed(0);

export const anA = (overrides?: Partial<A>): A => {
return {
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : faker['datatype']['number'](...[{\\"min\\":1,\\"max\\":100}]),
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : faker['lorem']['sentence'](...[]),
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : faker['internet']['email'](...[]),
};
};

export const aB = (overrides?: Partial<B>): B => {
return {
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : faker['datatype']['number'](...[{\\"min\\":-100,\\"max\\":0}]),
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : faker['datatype']['float'](...[{\\"min\\":-100,\\"max\\":0}]),
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
};
};

export const seedMocks = (seed: number) => faker.seed(seed);
"
`;
141 changes: 140 additions & 1 deletion tests/scalars/spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { plugin } from '../../src';
import testSchema from './schema';

it('should generate custom scalars for native and custom types', async () => {
it('should generate custom scalars for native and custom types using casual', async () => {
const result = await plugin(testSchema, [], {
scalars: {
String: 'string',
Expand Down Expand Up @@ -45,3 +45,142 @@ it('should generate custom scalars for native and custom types', async () => {

expect(result).toMatchSnapshot();
});

it('should generate dynamic custom scalars for native and custom types using casual', async () => {
const result = await plugin(testSchema, [], {
dynamicValues: true,
scalars: {
String: 'string',
Float: {
generator: 'double',
arguments: [-100, 0],
},
ID: {
generator: 'integer',
arguments: [1, 100],
},
Boolean: 'false',
Int: {
generator: 'integer',
arguments: [-100, 0],
},
AnyObject: 'email',
},
});

expect(result).toBeDefined();

// String
expect(result).toContain(
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : casual['string'],",
);

// Float
expect(result).toContain(
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : casual['double'](...[-100,0]),",
);

// ID
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : casual['integer'](...[1,100]),");

// Boolean
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");

// Int
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : casual['integer'](...[-100,0]),");

expect(result).toMatchSnapshot();
});

it('should generate custom scalars for native and custom types using faker', async () => {
const result = await plugin(testSchema, [], {
generateLibrary: 'faker',
scalars: {
String: 'lorem.sentence',
Float: {
generator: 'datatype.float',
arguments: [{ min: -100, max: 0}],
},
ID: {
generator: 'datatype.number',
arguments: [{ min: 1, max: 100}],
},
Boolean: 'false',
Int: {
generator: 'datatype.number',
arguments: [{ min: -100, max: 0}],
},
AnyObject: 'internet.email',
},
});

expect(result).toBeDefined();

// String
expect(result).toContain(
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : 'Corrupti qui incidunt eius consequatur qui.',",
);

// Float
expect(result).toContain(
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : -24.51,",
);

// ID
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 83,");

// Boolean
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");

// Int
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : -93,");

expect(result).toMatchSnapshot();
});

it('should generate dynamic custom scalars for native and custom types using faker', async () => {
const result = await plugin(testSchema, [], {
generateLibrary: 'faker',
dynamicValues: true,
scalars: {
String: 'lorem.sentence',
Float: {
generator: 'datatype.float',
arguments: [{ min: -100, max: 0}],
},
ID: {
generator: 'datatype.number',
arguments: [{ min: 1, max: 100}],
},
Boolean: 'false',
Int: {
generator: 'datatype.number',
arguments: [{ min: -100, max: 0}],
},
AnyObject: 'internet.email',
},
});

expect(result).toBeDefined();

// String
expect(result).toContain(
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : faker['lorem']['sentence'](...[]),",
);

// Float
expect(result).toContain(
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : faker['datatype']['float'](...[{\"min\":-100,\"max\":0}]),",
);

// ID
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : faker['datatype']['number'](...[{\"min\":1,\"max\":100}]),");

// Boolean
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");

// Int
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : faker['datatype']['number'](...[{\"min\":-100,\"max\":0}]),");

expect(result).toMatchSnapshot();
});