From 6ab946a362f99ae6d1ba2fba7e4611a5e875301a Mon Sep 17 00:00:00 2001 From: Eddy Nguyen Date: Fri, 28 Nov 2025 01:20:40 +1100 Subject: [PATCH 1/4] Add tests for enum --- .../typescript/operations/src/visitor.ts | 1 + .../tests/ts-documents.standalone.spec.ts | 307 +++++++++++++++++- 2 files changed, 301 insertions(+), 7 deletions(-) diff --git a/packages/plugins/typescript/operations/src/visitor.ts b/packages/plugins/typescript/operations/src/visitor.ts index 3e4ae45692b..c0cc5afae3e 100644 --- a/packages/plugins/typescript/operations/src/visitor.ts +++ b/packages/plugins/typescript/operations/src/visitor.ts @@ -167,6 +167,7 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< ) ); this._declarationBlockConfig = { + enumNameValueSeparator: ' =', ignoreExport: this.config.noExport, }; } diff --git a/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts b/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts index 06dd1752443..5f079101a55 100644 --- a/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts +++ b/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts @@ -1,5 +1,5 @@ import { mergeOutputs } from '@graphql-codegen/plugin-helpers'; -// import { validateTs } from '@graphql-codegen/testing'; +import { validateTs } from '@graphql-codegen/testing'; import { buildSchema, parse } from 'graphql'; import { plugin } from '../src/index.js'; @@ -165,11 +165,304 @@ describe('TypeScript Operations Plugin - Standalone', () => { }); describe('TypeScript Operations Plugin - Enum', () => { - it.todo('does not generate unused enum in variables and result'); - it.todo('handles native numeric enum correctly'); - it.todo('handles const enum correctly'); - it.todo('handles native const enum correctly'); - it.todo('handles native enum correctly'); - it.todo('handles EnumValues correctly'); + it('does not generate enums if not used in variables and result', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], {})]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type MeQueryVariables = Exact<{ [key: string]: never; }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `native-numeric` enum correctly', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native-numeric' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + Admin = 0, + Customer = 1 + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `const` enum correctly', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'const' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export const UserRole = { + Admin: 'ADMIN', + Customer: 'CUSTOMER' + } as const; + + export type UserRole = typeof UserRole[keyof typeof UserRole]; + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `native-const` enum correctly', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native-const' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export const enum UserRole { + Admin = 'ADMIN', + Customer = 'CUSTOMER' + }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `native` enum correctly', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + Admin = 'ADMIN', + Customer = 'CUSTOMER' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with `native` enum correctly', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'native', + enumValues: { + UserRole: { + ADMIN: 0, + CUSTOMER: 'test', + }, + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + Admin = 0, + Customer = 'test' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it.todo('handles `enumValues` as file correctly'); // Bring over tests from https://github.com/dotansimha/graphql-code-generator/blob/accdab69106605241933e9d66d64dc7077656f30/packages/plugins/typescript/typescript/tests/typescript.spec.ts }); From dd76ac7ad0420bc9276cd5579cf7bf16bb12d67a Mon Sep 17 00:00:00 2001 From: Eddy Nguyen Date: Sat, 29 Nov 2025 14:10:31 +1100 Subject: [PATCH 2/4] Baselin tests --- .../tests/ts-documents.standalone.spec.ts | 1025 ++++++++++++++++- 1 file changed, 1013 insertions(+), 12 deletions(-) diff --git a/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts b/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts index 5f079101a55..e3a2a69f90e 100644 --- a/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts +++ b/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts @@ -29,13 +29,19 @@ describe('TypeScript Operations Plugin - Standalone', () => { createdAt: DateTime! } + "UserRole Description" enum UserRole { + "UserRole ADMIN" ADMIN + "UserRole CUSTOMER" CUSTOMER } + "UsersInput Description" input UsersInput { + "UsersInput from" from: DateTime + "UsersInput to" to: DateTime role: UserRole } @@ -88,8 +94,11 @@ describe('TypeScript Operations Plugin - Standalone', () => { expect(result).toMatchInlineSnapshot(` "type Exact = { [K in keyof T]: T[K] }; + /** UserRole Description */ export type UserRole = + /** UserRole ADMIN */ | 'ADMIN' + /** UserRole CUSTOMER */ | 'CUSTOMER'; export type UserQueryVariables = Exact<{ @@ -207,7 +216,7 @@ describe('TypeScript Operations Plugin - Enum', () => { validateTs(result, undefined, undefined, undefined, undefined, true); }); - it('handles `native-numeric` enum correctly', async () => { + it('handles `native-numeric` enum', async () => { const schema = buildSchema(/* GraphQL */ ` type Query { me: User @@ -256,7 +265,7 @@ describe('TypeScript Operations Plugin - Enum', () => { validateTs(result, undefined, undefined, undefined, undefined, true); }); - it('handles `const` enum correctly', async () => { + it('handles `const` enum', async () => { const schema = buildSchema(/* GraphQL */ ` type Query { me: User @@ -270,8 +279,11 @@ describe('TypeScript Operations Plugin - Enum', () => { } enum UserRole { - ADMIN - CUSTOMER + A_B_C + X_Y_Z + _TEST + My_Value + _123 } scalar DateTime @@ -289,8 +301,11 @@ describe('TypeScript Operations Plugin - Enum', () => { expect(result).toMatchInlineSnapshot(` "type Exact = { [K in keyof T]: T[K] }; export const UserRole = { - Admin: 'ADMIN', - Customer: 'CUSTOMER' + ABC: 'A_B_C', + XYZ: 'X_Y_Z', + Test: '_TEST', + MyValue: 'My_Value', + '123': '_123' } as const; export type UserRole = typeof UserRole[keyof typeof UserRole]; @@ -306,7 +321,7 @@ describe('TypeScript Operations Plugin - Enum', () => { validateTs(result, undefined, undefined, undefined, undefined, true); }); - it('handles `native-const` enum correctly', async () => { + it('handles `native-const` enum', async () => { const schema = buildSchema(/* GraphQL */ ` type Query { me: User @@ -319,9 +334,12 @@ describe('TypeScript Operations Plugin - Enum', () => { createdAt: DateTime! } + """ + Multiline comment test + """ enum UserRole { ADMIN - CUSTOMER + CUSTOMER @deprecated(reason: "Enum value CUSTOMER has been deprecated.") } scalar DateTime @@ -338,8 +356,10 @@ describe('TypeScript Operations Plugin - Enum', () => { expect(result).toMatchInlineSnapshot(` "type Exact = { [K in keyof T]: T[K] }; + /** Multiline comment test */ export const enum UserRole { Admin = 'ADMIN', + /** @deprecated Enum value CUSTOMER has been deprecated. */ Customer = 'CUSTOMER' }; @@ -355,7 +375,7 @@ describe('TypeScript Operations Plugin - Enum', () => { validateTs(result, undefined, undefined, undefined, undefined, true); }); - it('handles `native` enum correctly', async () => { + it('handles `native` enum', async () => { const schema = buildSchema(/* GraphQL */ ` type Query { me: User @@ -404,7 +424,137 @@ describe('TypeScript Operations Plugin - Enum', () => { validateTs(result, undefined, undefined, undefined, undefined, true); }); - it('handles `enumValues` with `native` enum correctly', async () => { + it('handles `enumValues` with `string-literal` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + A_B_C + X_Y_Z + _TEST + My_Value + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'string-literal', + enumValues: { + UserRole: { + A_B_C: 0, + X_Y_Z: 'Foo', + _TEST: 'Bar', + My_Value: 1, + }, + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRole = + | 0 + | 'Foo' + | 'Bar' + | 1; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with `const` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + A_B_C + X_Y_Z + _TEST + My_Value + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'const', + enumValues: { + UserRole: { + A_B_C: 0, + X_Y_Z: 'Foo', + _TEST: 'Bar', + My_Value: 1, + }, + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export const UserRole = { + ABC: 0, + XYZ: 'Foo', + Test: 'Bar', + MyValue: 1 + } as const; + + export type UserRole = typeof UserRole[keyof typeof UserRole]; + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with `native` enum', async () => { const schema = buildSchema(/* GraphQL */ ` type Query { me: User @@ -463,6 +613,857 @@ describe('TypeScript Operations Plugin - Enum', () => { validateTs(result, undefined, undefined, undefined, undefined, true); }); - it.todo('handles `enumValues` as file correctly'); - // Bring over tests from https://github.com/dotansimha/graphql-code-generator/blob/accdab69106605241933e9d66d64dc7077656f30/packages/plugins/typescript/typescript/tests/typescript.spec.ts + it('handles `enumValues` as file import', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumValues: { + UserRole: './my-file#MyEnum', + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { MyEnum as UserRole } from './my-file'; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with custom imported enum from namespace with different name', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumValues: { + UserRole: './my-file#NS.ETest', + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { NS } from './my-file'; + import UserRole = NS.ETest; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with custom imported enum from namespace with the same name', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumValues: { + UserRole: './my-file#NS.UserRole', + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { NS } from './my-file'; + import UserRole = NS.UserRole; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` from a single file', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + enum UserStatus { + ACTIVE + PENDING + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!, $status: UserStatus!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'native', + enumValues: './my-file', + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { UserRole } from './my-file'; + import { UserStatus } from './my-file'; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export { UserStatus }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + status: UserStatus; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` from a single file when specified as string', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + enum UserStatus { + ACTIVE + PENDING + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!, $status: UserStatus!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'native', + enumValues: { UserRole: './my-file#UserRole', UserStatus: './my-file#UserStatus2X' }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { UserRole } from './my-file'; + import { UserStatus2X as UserStatus } from './my-file'; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export { UserStatus }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + status: UserStatus; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('removes underscore from enum values', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + A_B_C + X_Y_Z + _TEST + My_Value + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + ABC = 'A_B_C', + XYZ = 'X_Y_Z', + Test = '_TEST', + MyValue = 'My_Value' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('keeps underscores in enum values when the value is only underscores', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + _ + __ + _TEST + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + _ = '_', + __ = '__', + Test = '_TEST' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('adds typesPrefix to enum when enumPrefix is true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { typesPrefix: 'I', enumPrefix: true })]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type IUserRole = + | 'ADMIN' + | 'CUSTOMER'; + + export type IMeQueryVariables = Exact<{ + role: IUserRole; + }>; + + + export type IMeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('does not add typesPrefix to enum when enumPrefix is false', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { typesPrefix: 'I', enumPrefix: false })]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRole = + | 'ADMIN' + | 'CUSTOMER'; + + export type IMeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type IMeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('adds typesSuffix to enum when enumSuffix is true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { typesSuffix: 'Z', enumSuffix: true })]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRoleZ = + | 'ADMIN' + | 'CUSTOMER'; + + export type MeQueryVariablesZ = Exact<{ + role: UserRoleZ; + }>; + + + export type MeQueryZ = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('does not add typesSuffix to enum when enumSuffix is false', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { typesSuffix: 'Z', enumSuffix: false })]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRole = + | 'ADMIN' + | 'CUSTOMER'; + + export type MeQueryVariablesZ = Exact<{ + role: UserRole; + }>; + + + export type MeQueryZ = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('keeps enum value naming convention when namingConvention.enumValues is `keep`', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + namingConvention: { + typeNames: 'change-case-all#lowerCase', + enumValues: 'keep', + }, + }), + ]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type userrole = + | 'ADMIN' + | 'CUSTOMER'; + + export type mequeryvariables = Exact<{ + role: userrole; + }>; + + + export type mequery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('uses custom enum naming convention when namingConvention.enumValues is provided and enumType is native', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'native', + namingConvention: { + typeNames: 'keep', + enumValues: 'change-case-all#lowerCase', + }, + }), + ]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + admin = 'ADMIN', + customer = 'CUSTOMER' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('does not contain "export" when noExport is set to true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + noExport: true, + }), + ]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + type UserRole = + | 'ADMIN' + | 'CUSTOMER'; + + type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); +}); + +describe('TypeScript Operations Plugin - Enum `%future added value`', () => { + it('adds `%future added value` to the type when enumType is `string-literal` and futureProofEnums is true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { futureProofEnums: true })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRole = + | 'ADMIN' + | 'CUSTOMER' + | '%future added value'; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('it adds `%future added value` to output when enumType is `native` and futureProofEnums is true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + role + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native', futureProofEnums: true })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + Admin = 'ADMIN', + Customer = 'CUSTOMER' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string, role: UserRole } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it.todo('adds `%future added value` to enum usage when futureProofEnums is true and enumType is string-literal'); + it.todo('adds `%future added value` to enum usage when futureProofEnums is true and allowEnumStringTypes is true'); }); From 2d6ecd0f3fbcb08b1edc5bb3d4fc1bd15680ee6c Mon Sep 17 00:00:00 2001 From: Eddy Nguyen Date: Sat, 29 Nov 2025 14:11:26 +1100 Subject: [PATCH 3/4] Ensure correctness of enumValues import --- .../typescript/operations/src/index.ts | 1 + .../typescript/operations/src/visitor.ts | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/packages/plugins/typescript/operations/src/index.ts b/packages/plugins/typescript/operations/src/index.ts index 8dd2c1b3439..3e1497bc8f2 100644 --- a/packages/plugins/typescript/operations/src/index.ts +++ b/packages/plugins/typescript/operations/src/index.ts @@ -63,6 +63,7 @@ export const plugin: PluginFunction = { [K in keyof T]: T[K] };', ], diff --git a/packages/plugins/typescript/operations/src/visitor.ts b/packages/plugins/typescript/operations/src/visitor.ts index c0cc5afae3e..b8bc1f324f7 100644 --- a/packages/plugins/typescript/operations/src/visitor.ts +++ b/packages/plugins/typescript/operations/src/visitor.ts @@ -246,4 +246,58 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< return usedInputTypes; } + + // TODO: share with base-types-visitor + public getEnumsImports(): string[] { + return Object.keys(this.config.enumValues) + .flatMap(enumName => { + const mappedValue = this.config.enumValues[enumName]; + + if (mappedValue.sourceFile) { + if (mappedValue.isDefault) { + return [this._buildTypeImport(mappedValue.typeIdentifier, mappedValue.sourceFile, true)]; + } + + return this.handleEnumValueMapper( + mappedValue.typeIdentifier, + mappedValue.importIdentifier, + mappedValue.sourceIdentifier, + mappedValue.sourceFile + ); + } + + return []; + }) + .filter(Boolean); + } + protected _buildTypeImport(identifier: string, source: string, asDefault = false): string { + const { useTypeImports } = this.config; + if (asDefault) { + if (useTypeImports) { + return `import type { default as ${identifier} } from '${source}';`; + } + return `import ${identifier} from '${source}';`; + } + return `import${useTypeImports ? ' type' : ''} { ${identifier} } from '${source}';`; + } + + protected handleEnumValueMapper( + typeIdentifier: string, + importIdentifier: string | null, + sourceIdentifier: string | null, + sourceFile: string | null + ): string[] { + if (importIdentifier !== sourceIdentifier) { + // use namespace import to dereference nested enum + // { enumValues: { MyEnum: './my-file#NS.NestedEnum' } } + return [ + this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile), + `import ${typeIdentifier} = ${sourceIdentifier};`, + ]; + } + if (sourceIdentifier !== typeIdentifier) { + return [this._buildTypeImport(`${sourceIdentifier} as ${typeIdentifier}`, sourceFile)]; + } + return [this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile)]; + } } From 22f986ef01021fac0f43a9e45b0b58f37f430448 Mon Sep 17 00:00:00 2001 From: Eddy Nguyen Date: Sun, 30 Nov 2025 18:12:05 +1100 Subject: [PATCH 4/4] Split enum tests --- .../ts-documents.standalone.enum.spec.ts | 1246 ++++++++++++++++ .../tests/ts-documents.standalone.spec.ts | 1297 +---------------- 2 files changed, 1247 insertions(+), 1296 deletions(-) create mode 100644 packages/plugins/typescript/operations/tests/ts-documents.standalone.enum.spec.ts diff --git a/packages/plugins/typescript/operations/tests/ts-documents.standalone.enum.spec.ts b/packages/plugins/typescript/operations/tests/ts-documents.standalone.enum.spec.ts new file mode 100644 index 00000000000..992d3b85e1d --- /dev/null +++ b/packages/plugins/typescript/operations/tests/ts-documents.standalone.enum.spec.ts @@ -0,0 +1,1246 @@ +import { mergeOutputs } from '@graphql-codegen/plugin-helpers'; +import { validateTs } from '@graphql-codegen/testing'; +import { buildSchema, parse } from 'graphql'; +import { plugin } from '../src/index.js'; + +describe('TypeScript Operations Plugin - Enum', () => { + it('does not generate enums if not used in variables and result', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], {})]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type MeQueryVariables = Exact<{ [key: string]: never; }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `native-numeric` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native-numeric' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + Admin = 0, + Customer = 1 + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `const` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + A_B_C + X_Y_Z + _TEST + My_Value + _123 + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'const' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export const UserRole = { + ABC: 'A_B_C', + XYZ: 'X_Y_Z', + Test: '_TEST', + MyValue: 'My_Value', + '123': '_123' + } as const; + + export type UserRole = typeof UserRole[keyof typeof UserRole]; + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `native-const` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + """ + Multiline comment test + """ + enum UserRole { + ADMIN + CUSTOMER @deprecated(reason: "Enum value CUSTOMER has been deprecated.") + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native-const' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + /** Multiline comment test */ + export const enum UserRole { + Admin = 'ADMIN', + /** @deprecated Enum value CUSTOMER has been deprecated. */ + Customer = 'CUSTOMER' + }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `native` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + Admin = 'ADMIN', + Customer = 'CUSTOMER' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with `string-literal` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + A_B_C + X_Y_Z + _TEST + My_Value + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'string-literal', + enumValues: { + UserRole: { + A_B_C: 0, + X_Y_Z: 'Foo', + _TEST: 'Bar', + My_Value: 1, + }, + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRole = + | 0 + | 'Foo' + | 'Bar' + | 1; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with `const` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + A_B_C + X_Y_Z + _TEST + My_Value + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'const', + enumValues: { + UserRole: { + A_B_C: 0, + X_Y_Z: 'Foo', + _TEST: 'Bar', + My_Value: 1, + }, + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export const UserRole = { + ABC: 0, + XYZ: 'Foo', + Test: 'Bar', + MyValue: 1 + } as const; + + export type UserRole = typeof UserRole[keyof typeof UserRole]; + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with `native` enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'native', + enumValues: { + UserRole: { + ADMIN: 0, + CUSTOMER: 'test', + }, + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + Admin = 0, + Customer = 'test' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` as file import', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumValues: { + UserRole: './my-file#MyEnum', + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { MyEnum as UserRole } from './my-file'; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with custom imported enum from namespace with different name', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumValues: { + UserRole: './my-file#NS.ETest', + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { NS } from './my-file'; + import UserRole = NS.ETest; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` with custom imported enum from namespace with the same name', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumValues: { + UserRole: './my-file#NS.UserRole', + }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { NS } from './my-file'; + import UserRole = NS.UserRole; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` from a single file', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + enum UserStatus { + ACTIVE + PENDING + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!, $status: UserStatus!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'native', + enumValues: './my-file', + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { UserRole } from './my-file'; + import { UserStatus } from './my-file'; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export { UserStatus }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + status: UserStatus; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('handles `enumValues` from a single file when specified as string', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + enum UserStatus { + ACTIVE + PENDING + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!, $status: UserStatus!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'native', + enumValues: { UserRole: './my-file#UserRole', UserStatus: './my-file#UserStatus2X' }, + }), + ]); + + expect(result).toMatchInlineSnapshot(` + "import { UserRole } from './my-file'; + import { UserStatus2X as UserStatus } from './my-file'; + type Exact = { [K in keyof T]: T[K] }; + export { UserRole }; + + export { UserStatus }; + + export type MeQueryVariables = Exact<{ + role: UserRole; + status: UserStatus; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('removes underscore from enum values', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + A_B_C + X_Y_Z + _TEST + My_Value + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + ABC = 'A_B_C', + XYZ = 'X_Y_Z', + Test = '_TEST', + MyValue = 'My_Value' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('keeps underscores in enum values when the value is only underscores', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + _ + __ + _TEST + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + _ = '_', + __ = '__', + Test = '_TEST' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('adds typesPrefix to enum when enumPrefix is true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { typesPrefix: 'I', enumPrefix: true })]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type IUserRole = + | 'ADMIN' + | 'CUSTOMER'; + + export type IMeQueryVariables = Exact<{ + role: IUserRole; + }>; + + + export type IMeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('does not add typesPrefix to enum when enumPrefix is false', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { typesPrefix: 'I', enumPrefix: false })]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRole = + | 'ADMIN' + | 'CUSTOMER'; + + export type IMeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type IMeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('adds typesSuffix to enum when enumSuffix is true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { typesSuffix: 'Z', enumSuffix: true })]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRoleZ = + | 'ADMIN' + | 'CUSTOMER'; + + export type MeQueryVariablesZ = Exact<{ + role: UserRoleZ; + }>; + + + export type MeQueryZ = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('does not add typesSuffix to enum when enumSuffix is false', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { typesSuffix: 'Z', enumSuffix: false })]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRole = + | 'ADMIN' + | 'CUSTOMER'; + + export type MeQueryVariablesZ = Exact<{ + role: UserRole; + }>; + + + export type MeQueryZ = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('keeps enum value naming convention when namingConvention.enumValues is `keep`', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + namingConvention: { + typeNames: 'change-case-all#lowerCase', + enumValues: 'keep', + }, + }), + ]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type userrole = + | 'ADMIN' + | 'CUSTOMER'; + + export type mequeryvariables = Exact<{ + role: userrole; + }>; + + + export type mequery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('uses custom enum naming convention when namingConvention.enumValues is provided and enumType is native', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + enumType: 'native', + namingConvention: { + typeNames: 'keep', + enumValues: 'change-case-all#lowerCase', + }, + }), + ]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export enum UserRole { + admin = 'ADMIN', + customer = 'CUSTOMER' + } + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + + it('does not contain "export" when noExport is set to true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { + noExport: true, + }), + ]); + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + type UserRole = + | 'ADMIN' + | 'CUSTOMER'; + + type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); +}); + +describe('TypeScript Operations Plugin - Enum `%future added value`', () => { + it('adds `%future added value` to the type when enumType is `string-literal` and futureProofEnums is true', async () => { + const schema = buildSchema(/* GraphQL */ ` + type Query { + me: User + } + + type User { + id: ID! + name: String! + role: UserRole! + createdAt: DateTime! + } + + enum UserRole { + ADMIN + CUSTOMER + } + + scalar DateTime + `); + const document = parse(/* GraphQL */ ` + query Me($role: UserRole!) { + me { + id + } + } + `); + + const result = mergeOutputs([await plugin(schema, [{ document }], { futureProofEnums: true })]); + + expect(result).toMatchInlineSnapshot(` + "type Exact = { [K in keyof T]: T[K] }; + export type UserRole = + | 'ADMIN' + | 'CUSTOMER' + | '%future added value'; + + export type MeQueryVariables = Exact<{ + role: UserRole; + }>; + + + export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); +}); diff --git a/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts b/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts index e3a2a69f90e..f3106cf72a1 100644 --- a/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts +++ b/packages/plugins/typescript/operations/tests/ts-documents.standalone.spec.ts @@ -1,5 +1,5 @@ import { mergeOutputs } from '@graphql-codegen/plugin-helpers'; -import { validateTs } from '@graphql-codegen/testing'; +// import { validateTs } from '@graphql-codegen/testing'; import { buildSchema, parse } from 'graphql'; import { plugin } from '../src/index.js'; @@ -172,1298 +172,3 @@ describe('TypeScript Operations Plugin - Standalone', () => { `); }); }); - -describe('TypeScript Operations Plugin - Enum', () => { - it('does not generate enums if not used in variables and result', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], {})]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export type MeQueryVariables = Exact<{ [key: string]: never; }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `native-numeric` enum', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native-numeric' })]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export enum UserRole { - Admin = 0, - Customer = 1 - } - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `const` enum', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - A_B_C - X_Y_Z - _TEST - My_Value - _123 - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'const' })]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export const UserRole = { - ABC: 'A_B_C', - XYZ: 'X_Y_Z', - Test: '_TEST', - MyValue: 'My_Value', - '123': '_123' - } as const; - - export type UserRole = typeof UserRole[keyof typeof UserRole]; - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `native-const` enum', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - """ - Multiline comment test - """ - enum UserRole { - ADMIN - CUSTOMER @deprecated(reason: "Enum value CUSTOMER has been deprecated.") - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native-const' })]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - /** Multiline comment test */ - export const enum UserRole { - Admin = 'ADMIN', - /** @deprecated Enum value CUSTOMER has been deprecated. */ - Customer = 'CUSTOMER' - }; - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `native` enum', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export enum UserRole { - Admin = 'ADMIN', - Customer = 'CUSTOMER' - } - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `enumValues` with `string-literal` enum', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - A_B_C - X_Y_Z - _TEST - My_Value - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumType: 'string-literal', - enumValues: { - UserRole: { - A_B_C: 0, - X_Y_Z: 'Foo', - _TEST: 'Bar', - My_Value: 1, - }, - }, - }), - ]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export type UserRole = - | 0 - | 'Foo' - | 'Bar' - | 1; - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `enumValues` with `const` enum', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - A_B_C - X_Y_Z - _TEST - My_Value - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumType: 'const', - enumValues: { - UserRole: { - A_B_C: 0, - X_Y_Z: 'Foo', - _TEST: 'Bar', - My_Value: 1, - }, - }, - }), - ]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export const UserRole = { - ABC: 0, - XYZ: 'Foo', - Test: 'Bar', - MyValue: 1 - } as const; - - export type UserRole = typeof UserRole[keyof typeof UserRole]; - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `enumValues` with `native` enum', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumType: 'native', - enumValues: { - UserRole: { - ADMIN: 0, - CUSTOMER: 'test', - }, - }, - }), - ]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export enum UserRole { - Admin = 0, - Customer = 'test' - } - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `enumValues` as file import', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumValues: { - UserRole: './my-file#MyEnum', - }, - }), - ]); - - expect(result).toMatchInlineSnapshot(` - "import { MyEnum as UserRole } from './my-file'; - type Exact = { [K in keyof T]: T[K] }; - export { UserRole }; - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `enumValues` with custom imported enum from namespace with different name', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumValues: { - UserRole: './my-file#NS.ETest', - }, - }), - ]); - - expect(result).toMatchInlineSnapshot(` - "import { NS } from './my-file'; - import UserRole = NS.ETest; - type Exact = { [K in keyof T]: T[K] }; - export { UserRole }; - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `enumValues` with custom imported enum from namespace with the same name', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumValues: { - UserRole: './my-file#NS.UserRole', - }, - }), - ]); - - expect(result).toMatchInlineSnapshot(` - "import { NS } from './my-file'; - import UserRole = NS.UserRole; - type Exact = { [K in keyof T]: T[K] }; - export { UserRole }; - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `enumValues` from a single file', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - enum UserStatus { - ACTIVE - PENDING - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!, $status: UserStatus!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumType: 'native', - enumValues: './my-file', - }), - ]); - - expect(result).toMatchInlineSnapshot(` - "import { UserRole } from './my-file'; - import { UserStatus } from './my-file'; - type Exact = { [K in keyof T]: T[K] }; - export { UserRole }; - - export { UserStatus }; - - export type MeQueryVariables = Exact<{ - role: UserRole; - status: UserStatus; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('handles `enumValues` from a single file when specified as string', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - enum UserStatus { - ACTIVE - PENDING - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!, $status: UserStatus!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumType: 'native', - enumValues: { UserRole: './my-file#UserRole', UserStatus: './my-file#UserStatus2X' }, - }), - ]); - - expect(result).toMatchInlineSnapshot(` - "import { UserRole } from './my-file'; - import { UserStatus2X as UserStatus } from './my-file'; - type Exact = { [K in keyof T]: T[K] }; - export { UserRole }; - - export { UserStatus }; - - export type MeQueryVariables = Exact<{ - role: UserRole; - status: UserStatus; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('removes underscore from enum values', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - A_B_C - X_Y_Z - _TEST - My_Value - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export enum UserRole { - ABC = 'A_B_C', - XYZ = 'X_Y_Z', - Test = '_TEST', - MyValue = 'My_Value' - } - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('keeps underscores in enum values when the value is only underscores', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - _ - __ - _TEST - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native' })]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export enum UserRole { - _ = '_', - __ = '__', - Test = '_TEST' - } - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('adds typesPrefix to enum when enumPrefix is true', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { typesPrefix: 'I', enumPrefix: true })]); - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export type IUserRole = - | 'ADMIN' - | 'CUSTOMER'; - - export type IMeQueryVariables = Exact<{ - role: IUserRole; - }>; - - - export type IMeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('does not add typesPrefix to enum when enumPrefix is false', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { typesPrefix: 'I', enumPrefix: false })]); - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export type UserRole = - | 'ADMIN' - | 'CUSTOMER'; - - export type IMeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type IMeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('adds typesSuffix to enum when enumSuffix is true', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { typesSuffix: 'Z', enumSuffix: true })]); - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export type UserRoleZ = - | 'ADMIN' - | 'CUSTOMER'; - - export type MeQueryVariablesZ = Exact<{ - role: UserRoleZ; - }>; - - - export type MeQueryZ = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('does not add typesSuffix to enum when enumSuffix is false', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { typesSuffix: 'Z', enumSuffix: false })]); - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export type UserRole = - | 'ADMIN' - | 'CUSTOMER'; - - export type MeQueryVariablesZ = Exact<{ - role: UserRole; - }>; - - - export type MeQueryZ = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('keeps enum value naming convention when namingConvention.enumValues is `keep`', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - namingConvention: { - typeNames: 'change-case-all#lowerCase', - enumValues: 'keep', - }, - }), - ]); - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export type userrole = - | 'ADMIN' - | 'CUSTOMER'; - - export type mequeryvariables = Exact<{ - role: userrole; - }>; - - - export type mequery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('uses custom enum naming convention when namingConvention.enumValues is provided and enumType is native', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - enumType: 'native', - namingConvention: { - typeNames: 'keep', - enumValues: 'change-case-all#lowerCase', - }, - }), - ]); - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export enum UserRole { - admin = 'ADMIN', - customer = 'CUSTOMER' - } - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('does not contain "export" when noExport is set to true', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([ - await plugin(schema, [{ document }], { - noExport: true, - }), - ]); - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - type UserRole = - | 'ADMIN' - | 'CUSTOMER'; - - type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); -}); - -describe('TypeScript Operations Plugin - Enum `%future added value`', () => { - it('adds `%future added value` to the type when enumType is `string-literal` and futureProofEnums is true', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { futureProofEnums: true })]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export type UserRole = - | 'ADMIN' - | 'CUSTOMER' - | '%future added value'; - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it('it adds `%future added value` to output when enumType is `native` and futureProofEnums is true', async () => { - const schema = buildSchema(/* GraphQL */ ` - type Query { - me: User - } - - type User { - id: ID! - name: String! - role: UserRole! - createdAt: DateTime! - } - - enum UserRole { - ADMIN - CUSTOMER - } - - scalar DateTime - `); - const document = parse(/* GraphQL */ ` - query Me($role: UserRole!) { - me { - id - role - } - } - `); - - const result = mergeOutputs([await plugin(schema, [{ document }], { enumType: 'native', futureProofEnums: true })]); - - expect(result).toMatchInlineSnapshot(` - "type Exact = { [K in keyof T]: T[K] }; - export enum UserRole { - Admin = 'ADMIN', - Customer = 'CUSTOMER' - } - - export type MeQueryVariables = Exact<{ - role: UserRole; - }>; - - - export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string, role: UserRole } | null }; - " - `); - - validateTs(result, undefined, undefined, undefined, undefined, true); - }); - - it.todo('adds `%future added value` to enum usage when futureProofEnums is true and enumType is string-literal'); - it.todo('adds `%future added value` to enum usage when futureProofEnums is true and allowEnumStringTypes is true'); -});