UNPKG

apollo-codegen-swift

Version:
411 lines (368 loc) 12.4 kB
import { stripIndent } from "common-tags"; import { SwiftGenerator, SwiftSource, swift } from "../language"; import { valueFromAST } from "graphql"; describe("Swift code generation: Basic language constructs", () => { let generator: SwiftGenerator<any>; beforeEach(() => { generator = new SwiftGenerator({}); }); it(`should generate a class declaration`, () => { generator.classDeclaration( { className: "Hero", modifiers: ["public", "final"] }, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); generator.propertyDeclaration({ propertyName: "age", typeName: "Int", }); } ); expect(generator.output).toBe(stripIndent` public final class Hero { public var name: String public var age: Int } `); }); it(`should generate a class declaration matching modifiers`, () => { generator.classDeclaration( { className: "Hero", modifiers: ["final"] }, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); generator.propertyDeclaration({ propertyName: "age", typeName: "Int", }); } ); expect(generator.output).toBe(stripIndent` final class Hero { public var name: String public var age: Int } `); }); it(`should generate a class declaration with proper escaping`, () => { generator.classDeclaration( { className: "Type", modifiers: ["public", "final"] }, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); generator.propertyDeclaration({ propertyName: "age", typeName: "Int", }); generator.propertyDeclaration({ propertyName: "self", typeName: "Self", }); } ); expect(generator.output).toBe(stripIndent` public final class \`Type\` { public var name: String public var age: Int public var \`self\`: \`Self\` } `); }); it(`should generate a struct declaration`, () => { generator.structDeclaration({ structName: "Hero" }, false, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); generator.propertyDeclaration({ propertyName: "age", typeName: "Int", }); }); expect(generator.output).toBe(stripIndent` public struct Hero { public var name: String public var age: Int } `); }); it(`should generate a namespaced fragment`, () => { generator.structDeclaration( { structName: "Hero", adoptedProtocols: ["GraphQLFragment"], namespace: "StarWars", }, false, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); generator.propertyDeclaration({ propertyName: "age", typeName: "Int", }); } ); expect(generator.output).toBe(stripIndent` public struct Hero: GraphQLFragment { public var name: String public var age: Int } `); }); it(`should generate a namespaced fragment which is not public for individual files`, () => { generator.structDeclaration( { structName: "Hero", adoptedProtocols: ["GraphQLFragment"], namespace: "StarWars", }, true, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); generator.propertyDeclaration({ propertyName: "age", typeName: "Int", }); } ); expect(generator.output).toBe(stripIndent` struct Hero: GraphQLFragment { public var name: String public var age: Int } `); }); it(`should generate an escaped struct declaration`, () => { generator.structDeclaration({ structName: "Type" }, false, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); generator.propertyDeclaration({ propertyName: "yearOfBirth", typeName: "Int", }); generator.propertyDeclaration({ propertyName: "self", typeName: "Self", }); }); expect(generator.output).toBe(stripIndent` public struct \`Type\` { public var name: String public var yearOfBirth: Int public var \`self\`: \`Self\` } `); }); it(`should generate nested struct declarations`, () => { generator.structDeclaration({ structName: "Hero" }, false, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); generator.propertyDeclaration({ propertyName: "friends", typeName: "[Friend]", }); generator.structDeclaration({ structName: "Friend" }, false, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", }); }); }); expect(generator.output).toBe(stripIndent` public struct Hero { public var name: String public var friends: [Friend] public struct Friend { public var name: String } } `); }); it(`should generate a protocol declaration`, () => { generator.protocolDeclaration( { protocolName: "HeroDetails", adoptedProtocols: ["HasName"] }, () => { generator.protocolPropertyDeclaration({ propertyName: "name", typeName: "String", }); generator.protocolPropertyDeclaration({ propertyName: "age", typeName: "Int", }); generator.protocolPropertyDeclaration({ propertyName: "default", typeName: "Boolean", }); } ); expect(generator.output).toBe(stripIndent` public protocol HeroDetails: HasName { var name: String { get } var age: Int { get } var \`default\`: Boolean { get } } `); }); it(`should handle multi-line descriptions`, () => { generator.structDeclaration( { structName: "Hero", description: "A hero" }, false, () => { generator.propertyDeclaration({ propertyName: "name", typeName: "String", description: `A multiline comment \n on the hero's name.`, }); generator.propertyDeclaration({ propertyName: "age", typeName: "String", description: `A multiline comment \n on the hero's age.`, }); } ); expect(generator.output).toMatchSnapshot(); }); }); describe("Swift code generation: Escaping", () => { describe("using SwiftSource", () => { it(`should escape identifiers`, () => { expect(SwiftSource.identifier("self").source).toBe("`self`"); expect(SwiftSource.identifier("public").source).toBe("`public`"); expect(SwiftSource.identifier("Array<Type>").source).toBe( "Array<`Type`>" ); expect(SwiftSource.identifier("[Self?]?").source).toBe("[`Self`?]?"); }); it(`should not escape other words`, () => { expect(SwiftSource.identifier("me").source).toBe("me"); expect(SwiftSource.identifier("_Self").source).toBe("_Self"); expect(SwiftSource.identifier("classes").source).toBe("classes"); }); it(`should escape fewer words in member position`, () => { expect(SwiftSource.identifier(".self").source).toBe(".`self`"); expect(SwiftSource.identifier(".public").source).toBe(".public"); expect(SwiftSource.identifier("Foo.Self.Type.self.class").source).toBe( "Foo.Self.`Type`.`self`.class" ); }); it(`should escape fewer words at offset 0 with member escaping`, () => { expect(SwiftSource.memberName("self").source).toBe("`self`"); expect(SwiftSource.memberName("public").source).toBe("public"); expect(SwiftSource.memberName(" public").source).toBe(" `public`"); expect(SwiftSource.memberName("Foo.Self.Type.self.class").source).toBe( "Foo.Self.`Type`.`self`.class" ); }); it(`should escape strings`, () => { expect(SwiftSource.string("foobar").source).toBe('"foobar"'); expect(SwiftSource.string("foo\n bar ").source).toBe('"foo\\n bar "'); expect(SwiftSource.string("one'two\"three\\four\tfive").source).toBe( '"one\'two\\"three\\\\four\\tfive"' ); }); it(`should trim strings when asked`, () => { expect(SwiftSource.string("foobar", true).source).toBe('"foobar"'); expect(SwiftSource.string("foo\n bar ", true).source).toBe('"foo bar"'); }); it(`should generate multiline strings`, () => { expect(SwiftSource.multilineString("foobar").source).toBe( '"""\nfoobar\n"""' ); expect(SwiftSource.multilineString("foo\n bar ").source).toBe( '"""\nfoo\n bar \n"""' ); expect(SwiftSource.multilineString(`"""foo"""`).source).toBe( '#"""\n"""foo"""\n"""#' ); expect(SwiftSource.multilineString("foo\\nbar").source).toBe( '#"""\nfoo\\nbar\n"""#' ); expect(SwiftSource.multilineString(`"""\\"""#"""`).source).toBe( '##"""\n"""\\"""#"""\n"""##' ); expect(SwiftSource.multilineString(`foo\\\\#bar`).source).toBe( '##"""\nfoo\\\\#bar\n"""##' ); expect(SwiftSource.multilineString(`foo\\\\#\\##bar`).source).toBe( '###"""\nfoo\\\\#\\##bar\n"""###' ); expect(SwiftSource.multilineString("foo\\###nbar").source).toBe( '####"""\nfoo\\###nbar\n"""####' ); }); it(`should support concatenation`, () => { expect(swift`one`.concat().source).toBe("one"); expect(swift`one`.concat(swift`two`).source).toBe("onetwo"); expect(swift`one`.concat(swift`two`, swift`three`).source).toBe( "onetwothree" ); }); it(`should support appending`, () => { let value = swift`one`; value.append(); expect(value.source).toBe("one"); value.append(swift`foo`); expect(value.source).toBe("onefoo"); value.append(swift`bar`, swift`baz`, swift`qux`); expect(value.source).toBe("onefoobarbazqux"); }); }); describe("using SwiftGenerator", () => { let generator: SwiftGenerator<any>; beforeEach(() => { generator = new SwiftGenerator({}); }); it(`should not trim with multiline string if multiline strings are not suppressed and there is no triple quote`, () => { generator.multilineString("foo\n bar ", false); expect(generator.output).toBe('"""\nfoo\n bar \n"""'); }); it(`should trim with multilineString if multiline strings are suppressed`, () => { generator.multilineString("foo\n bar ", true); expect(generator.output).toBe('"foo bar"'); }); it(`shouldn't trim with multilineString when using """ even when multiline strings are suppressed`, () => { generator.multilineString('"""\nfoo\n bar \n"""', true); expect(generator.output).toBe('"\\"\\"\\"\\nfoo\\n bar \\n\\"\\"\\""'); }); }); describe("using template strings", () => { it(`should escape interpolated strings but not string literals`, () => { expect(swift`self`.source).toBe("self"); expect(swift`${"self"}`.source).toBe("`self`"); expect(swift`class ${"Foo.Type.self"}: ${"Protocol?"}`.source).toBe( "class Foo.`Type`.`self`: `Protocol`?" ); expect(swift`${["Self", "Foo.Self.self"]}`.source).toBe( "`Self`,Foo.Self.`self`" ); expect(swift`${true} ${"true"}`.source).toBe("true `true`"); expect(swift`${{ toString: () => "self" }}`.source).toBe("`self`"); }); it(`should not escape already-escaped interpolated strings`, () => { expect(swift`${swift`${"self"}`}`.source).toBe("`self`"); expect(swift`${"public"} ${new SwiftSource("public")}`.source).toBe( "`public` public" ); }); it(`should not escape with the raw tag`, () => { expect(SwiftSource.raw`${"self"}`.source).toBe("self"); }); }); });