diff --git a/tests/myzod.spec.ts b/tests/myzod.spec.ts index 1a53f782..cd9bc95a 100644 --- a/tests/myzod.spec.ts +++ b/tests/myzod.spec.ts @@ -3,179 +3,236 @@ import dedent from 'ts-dedent'; import { plugin } from '../src/index'; +const initialEmitValue = dedent(` + export const definedNonNullAnySchema = myzod.object({}); + + + `) + +function removedInitialEmitValue(content: string) { + return content.replace(initialEmitValue, ''); +} + describe('myzod', () => { - it.each([ - [ - 'non-null and defined', - { - textSchema: /* GraphQL */ ` - input PrimitiveInput { - a: ID! - b: String! - c: Boolean! - d: Int! - e: Float! - } - `, - wantContains: [ - 'export function PrimitiveInputSchema(): myzod.Type {', - 'a: myzod.string()', - 'b: myzod.string()', - 'c: myzod.boolean()', - 'd: myzod.number()', - 'e: myzod.number()', - ], - scalars: { - ID: 'string', - }, - }, - ], - [ - 'nullish', - { - textSchema: /* GraphQL */ ` - input PrimitiveInput { - a: ID - b: String - c: Boolean - d: Int - e: Float - z: String! # no defined check - } - `, - wantContains: [ - 'export function PrimitiveInputSchema(): myzod.Type {', - // alphabet order - 'a: myzod.string().optional().nullable(),', - 'b: myzod.string().optional().nullable(),', - 'c: myzod.boolean().optional().nullable(),', - 'd: myzod.number().optional().nullable(),', - 'e: myzod.number().optional().nullable(),', - ], - scalars: { - ID: 'string', - }, - }, - ], - [ - 'array', - { - textSchema: /* GraphQL */ ` - input ArrayInput { - a: [String] - b: [String!] - c: [String!]! - d: [[String]] - e: [[String]!] - f: [[String]!]! - } - `, - wantContains: [ - 'export function ArrayInputSchema(): myzod.Type {', - 'a: myzod.array(myzod.string().nullable()).optional().nullable(),', - 'b: myzod.array(myzod.string()).optional().nullable(),', - 'c: myzod.array(myzod.string()),', - 'd: myzod.array(myzod.array(myzod.string().nullable()).optional().nullable()).optional().nullable(),', - 'e: myzod.array(myzod.array(myzod.string().nullable())).optional().nullable(),', - 'f: myzod.array(myzod.array(myzod.string().nullable()))', - ], - scalars: undefined, - }, - ], - [ - 'ref input object', - { - textSchema: /* GraphQL */ ` - input AInput { - b: BInput! - } - input BInput { - c: CInput! - } - input CInput { - a: AInput! - } - `, - wantContains: [ - 'export function AInputSchema(): myzod.Type {', - 'b: myzod.lazy(() => BInputSchema())', - 'export function BInputSchema(): myzod.Type {', - 'c: myzod.lazy(() => CInputSchema())', - 'export function CInputSchema(): myzod.Type {', - 'a: myzod.lazy(() => AInputSchema())', - ], - scalars: undefined, - }, - ], - [ - 'nested input object', - { - textSchema: /* GraphQL */ ` - input NestedInput { - child: NestedInput - childrens: [NestedInput] - } - `, - wantContains: [ - 'export function NestedInputSchema(): myzod.Type {', - 'child: myzod.lazy(() => NestedInputSchema().optional().nullable()),', - 'childrens: myzod.array(myzod.lazy(() => NestedInputSchema().nullable())).optional().nullable()', - ], - scalars: undefined, + it('non-null and defined', async () => { + const schema = buildSchema(` + input PrimitiveInput { + a: ID! + b: String! + c: Boolean! + d: Int! + e: Float! + } + `); + const result = await plugin(schema, [], { + schema: 'myzod', + scalars: { + ID: 'string', }, - ], - [ - 'enum', - { - textSchema: /* GraphQL */ ` - enum PageType { - PUBLIC - BASIC_AUTH - } - input PageInput { - pageType: PageType! - } - `, - wantContains: [ - 'export const PageTypeSchema = myzod.enum(PageType)', - 'export function PageInputSchema(): myzod.Type {', - 'pageType: PageTypeSchema', - ], - scalars: undefined, + }, {}); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as myzod from 'myzod'", + ] + `); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function PrimitiveInputSchema(): myzod.Type { + return myzod.object({ + a: myzod.string(), + b: myzod.string(), + c: myzod.boolean(), + d: myzod.number(), + e: myzod.number() + }) + } + " + `) + }); + + it('nullish', async () => { + const schema = buildSchema(` + input PrimitiveInput { + a: ID + b: String + c: Boolean + d: Int + e: Float + z: String! # no defined check + } + `); + const result = await plugin(schema, [], { + schema: 'myzod', + scalars: { + ID: 'string', }, - ], - [ - 'camelcase', - { - textSchema: /* GraphQL */ ` - input HTTPInput { - method: HTTPMethod - url: URL! - } + }, {}); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as myzod from 'myzod'", + ] + `); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function PrimitiveInputSchema(): myzod.Type { + return myzod.object({ + a: myzod.string().optional().nullable(), + b: myzod.string().optional().nullable(), + c: myzod.boolean().optional().nullable(), + d: myzod.number().optional().nullable(), + e: myzod.number().optional().nullable(), + z: myzod.string() + }) + } + " + `) + }); - enum HTTPMethod { - GET - POST - } + it('array', async () => { + const schema = buildSchema(` + input ArrayInput { + a: [String] + b: [String!] + c: [String!]! + d: [[String]] + e: [[String]!] + f: [[String]!]! + } + `); + const result = await plugin(schema, [], { + schema: 'myzod', + }, {}); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function ArrayInputSchema(): myzod.Type { + return myzod.object({ + a: myzod.array(myzod.string().nullable()).optional().nullable(), + b: myzod.array(myzod.string()).optional().nullable(), + c: myzod.array(myzod.string()), + d: myzod.array(myzod.array(myzod.string().nullable()).optional().nullable()).optional().nullable(), + e: myzod.array(myzod.array(myzod.string().nullable())).optional().nullable(), + f: myzod.array(myzod.array(myzod.string().nullable())) + }) + } + " + `) + }); - scalar URL # unknown scalar, should be any (definedNonNullAnySchema) - `, - wantContains: [ - 'export function HttpInputSchema(): myzod.Type {', - 'export const HttpMethodSchema = myzod.enum(HttpMethod)', - 'method: HttpMethodSchema', - 'url: definedNonNullAnySchema', - ], - scalars: undefined, - }, - ], - ])('%s', async (_, { textSchema, wantContains, scalars }) => { - const schema = buildSchema(textSchema); - const result = await plugin(schema, [], { schema: 'myzod', scalars }, {}); - expect(result.prepend).toContain('import * as myzod from \'myzod\''); - - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + it('ref input object', async () => { + const schema = buildSchema(` + input AInput { + b: BInput! + } + input BInput { + c: CInput! + } + input CInput { + a: AInput! + } + `); + const result = await plugin(schema, [], { + schema: 'myzod', + }, {}); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function AInputSchema(): myzod.Type { + return myzod.object({ + b: myzod.lazy(() => BInputSchema()) + }) + } + + export function BInputSchema(): myzod.Type { + return myzod.object({ + c: myzod.lazy(() => CInputSchema()) + }) + } + + export function CInputSchema(): myzod.Type { + return myzod.object({ + a: myzod.lazy(() => AInputSchema()) + }) + } + " + `) + }); + + it('nested input object', async () => { + const schema = buildSchema(` + input NestedInput { + child: NestedInput + childrens: [NestedInput] + } + `); + const result = await plugin(schema, [], { + schema: 'myzod', + }, {}); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function NestedInputSchema(): myzod.Type { + return myzod.object({ + child: myzod.lazy(() => NestedInputSchema().optional().nullable()), + childrens: myzod.array(myzod.lazy(() => NestedInputSchema().nullable())).optional().nullable() + }) + } + " + `) + }); + + it('enum', async () => { + const schema = buildSchema(` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! + } + `); + const result = await plugin(schema, [], { + schema: 'myzod', + }, {}); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export const PageTypeSchema = myzod.enum(PageType); + + export function PageInputSchema(): myzod.Type { + return myzod.object({ + pageType: PageTypeSchema + }) + } + " + `) + }); + + it('camelcase', async () => { + const schema = buildSchema(` + input HTTPInput { + method: HTTPMethod + url: URL! + } + + enum HTTPMethod { + GET + POST + } + + scalar URL # unknown scalar, should be any (definedNonNullAnySchema) + `); + const result = await plugin(schema, [], { + schema: 'myzod', + }, {}); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export const HttpMethodSchema = myzod.enum(HttpMethod); + + export function HttpInputSchema(): myzod.Type { + return myzod.object({ + method: HttpMethodSchema.optional().nullable(), + url: definedNonNullAnySchema + }) + } + " + `) }); it('with scalars', async () => { @@ -200,8 +257,16 @@ describe('myzod', () => { }, {}, ); - expect(result.content).toContain('phrase: myzod.string()'); - expect(result.content).toContain('times: myzod.number()'); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function SaySchema(): myzod.Type { + return myzod.object({ + phrase: myzod.string(), + times: myzod.number() + }) + } + " + `) }); it('with importFrom', async () => { @@ -219,8 +284,21 @@ describe('myzod', () => { }, {}, ); - expect(result.prepend).toContain('import { Say } from \'./types\''); - expect(result.content).toContain('phrase: myzod.string()'); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as myzod from 'myzod'", + "import { Say } from './types'", + ] + `) + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function SaySchema(): myzod.Type { + return myzod.object({ + phrase: myzod.string() + }) + } + " + `) }); it('with importFrom & useTypeImports', async () => { @@ -239,8 +317,21 @@ describe('myzod', () => { }, {}, ); - expect(result.prepend).toContain('import type { Say } from \'./types\''); - expect(result.content).toContain('phrase: myzod.string()'); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as myzod from 'myzod'", + "import type { Say } from './types'", + ] + `) + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function SaySchema(): myzod.Type { + return myzod.object({ + phrase: myzod.string() + }) + } + " + `) }); it('with enumsAsTypes', async () => { @@ -259,7 +350,11 @@ describe('myzod', () => { }, {}, ); - expect(result.content).toContain('export type PageTypeSchema = myzod.literals(\'PUBLIC\', \'BASIC_AUTH\')'); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export type PageTypeSchema = myzod.literals('PUBLIC', 'BASIC_AUTH'); + " + `) }); it('with notAllowEmptyString', async () => { @@ -284,16 +379,19 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - 'export function PrimitiveInputSchema(): myzod.Type {', - 'a: myzod.string().min(1),', - 'b: myzod.string().min(1),', - 'c: myzod.boolean(),', - 'd: myzod.number(),', - 'e: myzod.number()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function PrimitiveInputSchema(): myzod.Type { + return myzod.object({ + a: myzod.string().min(1), + b: myzod.string().min(1), + c: myzod.boolean(), + d: myzod.number(), + e: myzod.number() + }) + } + " + `) }); it('with notAllowEmptyString issue #386', async () => { @@ -318,13 +416,21 @@ describe('myzod', () => { }, {}, ); - const wantContain = dedent` - export function InputNestedSchema(): myzod.Type { - return myzod.object({ - field: myzod.string().min(1) - }) - }`; - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function InputOneSchema(): myzod.Type { + return myzod.object({ + field: myzod.lazy(() => InputNestedSchema()) + }) + } + + export function InputNestedSchema(): myzod.Type { + return myzod.object({ + field: myzod.string().min(1) + }) + } + " + `) }); it('with scalarSchemas', async () => { @@ -349,14 +455,17 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - 'export function ScalarsInputSchema(): myzod.Type {', - 'date: myzod.date(),', - 'email: myzod.string()', // TODO: Test implementation - 'str: myzod.string()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function ScalarsInputSchema(): myzod.Type { + return myzod.object({ + date: myzod.date(), + email: myzod.string().optional().nullable(), + str: myzod.string() + }) + } + " + `) }); it('with typesPrefix', async () => { const schema = buildSchema(/* GraphQL */ ` @@ -374,8 +483,21 @@ describe('myzod', () => { }, {}, ); - expect(result.prepend).toContain('import { ISay } from \'./types\''); - expect(result.content).toContain('export function ISaySchema(): myzod.Type {'); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as myzod from 'myzod'", + "import { ISay } from './types'", + ] + `) + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function ISaySchema(): myzod.Type { + return myzod.object({ + phrase: myzod.string() + }) + } + " + `) }); it('with typesSuffix', async () => { const schema = buildSchema(/* GraphQL */ ` @@ -393,8 +515,21 @@ describe('myzod', () => { }, {}, ); - expect(result.prepend).toContain('import { SayI } from \'./types\''); - expect(result.content).toContain('export function SayISchema(): myzod.Type {'); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as myzod from 'myzod'", + "import { SayI } from './types'", + ] + `) + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function SayISchema(): myzod.Type { + return myzod.object({ + phrase: myzod.string() + }) + } + " + `) }); describe('issues #19', () => { it('string field', async () => { @@ -419,12 +554,15 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - 'export function UserCreateInputSchema(): myzod.Type {', - 'profile: myzod.string().min(1, "Please input more than 1").max(5000, "Please input less than 5000").optional().nullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function UserCreateInputSchema(): myzod.Type { + return myzod.object({ + profile: myzod.string().min(1, "Please input more than 1").max(5000, "Please input less than 5000").optional().nullable() + }) + } + " + `) }); it('not null field', async () => { const schema = buildSchema(/* GraphQL */ ` @@ -448,12 +586,15 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - 'export function UserCreateInputSchema(): myzod.Type {', - 'profile: myzod.string().min(1, "Please input more than 1").max(5000, "Please input less than 5000")', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function UserCreateInputSchema(): myzod.Type { + return myzod.object({ + profile: myzod.string().min(1, "Please input more than 1").max(5000, "Please input less than 5000") + }) + } + " + `) }); it('list field', async () => { const schema = buildSchema(/* GraphQL */ ` @@ -477,12 +618,15 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - 'export function UserCreateInputSchema(): myzod.Type {', - 'profile: myzod.array(myzod.string().nullable()).min(1, "Please input more than 1").max(5000, "Please input less than 5000").optional().nullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function UserCreateInputSchema(): myzod.Type { + return myzod.object({ + profile: myzod.array(myzod.string().nullable()).min(1, "Please input more than 1").max(5000, "Please input less than 5000").optional().nullable() + }) + } + " + `) }); }); @@ -526,19 +670,25 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - 'export function AuthorSchema(): myzod.Type {', - '__typename: myzod.literal(\'Author\').optional(),', - 'books: myzod.array(BookSchema().nullable()).optional().nullable(),', - 'name: myzod.string().optional().nullable()', - - 'export function BookSchema(): myzod.Type {', - '__typename: myzod.literal(\'Book\').optional(),', - 'author: AuthorSchema().optional().nullable(),', - 'title: myzod.string().optional().nullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function BookSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Book').optional(), + author: AuthorSchema().optional().nullable(), + title: myzod.string().optional().nullable() + }) + } + + export function AuthorSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Author').optional(), + books: myzod.array(BookSchema().nullable()).optional().nullable(), + name: myzod.string().optional().nullable() + }) + } + " + `) for (const wantNotContain of ['Query', 'Mutation', 'Subscription']) expect(result.content).not.toContain(wantNotContain); @@ -597,28 +747,36 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - // User Create Input - 'export function UserCreateInputSchema(): myzod.Type {', - 'name: myzod.string(),', - 'date: myzod.date(),', - 'email: myzod.string().email()', - // Username Update Input - 'export function UsernameUpdateInputSchema(): myzod.Type {', - 'updateInputId: myzod.number(),', - 'updateName: myzod.string()', - // User - 'export function UserSchema(): myzod.Type {', - '__typename: myzod.literal(\'User\').optional(),', - 'id: myzod.string(),', - 'name: myzod.string().optional().nullable(),', - 'age: myzod.number().optional().nullable(),', - 'email: myzod.string().email().optional().nullable(),', - 'isMember: myzod.boolean().optional().nullable(),', - 'createdAt: myzod.date()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function UserCreateInputSchema(): myzod.Type { + return myzod.object({ + name: myzod.string(), + date: myzod.date(), + email: myzod.string().email() + }) + } + + export function UsernameUpdateInputSchema(): myzod.Type { + return myzod.object({ + updateInputId: myzod.number(), + updateName: myzod.string() + }) + } + + export function UserSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('User').optional(), + id: myzod.string(), + name: myzod.string().optional().nullable(), + age: myzod.number().optional().nullable(), + email: myzod.string().email().optional().nullable(), + isMember: myzod.boolean().optional().nullable(), + createdAt: myzod.date() + }) + } + " + `) for (const wantNotContain of ['Query', 'Mutation', 'Subscription']) expect(result.content).not.toContain(wantNotContain); @@ -645,14 +803,27 @@ describe('myzod', () => { {}, ); - const wantContains = [ - // Shape Schema - 'export function ShapeSchema() {', - 'return myzod.union([CircleSchema(), SquareSchema()])', - '}', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function SquareSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Square').optional(), + size: myzod.number().optional().nullable() + }) + } + + export function CircleSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Circle').optional(), + radius: myzod.number().optional().nullable() + }) + } + + export function ShapeSchema() { + return myzod.union([CircleSchema(), SquareSchema()]) + } + " + `) }); it('generate union types with single element', async () => { @@ -680,15 +851,34 @@ describe('myzod', () => { {}, ); - const wantContains = [ - 'export function GeometrySchema(): myzod.Type {', - 'return myzod.object({', - '__typename: myzod.literal(\'Geometry\').optional(),', - 'shape: ShapeSchema().optional().nullable()', - '}', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function SquareSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Square').optional(), + size: myzod.number().optional().nullable() + }) + } + + export function CircleSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Circle').optional(), + radius: myzod.number().optional().nullable() + }) + } + + export function ShapeSchema() { + return myzod.union([CircleSchema(), SquareSchema()]) + } + + export function GeometrySchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Geometry').optional(), + shape: ShapeSchema().optional().nullable() + }) + } + " + `) }); it('correctly reference generated union types', async () => { @@ -709,14 +899,20 @@ describe('myzod', () => { {}, ); - const wantContains = [ - // Shape Schema - 'export function ShapeSchema() {', - 'return CircleSchema()', - '}', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function CircleSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Circle').optional(), + radius: myzod.number().optional().nullable() + }) + } + + export function ShapeSchema() { + return CircleSchema() + } + " + `) }); it('generate enum union types', async () => { @@ -744,13 +940,17 @@ describe('myzod', () => { {}, ); - const wantContains = [ - 'export function AnyTypeSchema() {', - 'return myzod.union([PageTypeSchema, MethodTypeSchema])', - '}', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export const PageTypeSchema = myzod.enum(PageType); + + export const MethodTypeSchema = myzod.enum(MethodType); + + export function AnyTypeSchema() { + return myzod.union([PageTypeSchema, MethodTypeSchema]) + } + " + `) }); it('generate union types with single element, export as const', async () => { @@ -779,14 +979,26 @@ describe('myzod', () => { {}, ); - const wantContains = [ - 'export const GeometrySchema: myzod.Type = myzod.object({', - '__typename: myzod.literal(\'Geometry\').optional(),', - 'shape: ShapeSchema.optional().nullable()', - '}', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export const CircleSchema: myzod.Type = myzod.object({ + __typename: myzod.literal('Circle').optional(), + radius: myzod.number().optional().nullable() + }); + + export const SquareSchema: myzod.Type = myzod.object({ + __typename: myzod.literal('Square').optional(), + size: myzod.number().optional().nullable() + }); + + export const ShapeSchema = myzod.union([CircleSchema, SquareSchema]); + + export const GeometrySchema: myzod.Type = myzod.object({ + __typename: myzod.literal('Geometry').optional(), + shape: ShapeSchema.optional().nullable() + }); + " + `) }); it('with object arguments', async () => { @@ -808,17 +1020,26 @@ describe('myzod', () => { }, {}, ); - const wantContain = dedent` - export function MyTypeFooArgsSchema(): myzod.Type { - return myzod.object({ - a: myzod.string().optional().nullable(), - b: myzod.number(), - c: myzod.boolean().optional().nullable(), - d: myzod.number(), - e: myzod.string().optional().nullable() - }) - }`; - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function MyTypeSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('MyType').optional(), + foo: myzod.string().optional().nullable() + }) + } + + export function MyTypeFooArgsSchema(): myzod.Type { + return myzod.object({ + a: myzod.string().optional().nullable(), + b: myzod.number(), + c: myzod.boolean().optional().nullable(), + d: myzod.number(), + e: myzod.string().optional().nullable() + }) + } + " + `) }); describe('with InterfaceType', () => { @@ -855,13 +1076,16 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - 'export function BookSchema(): myzod.Type {', - 'title: myzod.string().optional().nullable()', - ]; + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function BookSchema(): myzod.Type { + return myzod.object({ + title: myzod.string().optional().nullable() + }) + } + " + `) const wantNotContains = ['__typename: myzod.literal(\'Book\')']; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); for (const wantNotContain of wantNotContains) expect(result.content).not.toContain(wantNotContain); @@ -873,7 +1097,7 @@ describe('myzod', () => { author: Author title: String } - + interface Author { books: [Book] name: String @@ -888,17 +1112,23 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - 'export function AuthorSchema(): myzod.Type {', - 'books: myzod.array(BookSchema().nullable()).optional().nullable(),', - 'name: myzod.string().optional().nullable()', - - 'export function BookSchema(): myzod.Type {', - 'author: AuthorSchema().optional().nullable(),', - 'title: myzod.string().optional().nullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function BookSchema(): myzod.Type { + return myzod.object({ + author: AuthorSchema().optional().nullable(), + title: myzod.string().optional().nullable() + }) + } + + export function AuthorSchema(): myzod.Type { + return myzod.object({ + books: myzod.array(BookSchema().nullable()).optional().nullable(), + name: myzod.string().optional().nullable() + }) + } + " + `) }); it('generate object type contains interface type', async () => { const schema = buildSchema(/* GraphQL */ ` @@ -906,19 +1136,19 @@ describe('myzod', () => { title: String! author: Author! } - + type Textbook implements Book { title: String! author: Author! courses: [String!]! } - + type ColoringBook implements Book { title: String! author: Author! colors: [String!]! } - + type Author { books: [Book!] name: String @@ -933,52 +1163,42 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - [ - 'export function BookSchema(): myzod.Type {', - 'return myzod.object({', - 'title: myzod.string(),', - 'author: AuthorSchema()', - '})', - '}', - ], - - [ - 'export function TextbookSchema(): myzod.Type {', - 'return myzod.object({', - '__typename: myzod.literal(\'Textbook\').optional(),', - 'title: myzod.string(),', - 'author: AuthorSchema(),', - 'courses: myzod.array(myzod.string())', - '})', - '}', - ], - - [ - 'export function ColoringBookSchema(): myzod.Type {', - 'return myzod.object({', - '__typename: myzod.literal(\'ColoringBook\').optional(),', - 'title: myzod.string(),', - 'author: AuthorSchema(),', - 'colors: myzod.array(myzod.string())', - '})', - '}', - ], - - [ - 'export function AuthorSchema(): myzod.Type {', - 'return myzod.object({', - '__typename: myzod.literal(\'Author\').optional()', - 'books: myzod.array(BookSchema()).optional().nullable()', - 'name: myzod.string().optional().nullable()', - '})', - '}', - ], - ]; - for (const wantContain of wantContains) { - for (const wantContainLine of wantContain) - expect(result.content).toContain(wantContainLine); - } + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function BookSchema(): myzod.Type { + return myzod.object({ + title: myzod.string(), + author: AuthorSchema() + }) + } + + export function TextbookSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Textbook').optional(), + title: myzod.string(), + author: AuthorSchema(), + courses: myzod.array(myzod.string()) + }) + } + + export function ColoringBookSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('ColoringBook').optional(), + title: myzod.string(), + author: AuthorSchema(), + colors: myzod.array(myzod.string()) + }) + } + + export function AuthorSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('Author').optional(), + books: myzod.array(BookSchema()).optional().nullable(), + name: myzod.string().optional().nullable() + }) + } + " + `) }); }); }); @@ -1006,14 +1226,16 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - // User Create Input - 'export function UserCreateInputSchema(): myzod.Type {', - 'name: myzod.string().pattern(/^Sir/),', - 'age: myzod.number().min(0).max(100)', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function UserCreateInputSchema(): myzod.Type { + return myzod.object({ + name: myzod.string().pattern(/^Sir/), + age: myzod.number().min(0).max(100) + }) + } + " + `) }); it('exports as const instead of func', async () => { @@ -1031,7 +1253,13 @@ describe('myzod', () => { }, {}, ); - expect(result.content).toContain('export const SaySchema: myzod.Type = myzod.object({'); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export const SaySchema: myzod.Type = myzod.object({ + phrase: myzod.string() + }); + " + `) }); it('generate both input & type, export as const', async () => { @@ -1075,25 +1303,25 @@ describe('myzod', () => { }, {}, ); - const wantContains = [ - // User Create Input - 'export const UserCreateInputSchema: myzod.Type = myzod.object({', - 'name: myzod.string(),', - 'date: myzod.date(),', - 'email: myzod.string().email()', - // User - 'export const UserSchema: myzod.Type = myzod.object({', - '__typename: myzod.literal(\'User\').optional(),', - 'id: myzod.string(),', - 'name: myzod.string().optional().nullable(),', - 'age: myzod.number().optional().nullable(),', - 'email: myzod.string().email().optional().nullable(),', - 'isMember: myzod.boolean().optional().nullable(),', - 'createdAt: myzod.date()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export const UserCreateInputSchema: myzod.Type = myzod.object({ + name: myzod.string(), + date: myzod.date(), + email: myzod.string().email() + }); + export const UserSchema: myzod.Type = myzod.object({ + __typename: myzod.literal('User').optional(), + id: myzod.string(), + name: myzod.string().optional().nullable(), + age: myzod.number().optional().nullable(), + email: myzod.string().email().optional().nullable(), + isMember: myzod.boolean().optional().nullable(), + createdAt: myzod.date() + }); + " + `) for (const wantNotContain of ['Query', 'Mutation', 'Subscription']) expect(result.content).not.toContain(wantNotContain); }); @@ -1126,13 +1354,17 @@ describe('myzod', () => { }, {}, ); - const wantContain = dedent` - export function QueryInputSchema(): myzod.Type { - return myzod.object({ - _dummy: TestSchema.optional().nullable() - }) - }`; - expect(result.content).toContain(wantContain); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export const TestSchema = myzod.enum(Test); + + export function QueryInputSchema(): myzod.Type { + return myzod.object({ + _dummy: TestSchema.optional().nullable() + }) + } + " + `) }); it('with default input values', async () => { @@ -1159,13 +1391,20 @@ describe('myzod', () => { {}, ); - expect(result.content).toContain('export const PageTypeSchema = myzod.enum(PageType)'); - expect(result.content).toContain('export function PageInputSchema(): myzod.Type'); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export const PageTypeSchema = myzod.enum(PageType); - expect(result.content).toContain('pageType: PageTypeSchema.default("PUBLIC")'); - expect(result.content).toContain('greeting: myzod.string().default("Hello").optional().nullable()'); - expect(result.content).toContain('score: myzod.number().default(100).optional().nullable()'); - expect(result.content).toContain('ratio: myzod.number().default(0.5).optional().nullable()'); - expect(result.content).toContain('isMember: myzod.boolean().default(true).optional().nullable()'); + export function PageInputSchema(): myzod.Type { + return myzod.object({ + pageType: PageTypeSchema.default("PUBLIC"), + greeting: myzod.string().default("Hello").optional().nullable(), + score: myzod.number().default(100).optional().nullable(), + ratio: myzod.number().default(0.5).optional().nullable(), + isMember: myzod.boolean().default(true).optional().nullable() + }) + } + " + `) }); }); diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index 2593fc03..bd927891 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -1,181 +1,242 @@ import { buildClientSchema, buildSchema, introspectionFromSchema } from 'graphql'; -import dedent from 'ts-dedent'; import { plugin } from '../src/index'; describe('yup', () => { - it.each([ - [ - 'defined', - { - textSchema: /* GraphQL */ ` - input PrimitiveInput { - a: ID! - b: String! - c: Boolean! - d: Int! - e: Float! - } - `, - wantContains: [ - 'export function PrimitiveInputSchema(): yup.ObjectSchema', - 'a: yup.string().defined()', - 'b: yup.string().defined()', - 'c: yup.boolean().defined()', - 'd: yup.number().defined()', - 'e: yup.number().defined()', - ], - scalars: { - ID: 'string', - }, - }, - ], - [ - 'optional', - { - textSchema: /* GraphQL */ ` - input PrimitiveInput { - a: ID - b: String - c: Boolean - d: Int - e: Float - z: String! # no defined check - } - `, - wantContains: [ - 'export function PrimitiveInputSchema(): yup.ObjectSchema', - // alphabet order - 'a: yup.string().defined().nullable().optional(),', - 'b: yup.string().defined().nullable().optional(),', - 'c: yup.boolean().defined().nullable().optional(),', - 'd: yup.number().defined().nullable().optional(),', - 'e: yup.number().defined().nullable().optional(),', - ], - scalars: { - ID: 'string', - }, - }, - ], - [ - 'array', - { - textSchema: /* GraphQL */ ` - input ArrayInput { - a: [String] - b: [String!] - c: [String!]! - d: [[String]] - e: [[String]!] - f: [[String]!]! - } - `, - wantContains: [ - 'export function ArrayInputSchema(): yup.ObjectSchema', - 'a: yup.array(yup.string().defined().nullable()).defined().nullable().optional(),', - 'b: yup.array(yup.string().defined().nonNullable()).defined().nullable().optional(),', - 'c: yup.array(yup.string().defined().nonNullable()).defined(),', - 'd: yup.array(yup.array(yup.string().defined().nullable()).defined().nullable()).defined().nullable().optional(),', - 'e: yup.array(yup.array(yup.string().defined().nullable()).defined()).defined().nullable().optional(),', - 'f: yup.array(yup.array(yup.string().defined().nullable()).defined()).defined()', - ], - scalars: undefined, - }, - ], - [ - 'ref input object', - { - textSchema: /* GraphQL */ ` - input AInput { - b: BInput! - } - input BInput { - c: CInput! - } - input CInput { - a: AInput! - } - `, - wantContains: [ - 'export function AInputSchema(): yup.ObjectSchema', - 'b: yup.lazy(() => BInputSchema().nonNullable())', - 'export function BInputSchema(): yup.ObjectSchema', - 'c: yup.lazy(() => CInputSchema().nonNullable())', - 'export function CInputSchema(): yup.ObjectSchema', - 'a: yup.lazy(() => AInputSchema().nonNullable())', - ], - scalars: undefined, - }, - ], - [ - 'nested input object', + it('defined', async () => { + const textSchema = /* GraphQL */ ` + input PrimitiveInput { + a: ID! + b: String! + c: Boolean! + d: Int! + e: Float! + } + `; + const scalars = { ID: 'string' }; + + const schema = buildSchema(textSchema); + const result = await plugin(schema, [], { scalars }, {}); + + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as yup from 'yup'", + ] + `) + + expect(result.content).toMatchInlineSnapshot(` + " + + export function PrimitiveInputSchema(): yup.ObjectSchema { + return yup.object({ + a: yup.string().defined().nonNullable(), + b: yup.string().defined().nonNullable(), + c: yup.boolean().defined().nonNullable(), + d: yup.number().defined().nonNullable(), + e: yup.number().defined().nonNullable() + }) + } + " + `) + }); + + it('optional', async () => { + const textSchema = /* GraphQL */ ` + input PrimitiveInput { + a: ID + b: String + c: Boolean + d: Int + e: Float + z: String! # no defined check + } + `; + const scalars = { ID: 'string' }; + + const schema = buildSchema(textSchema); + const result = await plugin(schema, [], { scalars }, {}); + expect(result.content).toMatchInlineSnapshot(` + " + + export function PrimitiveInputSchema(): yup.ObjectSchema { + return yup.object({ + a: yup.string().defined().nullable().optional(), + b: yup.string().defined().nullable().optional(), + c: yup.boolean().defined().nullable().optional(), + d: yup.number().defined().nullable().optional(), + e: yup.number().defined().nullable().optional(), + z: yup.string().defined().nonNullable() + }) + } + " + `) + }); + + it('array', async () => { + const textSchema = /* GraphQL */ ` + input ArrayInput { + a: [String] + b: [String!] + c: [String!]! + d: [[String]] + e: [[String]!] + f: [[String]!]! + } + `; + + const schema = buildSchema(textSchema); + const result = await plugin(schema, [], {}, {}); + + expect(result.content).toMatchInlineSnapshot(` + " + + export function ArrayInputSchema(): yup.ObjectSchema { + return yup.object({ + a: yup.array(yup.string().defined().nullable()).defined().nullable().optional(), + b: yup.array(yup.string().defined().nonNullable()).defined().nullable().optional(), + c: yup.array(yup.string().defined().nonNullable()).defined(), + d: yup.array(yup.array(yup.string().defined().nullable()).defined().nullable()).defined().nullable().optional(), + e: yup.array(yup.array(yup.string().defined().nullable()).defined()).defined().nullable().optional(), + f: yup.array(yup.array(yup.string().defined().nullable()).defined()).defined() + }) + } + " + `) + }); + + it('ref input object', async () => { + const textSchema = /* GraphQL */ ` + input AInput { + b: BInput! + } + input BInput { + c: CInput! + } + input CInput { + a: AInput! + } + `; + + const schema = buildSchema(textSchema); + const result = await plugin(schema, [], {}, {}); + + expect(result.content).toMatchInlineSnapshot(` + " + + export function AInputSchema(): yup.ObjectSchema { + return yup.object({ + b: yup.lazy(() => BInputSchema().nonNullable()) + }) + } + + export function BInputSchema(): yup.ObjectSchema { + return yup.object({ + c: yup.lazy(() => CInputSchema().nonNullable()) + }) + } + + export function CInputSchema(): yup.ObjectSchema { + return yup.object({ + a: yup.lazy(() => AInputSchema().nonNullable()) + }) + } + " + `) + }); + + it('nested input object', async () => { + const schema = buildSchema(/* GraphQL */ ` + input NestedInput { + child: NestedInput + childrens: [NestedInput] + } + `); + const result = await plugin( + schema, + [], { - textSchema: /* GraphQL */ ` - input NestedInput { - child: NestedInput - childrens: [NestedInput] - } - `, - wantContains: [ - 'export function NestedInputSchema(): yup.ObjectSchema', - 'child: yup.lazy(() => NestedInputSchema()).optional(),', - 'childrens: yup.array(yup.lazy(() => NestedInputSchema())).defined().nullable().optional()', - ], scalars: undefined, }, - ], - [ - 'enum', + {}, + ); + expect(result.content).toMatchInlineSnapshot(` + " + + export function NestedInputSchema(): yup.ObjectSchema { + return yup.object({ + child: yup.lazy(() => NestedInputSchema()).optional(), + childrens: yup.array(yup.lazy(() => NestedInputSchema())).defined().nullable().optional() + }) + } + " + `) + }); + + it('enum', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! + } + `); + const result = await plugin( + schema, + [], { - textSchema: /* GraphQL */ ` - enum PageType { - PUBLIC - BASIC_AUTH - } - input PageInput { - pageType: PageType! - } - `, - wantContains: [ - 'export const PageTypeSchema = yup.string().oneOf(Object.values(PageType)).defined();', - 'export function PageInputSchema(): yup.ObjectSchema', - 'pageType: PageTypeSchema.nonNullable()', - ], scalars: undefined, }, - ], - [ - 'camelcase', - { - textSchema: /* GraphQL */ ` - input HTTPInput { - method: HTTPMethod - url: URL! - } + {}, + ); + expect(result.content).toMatchInlineSnapshot(` + " + export const PageTypeSchema = yup.string().oneOf(Object.values(PageType)).defined(); - enum HTTPMethod { - GET - POST - } + export function PageInputSchema(): yup.ObjectSchema { + return yup.object({ + pageType: PageTypeSchema.nonNullable() + }) + } + " + `) + }); - scalar URL # unknown scalar, should be any (yup.mixed()) - `, - wantContains: [ - 'export function HttpInputSchema(): yup.ObjectSchema', - 'export const HttpMethodSchema = yup.string().oneOf(Object.values(HttpMethod)).defined();', - 'method: HttpMethodSchema.nullable().optional(),', - 'url: yup.mixed().nonNullable()', - ], + it('camelcase', async () => { + const schema = buildSchema(/* GraphQL */ ` + input HTTPInput { + method: HTTPMethod + url: URL! + } + + enum HTTPMethod { + GET + POST + } + + scalar URL # unknown scalar, should be any (yup.mixed()) + `); + const result = await plugin( + schema, + [], + { scalars: undefined, }, - ], - ])('%s', async (_, { textSchema, wantContains, scalars }) => { - const schema = buildSchema(textSchema); - const result = await plugin(schema, [], { scalars }, {}); - expect(result.prepend).toContain('import * as yup from \'yup\''); + {}, + ); + expect(result.content).toMatchInlineSnapshot(` + " + export const HttpMethodSchema = yup.string().oneOf(Object.values(HttpMethod)).defined(); - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + export function HttpInputSchema(): yup.ObjectSchema { + return yup.object({ + method: HttpMethodSchema.nullable().optional(), + url: yup.mixed().nonNullable() + }) + } + " + `) }); it('with scalars', async () => { @@ -199,8 +260,17 @@ describe('yup', () => { }, {}, ); - expect(result.content).toContain('phrase: yup.string().defined()'); - expect(result.content).toContain('times: yup.number().defined()'); + expect(result.content).toMatchInlineSnapshot(` + " + + export function SaySchema(): yup.ObjectSchema { + return yup.object({ + phrase: yup.string().defined().nonNullable(), + times: yup.number().defined().nonNullable() + }) + } + " + `) }); it('with importFrom', async () => { @@ -217,8 +287,22 @@ describe('yup', () => { }, {}, ); - expect(result.prepend).toContain('import { Say } from \'./types\''); - expect(result.content).toContain('phrase: yup.string().defined()'); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as yup from 'yup'", + "import { Say } from './types'", + ] + `) + expect(result.content).toMatchInlineSnapshot(` + " + + export function SaySchema(): yup.ObjectSchema { + return yup.object({ + phrase: yup.string().defined().nonNullable() + }) + } + " + `) }); it('with importFrom & useTypeImports', async () => { @@ -236,8 +320,22 @@ describe('yup', () => { }, {}, ); - expect(result.prepend).toContain('import type { Say } from \'./types\''); - expect(result.content).toContain('phrase: yup.string().defined()'); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as yup from 'yup'", + "import type { Say } from './types'", + ] + `) + expect(result.content).toMatchInlineSnapshot(` + " + + export function SaySchema(): yup.ObjectSchema { + return yup.object({ + phrase: yup.string().defined().nonNullable() + }) + } + " + `) }); it('with enumsAsTypes', async () => { @@ -255,9 +353,11 @@ describe('yup', () => { }, {}, ); - expect(result.content).toContain( - 'export const PageTypeSchema = yup.string().oneOf([\'PUBLIC\', \'BASIC_AUTH\']).defined();', - ); + expect(result.content).toMatchInlineSnapshot(` + " + export const PageTypeSchema = yup.string().oneOf(['PUBLIC', 'BASIC_AUTH']).defined(); + " + `) }); it('with notAllowEmptyString', async () => { @@ -281,16 +381,20 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - 'export function PrimitiveInputSchema(): yup.ObjectSchema', - 'a: yup.string().defined().required(),', - 'b: yup.string().defined().required(),', - 'c: yup.boolean().defined().nonNullable(),', - 'd: yup.number().defined().nonNullable(),', - 'e: yup.number().defined().nonNullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + export function PrimitiveInputSchema(): yup.ObjectSchema { + return yup.object({ + a: yup.string().defined().required(), + b: yup.string().defined().required(), + c: yup.boolean().defined().nonNullable(), + d: yup.number().defined().nonNullable(), + e: yup.number().defined().nonNullable() + }) + } + " + `) }); it('with notAllowEmptyString issue #386', async () => { @@ -315,13 +419,22 @@ describe('yup', () => { }, {}, ); - const wantContain = dedent` - export function InputNestedSchema(): yup.ObjectSchema { - return yup.object({ - field: yup.string().defined().required() - }) - }`; - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + export function InputOneSchema(): yup.ObjectSchema { + return yup.object({ + field: yup.lazy(() => InputNestedSchema().nonNullable()) + }) + } + + export function InputNestedSchema(): yup.ObjectSchema { + return yup.object({ + field: yup.string().defined().required() + }) + } + " + `) }); it('with scalarSchemas', async () => { @@ -345,14 +458,18 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - 'export function ScalarsInputSchema(): yup.ObjectSchema', - 'date: yup.date().defined().nonNullable(),', - 'email: yup.string().email().defined().nullable().optional(),', - 'str: yup.string().defined().nonNullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + export function ScalarsInputSchema(): yup.ObjectSchema { + return yup.object({ + date: yup.date().defined().nonNullable(), + email: yup.string().email().defined().nullable().optional(), + str: yup.string().defined().nonNullable() + }) + } + " + `) }); it('with typesPrefix', async () => { @@ -370,8 +487,22 @@ describe('yup', () => { }, {}, ); - expect(result.prepend).toContain('import { ISay } from \'./types\''); - expect(result.content).toContain('export function ISaySchema(): yup.ObjectSchema {'); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as yup from 'yup'", + "import { ISay } from './types'", + ] + `) + expect(result.content).toMatchInlineSnapshot(` + " + + export function ISaySchema(): yup.ObjectSchema { + return yup.object({ + phrase: yup.string().defined().nonNullable() + }) + } + " + `) }); it('with typesSuffix', async () => { @@ -389,8 +520,22 @@ describe('yup', () => { }, {}, ); - expect(result.prepend).toContain('import { SayI } from \'./types\''); - expect(result.content).toContain('export function SayISchema(): yup.ObjectSchema {'); + expect(result.prepend).toMatchInlineSnapshot(` + [ + "import * as yup from 'yup'", + "import { SayI } from './types'", + ] + `) + expect(result.content).toMatchInlineSnapshot(` + " + + export function SayISchema(): yup.ObjectSchema { + return yup.object({ + phrase: yup.string().defined().nonNullable() + }) + } + " + `) }); describe('with withObjectType', () => { @@ -438,24 +583,40 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - 'export function AuthorSchema(): yup.ObjectSchema {', - '__typename: yup.string<\'Author\'>().optional(),', - 'books: yup.array(BookSchema().nullable()).defined().nullable().optional(),', - 'name: yup.string().defined().nullable().optional()', - - 'export function BookSchema(): yup.ObjectSchema {', - '__typename: yup.string<\'Book\'>().optional(),', - 'author: AuthorSchema().nullable().optional(),', - 'title: yup.string().defined().nonNullable()', - - 'export function Book2Schema(): yup.ObjectSchema {', - '__typename: yup.string<\'Book2\'>().optional(),', - 'author: AuthorSchema().nonNullable(),', - 'title: yup.string().defined().nullable().optional()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function BookSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Book'>().optional(), + author: AuthorSchema().nullable().optional(), + title: yup.string().defined().nullable().optional() + }) + } + + export function Book2Schema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Book2'>().optional(), + author: AuthorSchema().nonNullable(), + title: yup.string().defined().nonNullable() + }) + } + + export function AuthorSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Author'>().optional(), + books: yup.array(BookSchema().nullable()).defined().nullable().optional(), + name: yup.string().defined().nullable().optional() + }) + } + " + `) for (const wantNotContain of ['Query', 'Mutation', 'Subscription']) expect(result.content).not.toContain(wantNotContain); @@ -515,28 +676,43 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - // User Create Input - 'export function UserCreateInputSchema(): yup.ObjectSchema {', - 'name: yup.string().defined().nonNullable(),', - 'date: yup.date().defined().nonNullable(),', - 'email: yup.string().email().defined().nonNullable()', - // Username Update Input - 'export function UsernameUpdateInputSchema(): yup.ObjectSchema {', - 'updateInputId: yup.number().defined().nonNullable(),', - 'updateName: yup.string().defined().nonNullable()', - // User - 'export function UserSchema(): yup.ObjectSchema {', - '__typename: yup.string<\'User\'>().optional(),', - 'id: yup.string().defined().nonNullable(),', - 'name: yup.string().defined().nullable().optional(),', - 'age: yup.number().defined().nullable().optional(),', - 'isMember: yup.boolean().defined().nullable().optional(),', - 'email: yup.string().email().defined().nullable().optional(),', - 'createdAt: yup.date().defined().nonNullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function UserCreateInputSchema(): yup.ObjectSchema { + return yup.object({ + name: yup.string().defined().nonNullable(), + date: yup.date().defined().nonNullable(), + email: yup.string().email().defined().nonNullable() + }) + } + + export function UsernameUpdateInputSchema(): yup.ObjectSchema { + return yup.object({ + updateInputId: yup.number().defined().nonNullable(), + updateName: yup.string().defined().nonNullable() + }) + } + + export function UserSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'User'>().optional(), + id: yup.string().defined().nonNullable(), + name: yup.string().defined().nullable().optional(), + age: yup.number().defined().nullable().optional(), + email: yup.string().email().defined().nullable().optional(), + isMember: yup.boolean().defined().nullable().optional(), + createdAt: yup.date().defined().nonNullable() + }) + } + " + `) for (const wantNotContain of ['Query', 'Mutation', 'Subscription']) expect(result.content).not.toContain(wantNotContain); @@ -563,14 +739,34 @@ describe('yup', () => { {}, ); - const wantContains = [ - // Shape Schema - 'export function ShapeSchema(): yup.MixedSchema {', - 'union(CircleSchema(), SquareSchema())', - '}', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function SquareSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Square'>().optional(), + size: yup.number().defined().nullable().optional() + }) + } + + export function CircleSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Circle'>().optional(), + radius: yup.number().defined().nullable().optional() + }) + } + + export function ShapeSchema(): yup.MixedSchema { + return union(CircleSchema(), SquareSchema()) + } + " + `) }); it('generate union types with single element', async () => { @@ -598,15 +794,41 @@ describe('yup', () => { {}, ); - const wantContains = [ - 'export function GeometrySchema(): yup.ObjectSchema {', - 'return yup.object({', - '__typename: yup.string<\'Geometry\'>().optional(),', - 'shape: ShapeSchema().nullable().optional()', - '})', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function SquareSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Square'>().optional(), + size: yup.number().defined().nullable().optional() + }) + } + + export function CircleSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Circle'>().optional(), + radius: yup.number().defined().nullable().optional() + }) + } + + export function ShapeSchema(): yup.MixedSchema { + return union(CircleSchema(), SquareSchema()) + } + + export function GeometrySchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Geometry'>().optional(), + shape: ShapeSchema().nullable().optional() + }) + } + " + `) }); it('correctly reference generated union types', async () => { @@ -627,14 +849,27 @@ describe('yup', () => { {}, ); - const wantContains = [ - // Shape Schema - 'export function ShapeSchema(): yup.MixedSchema {', - 'return union(CircleSchema())', - '}', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function CircleSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Circle'>().optional(), + radius: yup.number().defined().nullable().optional() + }) + } + + export function ShapeSchema(): yup.MixedSchema { + return union(CircleSchema()) + } + " + `) }); it('generate enum union types', async () => { @@ -662,13 +897,23 @@ describe('yup', () => { {}, ); - const wantContains = [ - 'export function AnyTypeSchema(): yup.MixedSchema {', - 'union(PageTypeSchema, MethodTypeSchema)', - '}', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + export const PageTypeSchema = yup.string().oneOf(Object.values(PageType)).defined(); + + export const MethodTypeSchema = yup.string().oneOf(Object.values(MethodType)).defined(); + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function AnyTypeSchema(): yup.MixedSchema { + return union(PageTypeSchema, MethodTypeSchema) + } + " + `) }); it('generate union types with single element, export as const', async () => { @@ -697,14 +942,33 @@ describe('yup', () => { {}, ); - const wantContains = [ - 'export const GeometrySchema: yup.ObjectSchema = yup.object({', - '__typename: yup.string<\'Geometry\'>().optional(),', - 'shape: ShapeSchema.nullable().optional()', - '})', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export const CircleSchema: yup.ObjectSchema = yup.object({ + __typename: yup.string<'Circle'>().optional(), + radius: yup.number().defined().nullable().optional() + }); + + export const SquareSchema: yup.ObjectSchema = yup.object({ + __typename: yup.string<'Square'>().optional(), + size: yup.number().defined().nullable().optional() + }); + + export const ShapeSchema: yup.MixedSchema = union(CircleSchema, SquareSchema); + + export const GeometrySchema: yup.ObjectSchema = yup.object({ + __typename: yup.string<'Geometry'>().optional(), + shape: ShapeSchema.nullable().optional() + }); + " + `) }); it('with object arguments', async () => { @@ -726,17 +990,33 @@ describe('yup', () => { }, {}, ); - const wantContain = dedent` - export function MyTypeFooArgsSchema(): yup.ObjectSchema { - return yup.object({ - a: yup.string().defined().nullable().optional(), - b: yup.number().defined().nonNullable(), - c: yup.boolean().defined().nullable().optional(), - d: yup.number().defined().nonNullable(), - e: yup.string().defined().nullable().optional() - }) - }`; - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function MyTypeSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'MyType'>().optional(), + foo: yup.string().defined().nullable().optional() + }) + } + + export function MyTypeFooArgsSchema(): yup.ObjectSchema { + return yup.object({ + a: yup.string().defined().nullable().optional(), + b: yup.number().defined().nonNullable(), + c: yup.boolean().defined().nullable().optional(), + d: yup.number().defined().nonNullable(), + e: yup.string().defined().nullable().optional() + }) + } + " + `) }); describe('with InterfaceType', () => { @@ -774,14 +1054,24 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - 'export function BookSchema(): yup.ObjectSchema {', - 'title: yup.string().defined().nullable().optional()', - ]; - const wantNotContains = ['__typename: yup.string<\'Book\'>().optional()']; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function BookSchema(): yup.ObjectSchema { + return yup.object({ + title: yup.string().defined().nullable().optional() + }) + } + " + `) + + const wantNotContains = ['__typename: yup.string<\'Book\'>().optional()']; for (const wantNotContain of wantNotContains) expect(result.content).not.toContain(wantNotContain); }); @@ -792,12 +1082,12 @@ describe('yup', () => { author: Author title: String } - + interface Book2 { author: Author! title: String! } - + interface Author { books: [Book] name: String @@ -812,21 +1102,37 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - 'export function AuthorSchema(): yup.ObjectSchema {', - 'books: yup.array(BookSchema().nullable()).defined().nullable().optional(),', - 'name: yup.string().defined().nullable().optional()', - - 'export function BookSchema(): yup.ObjectSchema {', - 'author: AuthorSchema().nullable().optional(),', - 'title: yup.string().defined().nullable().optional()', - - 'export function Book2Schema(): yup.ObjectSchema {', - 'author: AuthorSchema().nonNullable(),', - 'title: yup.string().defined().nonNullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function BookSchema(): yup.ObjectSchema { + return yup.object({ + author: AuthorSchema().nullable().optional(), + title: yup.string().defined().nullable().optional() + }) + } + + export function Book2Schema(): yup.ObjectSchema { + return yup.object({ + author: AuthorSchema().nonNullable(), + title: yup.string().defined().nonNullable() + }) + } + + export function AuthorSchema(): yup.ObjectSchema { + return yup.object({ + books: yup.array(BookSchema().nullable()).defined().nullable().optional(), + name: yup.string().defined().nullable().optional() + }) + } + " + `) for (const wantNotContain of ['Query', 'Mutation', 'Subscription']) expect(result.content).not.toContain(wantNotContain); @@ -837,19 +1143,19 @@ describe('yup', () => { title: String! author: Author! } - + type Textbook implements Book { title: String! author: Author! courses: [String!]! } - + type ColoringBook implements Book { title: String! author: Author! colors: [String!]! } - + type Author { books: [Book!] name: String @@ -864,53 +1170,49 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - [ - 'export function BookSchema(): yup.ObjectSchema {', - 'return yup.object({', - 'title: yup.string().defined().nonNullable(),', - 'author: AuthorSchema().nonNullable()', - '})', - '}', - ], - - [ - 'export function TextbookSchema(): yup.ObjectSchema {', - 'return yup.object({', - '__typename: yup.string<\'Textbook\'>().optional(),', - 'title: yup.string().defined().nonNullable(),', - 'author: AuthorSchema().nonNullable(),', - 'courses: yup.array(yup.string().defined().nonNullable()).defined()', - '})', - '}', - ], - - [ - 'export function ColoringBookSchema(): yup.ObjectSchema {', - 'return yup.object({', - '__typename: yup.string<\'ColoringBook\'>().optional(),', - 'title: yup.string().defined().nonNullable(),', - 'author: AuthorSchema().nonNullable(),', - 'colors: yup.array(yup.string().defined().nonNullable()).defined()', - '})', - '}', - ], - - [ - 'export function AuthorSchema(): yup.ObjectSchema {', - 'return yup.object({', - '__typename: yup.string<\'Author\'>().optional(),', - 'books: yup.array(BookSchema().nonNullable()).defined().nullable().optional(),', - 'name: yup.string().defined().nullable().optional()', - '})', - '}', - ], - ]; - - for (const wantContain of wantContains) { - for (const wantContainLine of wantContain) - expect(result.content).toContain(wantContainLine); - } + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export function BookSchema(): yup.ObjectSchema { + return yup.object({ + title: yup.string().defined().nonNullable(), + author: AuthorSchema().nonNullable() + }) + } + + export function TextbookSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Textbook'>().optional(), + title: yup.string().defined().nonNullable(), + author: AuthorSchema().nonNullable(), + courses: yup.array(yup.string().defined().nonNullable()).defined() + }) + } + + export function ColoringBookSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'ColoringBook'>().optional(), + title: yup.string().defined().nonNullable(), + author: AuthorSchema().nonNullable(), + colors: yup.array(yup.string().defined().nonNullable()).defined() + }) + } + + export function AuthorSchema(): yup.ObjectSchema { + return yup.object({ + __typename: yup.string<'Author'>().optional(), + books: yup.array(BookSchema().nonNullable()).defined().nullable().optional(), + name: yup.string().defined().nullable().optional() + }) + } + " + `) }); }); }); @@ -938,14 +1240,17 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - // User Create Input - 'export function UserCreateInputSchema(): yup.ObjectSchema {', - 'name: yup.string().defined().nonNullable().matches(/^Sir/),', - 'age: yup.number().defined().nonNullable().min(0).max(100)', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + export function UserCreateInputSchema(): yup.ObjectSchema { + return yup.object({ + name: yup.string().defined().nonNullable().matches(/^Sir/), + age: yup.number().defined().nonNullable().min(0).max(100) + }) + } + " + `) }); it('exports as const instead of func', async () => { @@ -963,7 +1268,14 @@ describe('yup', () => { }, {}, ); - expect(result.content).toContain('export const SaySchema: yup.ObjectSchema = yup.object({'); + expect(result.content).toMatchInlineSnapshot(` + " + + export const SaySchema: yup.ObjectSchema = yup.object({ + phrase: yup.string().defined().nonNullable() + }); + " + `) }); it('generate both input & type, export as const', async () => { @@ -1007,24 +1319,32 @@ describe('yup', () => { }, {}, ); - const wantContains = [ - // User Create Input - 'export const UserCreateInputSchema: yup.ObjectSchema = yup.object({', - 'name: yup.string().defined().nonNullable(),', - 'date: yup.date().defined().nonNullable(),', - 'email: yup.string().email().defined().nonNullable()', - // User - 'export const UserSchema: yup.ObjectSchema = yup.object({', - '__typename: yup.string<\'User\'>().optional(),', - 'id: yup.string().defined().nonNullable(),', - 'name: yup.string().defined().nullable().optional(),', - 'age: yup.number().defined().nullable().optional(),', - 'email: yup.string().email().defined().nullable().optional(),', - 'isMember: yup.boolean().defined().nullable().optional(),', - 'createdAt: yup.date().defined().nonNullable()', - ]; - for (const wantContain of wantContains) - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + + function union(...schemas: ReadonlyArray>): yup.MixedSchema { + return yup.mixed().test({ + test: (value) => schemas.some((schema) => schema.isValidSync(value)) + }).defined() + } + + export const UserCreateInputSchema: yup.ObjectSchema = yup.object({ + name: yup.string().defined().nonNullable(), + date: yup.date().defined().nonNullable(), + email: yup.string().email().defined().nonNullable() + }); + + export const UserSchema: yup.ObjectSchema = yup.object({ + __typename: yup.string<'User'>().optional(), + id: yup.string().defined().nonNullable(), + name: yup.string().defined().nullable().optional(), + age: yup.number().defined().nullable().optional(), + email: yup.string().email().defined().nullable().optional(), + isMember: yup.boolean().defined().nullable().optional(), + createdAt: yup.date().defined().nonNullable() + }); + " + `) for (const wantNotContain of ['Query', 'Mutation', 'Subscription']) expect(result.content).not.toContain(wantNotContain); @@ -1058,13 +1378,17 @@ describe('yup', () => { }, {}, ); - const wantContain = dedent` - export function QueryInputSchema(): yup.ObjectSchema { - return yup.object({ - _dummy: TestSchema.nullable().optional() - }) - }`; - expect(result.content).toContain(wantContain); + expect(result.content).toMatchInlineSnapshot(` + " + export const TestSchema = yup.string().oneOf(Object.values(Test)).defined(); + + export function QueryInputSchema(): yup.ObjectSchema { + return yup.object({ + _dummy: TestSchema.nullable().optional() + }) + } + " + `) }); it('with default input values', async () => { @@ -1091,15 +1415,20 @@ describe('yup', () => { {}, ); - expect(result.content).toContain( - 'export const PageTypeSchema = yup.string().oneOf(Object.values(PageType)).defined()', - ); - expect(result.content).toContain('export function PageInputSchema(): yup.ObjectSchema'); + expect(result.content).toMatchInlineSnapshot(` + " + export const PageTypeSchema = yup.string().oneOf(Object.values(PageType)).defined(); - expect(result.content).toContain('pageType: PageTypeSchema.nonNullable().default("PUBLIC")'); - expect(result.content).toContain('greeting: yup.string().defined().nullable().default("Hello").optional()'); - expect(result.content).toContain('score: yup.number().defined().nullable().default(100).optional()'); - expect(result.content).toContain('ratio: yup.number().defined().nullable().default(0.5).optional()'); - expect(result.content).toContain('isMember: yup.boolean().defined().nullable().default(true).optional()'); + export function PageInputSchema(): yup.ObjectSchema { + return yup.object({ + pageType: PageTypeSchema.nonNullable().default("PUBLIC"), + greeting: yup.string().defined().nullable().default("Hello").optional(), + score: yup.number().defined().nullable().default(100).optional(), + ratio: yup.number().defined().nullable().default(0.5).optional(), + isMember: yup.boolean().defined().nullable().default(true).optional() + }) + } + " + `) }); });