From 11bc60df8fafeb3dfa52426beb0d8ca1b38f6d71 Mon Sep 17 00:00:00 2001 From: Calvin Cestari Date: Tue, 7 Jun 2022 10:20:13 -0700 Subject: [PATCH] fix: Union test mock generator (#2298) * change: Update version for next alpha release * docs: Update Roadmap to point to releases and not specific alpha version * docs: Update Changelog for next alpha release * change: Add target dependency for test mocks package * fix: Generate test mock extensions for Union types * chore: Update AnimalKingdomAPI generated files * ci: Add Union mock generated file to TestMocks expected paths test --- Apollo.xcodeproj/project.pbxproj | 20 ++++- .../TestMocks/ClassroomPet+Mock.swift | 9 +++ Sources/ApolloCodegenLib/ApolloCodegen.swift | 11 +++ .../MockUnionFileGenerator.swift | 23 ++++++ .../Templates/MockUnionTemplate.swift | 24 ++++++ .../SwiftPackageManagerModuleTemplate.swift | 11 ++- .../ApolloCodegenTests.swift | 1 + .../MockUnionFileGeneratorTests.swift | 52 +++++++++++++ .../Templates/MockUnionTemplateTests.swift | 77 +++++++++++++++++++ ...iftPackageManagerModuleTemplateTests.swift | 2 + 10 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 Sources/AnimalKingdomAPI/AnimalKingdomAPI/TestMocks/ClassroomPet+Mock.swift create mode 100644 Sources/ApolloCodegenLib/FileGenerators/MockUnionFileGenerator.swift create mode 100644 Sources/ApolloCodegenLib/Templates/MockUnionTemplate.swift create mode 100644 Tests/ApolloCodegenTests/CodeGeneration/FileGenerators/MockUnionFileGeneratorTests.swift create mode 100644 Tests/ApolloCodegenTests/CodeGeneration/Templates/MockUnionTemplateTests.swift diff --git a/Apollo.xcodeproj/project.pbxproj b/Apollo.xcodeproj/project.pbxproj index 66e7dfb4df..4e320b5fb6 100644 --- a/Apollo.xcodeproj/project.pbxproj +++ b/Apollo.xcodeproj/project.pbxproj @@ -425,6 +425,10 @@ E61DD76526D60C1800C41614 /* SQLiteDotSwiftDatabaseBehaviorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E61DD76426D60C1800C41614 /* SQLiteDotSwiftDatabaseBehaviorTests.swift */; }; E61EF713275EC99A00191DA7 /* ApolloCodegenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E61EF712275EC99A00191DA7 /* ApolloCodegenTests.swift */; }; E61F4BF827A8FC8E0049886A /* FragmentFileGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E61F4BF727A8FC8E0049886A /* FragmentFileGeneratorTests.swift */; }; + E6203342284F1C9600A291D1 /* MockUnionFileGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6203341284F1C9600A291D1 /* MockUnionFileGenerator.swift */; }; + E6203344284F1D1100A291D1 /* MockUnionTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6203343284F1D1100A291D1 /* MockUnionTemplate.swift */; }; + E6203346284F252A00A291D1 /* MockUnionFileGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6203345284F252A00A291D1 /* MockUnionFileGeneratorTests.swift */; }; + E6203348284F25DF00A291D1 /* MockUnionTemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6203347284F25DF00A291D1 /* MockUnionTemplateTests.swift */; }; E623FD2A2797A6F4008B4CED /* InterfaceTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E623FD292797A6F4008B4CED /* InterfaceTemplate.swift */; }; E64F7EB827A0854E0059C021 /* UnionTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64F7EB727A0854E0059C021 /* UnionTemplate.swift */; }; E64F7EBA27A085D90059C021 /* UnionTemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64F7EB927A085D90059C021 /* UnionTemplateTests.swift */; }; @@ -1287,6 +1291,10 @@ E61DD76426D60C1800C41614 /* SQLiteDotSwiftDatabaseBehaviorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLiteDotSwiftDatabaseBehaviorTests.swift; sourceTree = ""; }; E61EF712275EC99A00191DA7 /* ApolloCodegenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloCodegenTests.swift; sourceTree = ""; }; E61F4BF727A8FC8E0049886A /* FragmentFileGeneratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FragmentFileGeneratorTests.swift; sourceTree = ""; }; + E6203341284F1C9600A291D1 /* MockUnionFileGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUnionFileGenerator.swift; sourceTree = ""; }; + E6203343284F1D1100A291D1 /* MockUnionTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUnionTemplate.swift; sourceTree = ""; }; + E6203345284F252A00A291D1 /* MockUnionFileGeneratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUnionFileGeneratorTests.swift; sourceTree = ""; }; + E6203347284F25DF00A291D1 /* MockUnionTemplateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUnionTemplateTests.swift; sourceTree = ""; }; E623FD292797A6F4008B4CED /* InterfaceTemplate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterfaceTemplate.swift; sourceTree = ""; }; E623FD2B2797A700008B4CED /* InterfaceTemplateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterfaceTemplateTests.swift; sourceTree = ""; }; E64F7EB727A0854E0059C021 /* UnionTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnionTemplate.swift; sourceTree = ""; }; @@ -2332,6 +2340,8 @@ DE5FD60A276970FC0033EE23 /* FragmentTemplate.swift */, E69BEDA42798B86D00000D10 /* InputObjectTemplate.swift */, E623FD292797A6F4008B4CED /* InterfaceTemplate.swift */, + DE9CEAEE282C62B700959AF9 /* MockObjectTemplate.swift */, + E6203343284F1D1100A291D1 /* MockUnionTemplate.swift */, E64F7EC027A122300059C021 /* ObjectTemplate.swift */, DE5FD5FC2769222D0033EE23 /* OperationDefinitionTemplate.swift */, E6EE62F027DBE6F200627257 /* SchemaModuleNamespaceTemplate.swift */, @@ -2340,7 +2350,6 @@ E6B42D0827A472A700A3BD58 /* SwiftPackageManagerModuleTemplate.swift */, E60AE2ED27E3FC6C003C093A /* TemplateRenderer.swift */, E64F7EB727A0854E0059C021 /* UnionTemplate.swift */, - DE9CEAEE282C62B700959AF9 /* MockObjectTemplate.swift */, ); path = Templates; sourceTree = ""; @@ -2356,6 +2365,7 @@ E623FD2B2797A700008B4CED /* InterfaceTemplateTests.swift */, E64F7EC227A1243A0059C021 /* ObjectTemplateTests.swift */, DE9CEAF0282C632B00959AF9 /* MockObjectTemplateTests.swift */, + E6203347284F25DF00A291D1 /* MockUnionTemplateTests.swift */, DE6D07FA27BC3BE9009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift */, DE09F9C5270269F800795949 /* OperationDefinitionTemplate_DocumentType_Tests.swift */, DE09066E27A4713F00211300 /* OperationDefinitionTemplateTests.swift */, @@ -2920,6 +2930,7 @@ E610D8DC278EB1500023E495 /* InterfaceFileGeneratorTests.swift */, E66F8896276C136B0000BDA8 /* ObjectFileGeneratorTests.swift */, DE0D38F1282ECF9700AFCC84 /* MockObjectFileGeneratorTests.swift */, + E6203345284F252A00A291D1 /* MockUnionFileGeneratorTests.swift */, E607AD8D27A88F340026742A /* OperationFileGeneratorTests.swift */, E6D90D0C278FFE35009CAC5D /* SchemaFileGeneratorTests.swift */, E6B42D0A27A4746800A3BD58 /* SchemaModuleFileGeneratorTests.swift */, @@ -2957,8 +2968,9 @@ DE4D54E827A3518100D26B68 /* FragmentFileGenerator.swift */, E6D90D06278FA595009CAC5D /* InputObjectFileGenerator.swift */, E610D8DA278EB0900023E495 /* InterfaceFileGenerator.swift */, - E66F8898276C15580000BDA8 /* ObjectFileGenerator.swift */, DE0D38EF282ECA6D00AFCC84 /* MockObjectFileGenerator.swift */, + E6203341284F1C9600A291D1 /* MockUnionFileGenerator.swift */, + E66F8898276C15580000BDA8 /* ObjectFileGenerator.swift */, DE4D54E627A3504B00D26B68 /* OperationFileGenerator.swift */, E6D90D0A278FFDDA009CAC5D /* SchemaFileGenerator.swift */, E68D824627A228A80040A46F /* SchemaModuleFileGenerator.swift */, @@ -3810,6 +3822,7 @@ E610D8DF278F8F1E0023E495 /* UnionFileGenerator.swift in Sources */, DEE2DAA227BAF00500EC0607 /* GraphQLType+Rendered.swift in Sources */, DE223C2D2721FCE8004A0148 /* ScopedSelectionSetHashable.swift in Sources */, + E6203344284F1D1100A291D1 /* MockUnionTemplate.swift in Sources */, DE79642D27699A6A00978A03 /* IR+NamedFragmentBuilder.swift in Sources */, 9B47518D2575AA850001FB87 /* Pluralizer.swift in Sources */, E6D90D0B278FFDDA009CAC5D /* SchemaFileGenerator.swift in Sources */, @@ -3824,6 +3837,7 @@ E610D8DB278EB0900023E495 /* InterfaceFileGenerator.swift in Sources */, 9B7B6F69233C2C0C00F32205 /* FileManager+Apollo.swift in Sources */, E674DB41274C0A9B009BB90E /* Glob.swift in Sources */, + E6203342284F1C9600A291D1 /* MockUnionFileGenerator.swift in Sources */, DE6D07F927BC3B6D009F5F33 /* GraphQLInputField+Rendered.swift in Sources */, 9F1A966C258F34BB00A06EEB /* GraphQLSchema.swift in Sources */, 9BE74D3D23FB4A8E006D354F /* FileFinder.swift in Sources */, @@ -3900,7 +3914,9 @@ 9B8C3FB5248DA3E000707B13 /* URLExtensionsTests.swift in Sources */, E64F7EBA27A085D90059C021 /* UnionTemplateTests.swift in Sources */, E610D8D9278EA2560023E495 /* EnumFileGeneratorTests.swift in Sources */, + E6203348284F25DF00A291D1 /* MockUnionTemplateTests.swift in Sources */, 9FDE0731258F3AA100DC0CA5 /* SchemaLoadingTests.swift in Sources */, + E6203346284F252A00A291D1 /* MockUnionFileGeneratorTests.swift in Sources */, E6908E55282694630054682B /* ApolloCodegenConfigurationCodableTests.swift in Sources */, DE223C3327221144004A0148 /* IRMatchers.swift in Sources */, E64F7EBF27A11B110059C021 /* GraphQLNamedType+SwiftTests.swift in Sources */, diff --git a/Sources/AnimalKingdomAPI/AnimalKingdomAPI/TestMocks/ClassroomPet+Mock.swift b/Sources/AnimalKingdomAPI/AnimalKingdomAPI/TestMocks/ClassroomPet+Mock.swift new file mode 100644 index 0000000000..2fb584dc9f --- /dev/null +++ b/Sources/AnimalKingdomAPI/AnimalKingdomAPI/TestMocks/ClassroomPet+Mock.swift @@ -0,0 +1,9 @@ +// @generated +// This file was automatically generated and should not be edited. + +import ApolloTestSupport +import AnimalKingdomAPI + +extension ClassroomPet: MockFieldValue { + public typealias MockValueCollectionType = Array +} \ No newline at end of file diff --git a/Sources/ApolloCodegenLib/ApolloCodegen.swift b/Sources/ApolloCodegenLib/ApolloCodegen.swift index 8c87b83781..6309512e2d 100644 --- a/Sources/ApolloCodegenLib/ApolloCodegen.swift +++ b/Sources/ApolloCodegenLib/ApolloCodegen.swift @@ -147,6 +147,17 @@ public class ApolloCodegen { forConfig: config, fileManager: fileManager ) + + if config.output.testMocks != .none { + try MockUnionFileGenerator( + graphqlUnion: graphQLUnion, + ir: ir, + config: config + ).generate( + forConfig: config, + fileManager: fileManager + ) + } } } diff --git a/Sources/ApolloCodegenLib/FileGenerators/MockUnionFileGenerator.swift b/Sources/ApolloCodegenLib/FileGenerators/MockUnionFileGenerator.swift new file mode 100644 index 0000000000..338556cd0a --- /dev/null +++ b/Sources/ApolloCodegenLib/FileGenerators/MockUnionFileGenerator.swift @@ -0,0 +1,23 @@ +import Foundation +import ApolloUtils + +/// Generates a file providing the ability to mock a GraphQLUnionType for testing purposes. +struct MockUnionFileGenerator: FileGenerator { + /// Source GraphQL union. + let graphqlUnion: GraphQLUnionType + + let ir: IR + + let config: ReferenceWrapped + + var template: TemplateRenderer { + MockUnionTemplate( + graphqlUnion: graphqlUnion, + config: config, + ir: ir + ) + } + + var target: FileTarget { .testMock } + var fileName: String { "\(graphqlUnion.name)+Mock.swift" } +} diff --git a/Sources/ApolloCodegenLib/Templates/MockUnionTemplate.swift b/Sources/ApolloCodegenLib/Templates/MockUnionTemplate.swift new file mode 100644 index 0000000000..ae4df37b38 --- /dev/null +++ b/Sources/ApolloCodegenLib/Templates/MockUnionTemplate.swift @@ -0,0 +1,24 @@ +import Foundation +import ApolloUtils + +struct MockUnionTemplate: TemplateRenderer { + /// IR representation of source [GraphQL Union](https://spec.graphql.org/draft/#sec-Unions). + let graphqlUnion: GraphQLUnionType + + /// Shared codegen configuration. + let config: ReferenceWrapped + + let ir: IR + + let target: TemplateTarget = .testMockFile + + var template: TemplateString { + TemplateString(""" + extension \ + \(if: !config.output.schemaTypes.isInModule, "\(ir.schema.name.firstUppercased).")\ + \(graphqlUnion.name.firstUppercased): MockFieldValue { + public typealias MockValueCollectionType = Array + } + """) + } +} diff --git a/Sources/ApolloCodegenLib/Templates/SwiftPackageManagerModuleTemplate.swift b/Sources/ApolloCodegenLib/Templates/SwiftPackageManagerModuleTemplate.swift index 0936a7cacc..6a7e457f69 100644 --- a/Sources/ApolloCodegenLib/Templates/SwiftPackageManagerModuleTemplate.swift +++ b/Sources/ApolloCodegenLib/Templates/SwiftPackageManagerModuleTemplate.swift @@ -13,13 +13,15 @@ struct SwiftPackageManagerModuleTemplate: TemplateRenderer { let headerTemplate: TemplateString? = nil var template: TemplateString { - TemplateString(""" + let casedModuleName = moduleName.firstUppercased + + return TemplateString(""" // swift-tools-version:5.3 import PackageDescription let package = Package( - name: "\(moduleName.firstUppercased)", + name: "\(casedModuleName)", platforms: [ .iOS(.v12), .macOS(.v10_14), @@ -27,14 +29,14 @@ struct SwiftPackageManagerModuleTemplate: TemplateRenderer { .watchOS(.v5), ], products: [ - .library(name: "\(moduleName.firstUppercased)", targets: ["\(moduleName.firstUppercased)"]), + .library(name: "\(casedModuleName)", targets: ["\(casedModuleName)"]), ], dependencies: [ .package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.0.0-alpha.6"), ], targets: [ .target( - name: "\(moduleName.firstUppercased)", + name: "\(casedModuleName)", dependencies: [ .product(name: "ApolloAPI", package: "apollo-ios"), ], @@ -45,6 +47,7 @@ struct SwiftPackageManagerModuleTemplate: TemplateRenderer { name: "\($0.targetName)", dependencies: [ .product(name: "ApolloTestSupport", package: "apollo-ios"), + .target(name: "\(casedModuleName)"), ], path: "\($0.path)" ), diff --git a/Tests/ApolloCodegenTests/ApolloCodegenTests.swift b/Tests/ApolloCodegenTests/ApolloCodegenTests.swift index 56d607c181..85aaa9dbec 100644 --- a/Tests/ApolloCodegenTests/ApolloCodegenTests.swift +++ b/Tests/ApolloCodegenTests/ApolloCodegenTests.swift @@ -460,6 +460,7 @@ class ApolloCodegenTests: XCTestCase { directoryURL.appendingPathComponent("TestMocks/Dog+Mock.swift").path, directoryURL.appendingPathComponent("TestMocks/Fish+Mock.swift").path, directoryURL.appendingPathComponent("TestMocks/Crocodile+Mock.swift").path, + directoryURL.appendingPathComponent("TestMocks/ClassroomPet+Mock.swift").path, ] // when diff --git a/Tests/ApolloCodegenTests/CodeGeneration/FileGenerators/MockUnionFileGeneratorTests.swift b/Tests/ApolloCodegenTests/CodeGeneration/FileGenerators/MockUnionFileGeneratorTests.swift new file mode 100644 index 0000000000..a5e4a47331 --- /dev/null +++ b/Tests/ApolloCodegenTests/CodeGeneration/FileGenerators/MockUnionFileGeneratorTests.swift @@ -0,0 +1,52 @@ +import XCTest +import Nimble +@testable import ApolloCodegenLib +import ApolloUtils + +class MockUnionFileGeneratorTests: XCTestCase { + let graphqlUnion = GraphQLUnionType.mock("MockUnion", types: []) + + var subject: MockUnionFileGenerator! + + override func tearDown() { + subject = nil + } + + // MARK: Test Helpers + + private func buildSubject() { + subject = MockUnionFileGenerator( + graphqlUnion: graphqlUnion, + ir: .mock(compilationResult: .mock()), + config: ReferenceWrapped(value: .mock(.other)) + ) + } + + // MARK: Property Tests + + func test__properties__shouldReturnTargetType_testMock() { + // given + buildSubject() + + // then + expect(self.subject.target).to(equal(.testMock)) + } + + func test__properties__givenGraphQLUnion_shouldReturnFileName_matchingUnionName() { + // given + buildSubject() + + let expected = "\(graphqlUnion.name)+Mock.swift" + + // then + expect(self.subject.fileName).to(equal(expected)) + } + + func test__properties__givenGraphQLUnion_shouldOverwrite() { + // given + buildSubject() + + // then + expect(self.subject.overwrite).to(beTrue()) + } +} diff --git a/Tests/ApolloCodegenTests/CodeGeneration/Templates/MockUnionTemplateTests.swift b/Tests/ApolloCodegenTests/CodeGeneration/Templates/MockUnionTemplateTests.swift new file mode 100644 index 0000000000..0fc0cca8e0 --- /dev/null +++ b/Tests/ApolloCodegenTests/CodeGeneration/Templates/MockUnionTemplateTests.swift @@ -0,0 +1,77 @@ +import XCTest +import Nimble +@testable import ApolloCodegenLib +import ApolloCodegenInternalTestHelpers +import ApolloUtils + +class MockUnionTemplateTests: XCTestCase { + var ir: IR! + var subject: MockUnionTemplate! + + override func tearDown() { + subject = nil + + super.tearDown() + } + + // MARK: Helpers + + private func buildSubject( + name: String = "Pet", + types: [GraphQLObjectType] = [], + moduleType: ApolloCodegenConfiguration.SchemaTypesFileOutput.ModuleType = .swiftPackageManager + ) { + let config = ApolloCodegenConfiguration.mock(moduleType) + ir = IR.mock(compilationResult: .mock()) + + subject = MockUnionTemplate( + graphqlUnion: GraphQLUnionType.mock(name, types: types), + config: ReferenceWrapped(value: config), + ir: ir + ) + } + + private func renderSubject() -> String { + subject.template.description + } + + // MARK: Boilerplate tests + + func test__target__isTestMockFile() { + buildSubject() + + expect(self.subject.target).to(equal(.testMockFile)) + } + + func test_render_givenSchemaType_generatesExtensionWithTypealias() { + // given + buildSubject(name: "Pet") + + let expected = """ + extension Pet: MockFieldValue { + public typealias MockValueCollectionType = Array + } + """ + + // when + let actual = renderSubject() + + // then + expect(actual).to(equalLineByLine(expected)) + } + + func test_render_givenConfig_SchemaTypeOutputNone_generatesExtensionWithSchemaNamespace() { + // given + buildSubject(name: "Pet", moduleType: .embeddedInTarget(name: "MockApplication")) + + let expected = """ + extension TestSchema.Pet: MockFieldValue { + """ + + // when + let actual = renderSubject() + + // then + expect(actual).to(equalLineByLine(expected, ignoringExtraLines: true)) + } +} diff --git a/Tests/ApolloCodegenTests/CodeGeneration/Templates/SwiftPackageManagerModuleTemplateTests.swift b/Tests/ApolloCodegenTests/CodeGeneration/Templates/SwiftPackageManagerModuleTemplateTests.swift index 795dbb890e..6a6609d5b8 100644 --- a/Tests/ApolloCodegenTests/CodeGeneration/Templates/SwiftPackageManagerModuleTemplateTests.swift +++ b/Tests/ApolloCodegenTests/CodeGeneration/Templates/SwiftPackageManagerModuleTemplateTests.swift @@ -182,6 +182,7 @@ class SwiftPackageManagerModuleTemplateTests: XCTestCase { name: "TestModuleTestMocks", dependencies: [ .product(name: "ApolloTestSupport", package: "apollo-ios"), + .target(name: "TestModule"), ], path: "./TestMocks" ), @@ -212,6 +213,7 @@ class SwiftPackageManagerModuleTemplateTests: XCTestCase { name: "CustomMocks", dependencies: [ .product(name: "ApolloTestSupport", package: "apollo-ios"), + .target(name: "TestModule"), ], path: "./CustomMocks" ),