diff --git a/README.md b/README.md index f0ff221..cd900c2 100644 --- a/README.md +++ b/README.md @@ -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: @@ -60,7 +61,7 @@ plugins: **With multiple arguments** -``` +```yaml plugins: - typescript-mock-data: scalars: @@ -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 diff --git a/src/index.ts b/src/index.ts index dfaba57..d6264cb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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'; @@ -101,7 +102,7 @@ const getScalarDefinition = (value: ScalarDefinition | ScalarGeneratorName): Sca return value; }; -const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options) => { +const getCasualCustomScalarValue = (customScalar: ScalarDefinition, opts: Options) => { // 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]; @@ -128,6 +129,65 @@ const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options { + 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) => { + // 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) => { + 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): string | number | boolean => { if (!opts.currentType) { return ''; diff --git a/tests/scalars/__snapshots__/spec.ts.snap b/tests/scalars/__snapshots__/spec.ts.snap index c0b7a6b..893d707 100644 --- a/tests/scalars/__snapshots__/spec.ts.snap +++ b/tests/scalars/__snapshots__/spec.ts.snap @@ -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 => { return { @@ -20,3 +20,76 @@ export const aB = (overrides?: Partial): B => { }; " `; + +exports[`should generate custom scalars for native and custom types using faker 1`] = ` +" +export const anA = (overrides?: Partial): 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 => { + 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 => { + 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 => { + 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 => { + 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 => { + 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); +" +`; diff --git a/tests/scalars/spec.ts b/tests/scalars/spec.ts index c318967..59ef5bf 100644 --- a/tests/scalars/spec.ts +++ b/tests/scalars/spec.ts @@ -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', @@ -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(); +});