From eb2bbd4edba9eb41a78b80a34287ba24e94740db Mon Sep 17 00:00:00 2001 From: huseyinbuyukdere Date: Thu, 9 Jan 2025 14:45:48 +0300 Subject: [PATCH 1/5] added included and excluded types feature --- README.md | 28 +++++++++ src/index.ts | 18 +++++- .../typescript-mock-data.spec.ts.snap | 57 +++++++++++++++++++ tests/typescript-mock-data.spec.ts | 36 ++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7045ed..e366dd3 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,34 @@ Adds `__typename` property to mock data Changes enums to TypeScript string union types +### includedTypes (`string[]`, defaultValue: `undefined`) + +Specifies an array of types to **include** in the mock generation. When provided, only the types listed in this array will have mock data generated. + +Example: + +```yaml +plugins: + - typescript-mock-data: + includedTypes: + - User + - Avatar +``` + +### excludedTypes (`string[]`, defaultValue: `undefined`) + +Specifies an array of types to **exclude** in the mock generation. When provided, the types listed in this array will not have mock data generated. + +Example: + +```yaml +plugins: + - typescript-mock-data: + excludedTypes: + - User + - Avatar +``` + ### terminateCircularRelationships (`boolean | 'immediate'`, defaultValue: `false`) When enabled, prevents circular relationships from triggering infinite recursion. After the first resolution of a diff --git a/src/index.ts b/src/index.ts index 53f88fb..7cfb1eb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -590,6 +590,8 @@ export interface TypescriptMocksPluginConfig { defaultNullableToNull?: boolean; useTypeImports?: boolean; typeNamesMapping?: Record; + includedTypes?: string[]; + excludedTypes?: string[]; } interface TypeItem { @@ -832,7 +834,21 @@ export const plugin: PluginFunction = (schema, docu // run on the types first oldVisit(astNode, { leave: typeVisitor }); const result = oldVisit(astNode, { leave: visitor }); - const definitions = result.definitions.filter((definition: any) => !!definition); + + const { includedTypes, excludedTypes } = config; + const shouldGenerateMockForType = (typeName: string) => { + if (includedTypes && includedTypes.length > 0) { + return includedTypes.includes(typeName); + } + if (excludedTypes && excludedTypes.length > 0) { + return !excludedTypes.includes(typeName); + } + return true; + }; + + const definitions = result.definitions.filter( + (definition: any) => !!definition && shouldGenerateMockForType(definition.typeName), + ); const typesFile = config.typesFile ? config.typesFile.replace(/\.[\w]+$/, '') : null; const typesFileImport = getImportTypes({ diff --git a/tests/__snapshots__/typescript-mock-data.spec.ts.snap b/tests/__snapshots__/typescript-mock-data.spec.ts.snap index 8628fac..0b21e65 100644 --- a/tests/__snapshots__/typescript-mock-data.spec.ts.snap +++ b/tests/__snapshots__/typescript-mock-data.spec.ts.snap @@ -1030,6 +1030,63 @@ export const aQuery = (overrides?: Partial): Query => { " `; +exports[`should exclude specified types from mock generation 1`] = ` +" +export const aWithAvatar = (overrides?: Partial): WithAvatar => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '99f515e7-21e0-461d-b823-0d4c7f4dafc5', + avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(), + }; +}; + +export const aCamelCaseThing = (overrides?: Partial): CamelCaseThing => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '245b9cf9-10fa-4974-8100-fbeee5ee7fd4', + }; +}; + +export const aPrefixedResponse = (overrides?: Partial): PrefixedResponse => { + return { + ping: overrides && overrides.hasOwnProperty('ping') ? overrides.ping! : 'nam', + }; +}; + +export const anAbcType = (overrides?: Partial): AbcType => { + return { + abc: overrides && overrides.hasOwnProperty('abc') ? overrides.abc! : 'accommodo', + }; +}; + +export const aListType = (overrides?: Partial): ListType => { + return { + stringList: overrides && overrides.hasOwnProperty('stringList') ? overrides.stringList! : ['accusator'], + nullableStringList: overrides && overrides.hasOwnProperty('nullableStringList') ? overrides.nullableStringList! : ['tricesimus'], + }; +}; + +export const anUpdateUserInput = (overrides?: Partial): UpdateUserInput => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '0d6a9360-d92b-4660-b1e5-f14155047bdd', + login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'apud', + avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(), + }; +}; + +export const aMutation = (overrides?: Partial): Mutation => { + return { + updateUser: overrides && overrides.hasOwnProperty('updateUser') ? overrides.updateUser! : aUser(), + }; +}; + +export const aQuery = (overrides?: Partial): Query => { + return { + user: overrides && overrides.hasOwnProperty('user') ? overrides.user! : aUser(), + prefixed_query: overrides && overrides.hasOwnProperty('prefixed_query') ? overrides.prefixed_query! : aPrefixedResponse(), + }; +}; +" +`; + exports[`should generate dynamic values in mocks 1`] = ` "import { fakerEN as faker } from '@faker-js/faker'; diff --git a/tests/typescript-mock-data.spec.ts b/tests/typescript-mock-data.spec.ts index bdd692e..89560ef 100644 --- a/tests/typescript-mock-data.spec.ts +++ b/tests/typescript-mock-data.spec.ts @@ -597,3 +597,39 @@ it('overriding works as expected when defaultNullableToNull is true', async () = expect(result).toMatchSnapshot(); }); + +it('should generate mock data only for included types', async () => { + const result = await plugin(testSchema, [], { + includedTypes: ['User', 'Avatar'], + }); + + expect(result).toBeDefined(); + expect(result).toContain('export const aUser'); + expect(result).toContain('export const anAvatar'); + expect(result).not.toContain('export const aPrefixedResponse'); + expect(result).not.toContain('export const aCamelCaseThing'); +}); + +it('should exclude specified types from mock generation', async () => { + const result = await plugin(testSchema, [], { + excludedTypes: ['User', 'Avatar'], + }); + + expect(result).toBeDefined(); + expect(result).not.toContain('export const aUser'); + expect(result).not.toContain('export const anAvatar'); + expect(result).toContain('export const aPrefixedResponse'); + expect(result).toContain('export const aCamelCaseThing'); + expect(result).toMatchSnapshot(); +}); + +it('should prioritize includedTypes over excludedTypes if both are specified', async () => { + const result = await plugin(testSchema, [], { + includedTypes: ['User'], + excludedTypes: ['User', 'Avatar'], + }); + expect(result).toBeDefined(); + expect(result).toContain('export const aUser'); + expect(result).not.toContain('export const anAvatar'); + expect(result).not.toContain('export const aPrefixedResponse'); +}); From 54a8daa31a34f6975fcf64029ffde630b6b26535 Mon Sep 17 00:00:00 2001 From: huseyinbuyukdere Date: Thu, 9 Jan 2025 14:45:48 +0300 Subject: [PATCH 2/5] added included and excluded types feature --- README.md | 28 ++++++++++++++++++++++++ src/index.ts | 18 ++++++++++++++- tests/typescript-mock-data.spec.ts | 35 ++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7045ed..e366dd3 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,34 @@ Adds `__typename` property to mock data Changes enums to TypeScript string union types +### includedTypes (`string[]`, defaultValue: `undefined`) + +Specifies an array of types to **include** in the mock generation. When provided, only the types listed in this array will have mock data generated. + +Example: + +```yaml +plugins: + - typescript-mock-data: + includedTypes: + - User + - Avatar +``` + +### excludedTypes (`string[]`, defaultValue: `undefined`) + +Specifies an array of types to **exclude** in the mock generation. When provided, the types listed in this array will not have mock data generated. + +Example: + +```yaml +plugins: + - typescript-mock-data: + excludedTypes: + - User + - Avatar +``` + ### terminateCircularRelationships (`boolean | 'immediate'`, defaultValue: `false`) When enabled, prevents circular relationships from triggering infinite recursion. After the first resolution of a diff --git a/src/index.ts b/src/index.ts index 53f88fb..7cfb1eb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -590,6 +590,8 @@ export interface TypescriptMocksPluginConfig { defaultNullableToNull?: boolean; useTypeImports?: boolean; typeNamesMapping?: Record; + includedTypes?: string[]; + excludedTypes?: string[]; } interface TypeItem { @@ -832,7 +834,21 @@ export const plugin: PluginFunction = (schema, docu // run on the types first oldVisit(astNode, { leave: typeVisitor }); const result = oldVisit(astNode, { leave: visitor }); - const definitions = result.definitions.filter((definition: any) => !!definition); + + const { includedTypes, excludedTypes } = config; + const shouldGenerateMockForType = (typeName: string) => { + if (includedTypes && includedTypes.length > 0) { + return includedTypes.includes(typeName); + } + if (excludedTypes && excludedTypes.length > 0) { + return !excludedTypes.includes(typeName); + } + return true; + }; + + const definitions = result.definitions.filter( + (definition: any) => !!definition && shouldGenerateMockForType(definition.typeName), + ); const typesFile = config.typesFile ? config.typesFile.replace(/\.[\w]+$/, '') : null; const typesFileImport = getImportTypes({ diff --git a/tests/typescript-mock-data.spec.ts b/tests/typescript-mock-data.spec.ts index bdd692e..998026c 100644 --- a/tests/typescript-mock-data.spec.ts +++ b/tests/typescript-mock-data.spec.ts @@ -597,3 +597,38 @@ it('overriding works as expected when defaultNullableToNull is true', async () = expect(result).toMatchSnapshot(); }); + +it('should generate mock data only for included types', async () => { + const result = await plugin(testSchema, [], { + includedTypes: ['User', 'Avatar'], + }); + + expect(result).toBeDefined(); + expect(result).toContain('export const aUser'); + expect(result).toContain('export const anAvatar'); + expect(result).not.toContain('export const aPrefixedResponse'); + expect(result).not.toContain('export const aCamelCaseThing'); +}); + +it('should exclude specified types from mock generation', async () => { + const result = await plugin(testSchema, [], { + excludedTypes: ['User', 'Avatar'], + }); + + expect(result).toBeDefined(); + expect(result).not.toContain('export const aUser'); + expect(result).not.toContain('export const anAvatar'); + expect(result).toContain('export const aPrefixedResponse'); + expect(result).toContain('export const aCamelCaseThing'); +}); + +it('should prioritize includedTypes over excludedTypes if both are specified', async () => { + const result = await plugin(testSchema, [], { + includedTypes: ['User'], + excludedTypes: ['User', 'Avatar'], + }); + expect(result).toBeDefined(); + expect(result).toContain('export const aUser'); + expect(result).not.toContain('export const anAvatar'); + expect(result).not.toContain('export const aPrefixedResponse'); +}); From b42f196778a2c778c7ed9fd661af10c617dc3be2 Mon Sep 17 00:00:00 2001 From: huseyinbuyukdere Date: Thu, 9 Jan 2025 14:51:20 +0300 Subject: [PATCH 3/5] snapshot result is removed --- .../typescript-mock-data.spec.ts.snap | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/tests/__snapshots__/typescript-mock-data.spec.ts.snap b/tests/__snapshots__/typescript-mock-data.spec.ts.snap index 0b21e65..8628fac 100644 --- a/tests/__snapshots__/typescript-mock-data.spec.ts.snap +++ b/tests/__snapshots__/typescript-mock-data.spec.ts.snap @@ -1030,63 +1030,6 @@ export const aQuery = (overrides?: Partial): Query => { " `; -exports[`should exclude specified types from mock generation 1`] = ` -" -export const aWithAvatar = (overrides?: Partial): WithAvatar => { - return { - id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '99f515e7-21e0-461d-b823-0d4c7f4dafc5', - avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(), - }; -}; - -export const aCamelCaseThing = (overrides?: Partial): CamelCaseThing => { - return { - id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '245b9cf9-10fa-4974-8100-fbeee5ee7fd4', - }; -}; - -export const aPrefixedResponse = (overrides?: Partial): PrefixedResponse => { - return { - ping: overrides && overrides.hasOwnProperty('ping') ? overrides.ping! : 'nam', - }; -}; - -export const anAbcType = (overrides?: Partial): AbcType => { - return { - abc: overrides && overrides.hasOwnProperty('abc') ? overrides.abc! : 'accommodo', - }; -}; - -export const aListType = (overrides?: Partial): ListType => { - return { - stringList: overrides && overrides.hasOwnProperty('stringList') ? overrides.stringList! : ['accusator'], - nullableStringList: overrides && overrides.hasOwnProperty('nullableStringList') ? overrides.nullableStringList! : ['tricesimus'], - }; -}; - -export const anUpdateUserInput = (overrides?: Partial): UpdateUserInput => { - return { - id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '0d6a9360-d92b-4660-b1e5-f14155047bdd', - login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'apud', - avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(), - }; -}; - -export const aMutation = (overrides?: Partial): Mutation => { - return { - updateUser: overrides && overrides.hasOwnProperty('updateUser') ? overrides.updateUser! : aUser(), - }; -}; - -export const aQuery = (overrides?: Partial): Query => { - return { - user: overrides && overrides.hasOwnProperty('user') ? overrides.user! : aUser(), - prefixed_query: overrides && overrides.hasOwnProperty('prefixed_query') ? overrides.prefixed_query! : aPrefixedResponse(), - }; -}; -" -`; - exports[`should generate dynamic values in mocks 1`] = ` "import { fakerEN as faker } from '@faker-js/faker'; From 4211203dd5b4c68971db590bc77bf65f6d996cee Mon Sep 17 00:00:00 2001 From: huseyinbuyukdere Date: Thu, 13 Feb 2025 12:20:23 +0300 Subject: [PATCH 4/5] code review issues are resolved --- src/index.ts | 3 +++ .../typescript-mock-data.spec.ts.snap | 26 +++++++++++++++++++ tests/typescript-mock-data.spec.ts | 1 + 3 files changed, 30 insertions(+) diff --git a/src/index.ts b/src/index.ts index 7cfb1eb..02a5d30 100644 --- a/src/index.ts +++ b/src/index.ts @@ -837,6 +837,9 @@ export const plugin: PluginFunction = (schema, docu const { includedTypes, excludedTypes } = config; const shouldGenerateMockForType = (typeName: string) => { + if (!typeName) { + return true; + } if (includedTypes && includedTypes.length > 0) { return includedTypes.includes(typeName); } diff --git a/tests/__snapshots__/typescript-mock-data.spec.ts.snap b/tests/__snapshots__/typescript-mock-data.spec.ts.snap index 8628fac..fb0a562 100644 --- a/tests/__snapshots__/typescript-mock-data.spec.ts.snap +++ b/tests/__snapshots__/typescript-mock-data.spec.ts.snap @@ -1594,6 +1594,32 @@ export const aQuery = (overrides?: Partial): Query => { " `; +exports[`should generate mock data only for included types 1`] = ` +" +export const anAvatar = (overrides?: Partial): Avatar => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '1550ff93-cd31-49b4-a3c3-8ef1cb68bdc3', + url: overrides && overrides.hasOwnProperty('url') ? overrides.url! : 'consectetur', + }; +}; + +export const aUser = (overrides?: Partial): User => { + return { + id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'b5756f00-51a6-422a-81a7-dc13ee6a6375', + creationDate: overrides && overrides.hasOwnProperty('creationDate') ? overrides.creationDate! : '2021-06-27T14:29:24.774Z', + login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'sordeo', + avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(), + status: overrides && overrides.hasOwnProperty('status') ? overrides.status! : Status.Online, + customStatus: overrides && overrides.hasOwnProperty('customStatus') ? overrides.customStatus! : AbcStatus.HasXyzStatus, + scalarValue: overrides && overrides.hasOwnProperty('scalarValue') ? overrides.scalarValue! : 'arx', + camelCaseThing: overrides && overrides.hasOwnProperty('camelCaseThing') ? overrides.camelCaseThing! : aCamelCaseThing(), + unionThing: overrides && overrides.hasOwnProperty('unionThing') ? overrides.unionThing! : anAvatar(), + prefixedEnum: overrides && overrides.hasOwnProperty('prefixedEnum') ? overrides.prefixedEnum! : PrefixedEnum.PrefixedValue, + }; +}; +" +`; + exports[`should generate mock data with PascalCase enum values by default 1`] = ` " export const anAvatar = (overrides?: Partial): Avatar => { diff --git a/tests/typescript-mock-data.spec.ts b/tests/typescript-mock-data.spec.ts index 998026c..0aa274f 100644 --- a/tests/typescript-mock-data.spec.ts +++ b/tests/typescript-mock-data.spec.ts @@ -603,6 +603,7 @@ it('should generate mock data only for included types', async () => { includedTypes: ['User', 'Avatar'], }); + expect(result).toMatchSnapshot(); expect(result).toBeDefined(); expect(result).toContain('export const aUser'); expect(result).toContain('export const anAvatar'); From c2bc6b09b61738ce42178a49720e00cd19de7e79 Mon Sep 17 00:00:00 2001 From: huseyinbuyukdere Date: Tue, 25 Feb 2025 14:58:10 +0300 Subject: [PATCH 5/5] fixed code review issue --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index e2e88ec..a6b795f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -839,7 +839,7 @@ export const plugin: PluginFunction = (schema, docu const result = oldVisit(astNode, { leave: visitor }); const { includedTypes, excludedTypes } = config; - const shouldGenerateMockForType = (typeName: string) => { + const shouldGenerateMockForType = (typeName?: string) => { if (!typeName) { return true; }