From a47550fa5831d97c9efc471c315d612817f86de0 Mon Sep 17 00:00:00 2001 From: Dave Gallagher Date: Thu, 25 Oct 2018 09:50:06 -0700 Subject: [PATCH] Added support to create new CaseField's over the API. - Added addCaseField() to API and ObjectAPI. - Added NewCaseField models. - Added end-to-end and model tests for NewCaseField. - Bumped version to 1.2.1. - Bumped example project to use 1.2.1. --- Example/Cartfile.private | 2 +- Example/Cartfile.resolved | 2 +- QuizTrain.xcodeproj/project.pbxproj | 192 +++++- .../xcschemes/QuizTrainTests-iOS.xcscheme | 41 +- .../xcschemes/QuizTrainTests-macOS.xcscheme | 41 +- .../xcschemes/QuizTrainTests-tvOS.xcscheme | 41 +- QuizTrain/Info.plist | 2 +- QuizTrain/Models/CaseField.swift | 25 +- QuizTrain/Network/API.swift | 7 + .../Add/NewCaseField/NewCaseField.swift | 424 ++++++++++++ .../Add/NewCaseField/NewCaseFieldConfig.swift | 64 ++ .../NewCaseFieldConfigContext.swift | 15 + .../NewCaseFieldConfigOptions.swift | 457 +++++++++++++ .../Add/NewCaseField/NewCaseFieldData.swift | 64 ++ .../Add/NewCaseField/NewCaseFieldType.swift | 49 ++ QuizTrain/Network/ObjectAPI.swift | 476 ++++++------- .../NewCheckboxCaseFieldTests.swift | 194 ++++++ .../NewDateCaseFieldTests.swift | 187 ++++++ .../NewDropdownCaseFieldTests.swift | 231 +++++++ .../NewIntegerCaseFieldTests.swift | 201 ++++++ .../NewMilestoneCaseFieldTests.swift | 187 ++++++ .../NewMultiselectCaseFieldTests.swift | 219 ++++++ .../NewStepsCaseFieldTests.swift | 207 ++++++ .../NewStringCaseFieldTests.swift | 201 ++++++ .../NewTextCaseFieldTests.swift | 215 ++++++ .../NewURLCaseFieldTests.swift | 201 ++++++ .../NewUserCaseFieldTests.swift | 201 ++++++ QuizTrainTests/Network/ObjectAPITests.swift | 623 +++++++++++++++++- QuizTrainTests/README.md | 20 + .../Asserts/AssertCodable.swift | 67 ++ .../Tests/CodableTests.swift | 19 + README.md | 2 +- 32 files changed, 4625 insertions(+), 252 deletions(-) create mode 100644 QuizTrain/Network/Models/Add/NewCaseField/NewCaseField.swift create mode 100644 QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfig.swift create mode 100644 QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfigContext.swift create mode 100644 QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfigOptions.swift create mode 100644 QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldData.swift create mode 100644 QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldType.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewCheckboxCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewDateCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewDropdownCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewIntegerCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewMilestoneCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewMultiselectCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewStepsCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewStringCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewTextCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewURLCaseFieldTests.swift create mode 100644 QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewUserCaseFieldTests.swift create mode 100644 QuizTrainTests/Testing Protocols/Asserts/AssertCodable.swift create mode 100644 QuizTrainTests/Testing Protocols/Tests/CodableTests.swift diff --git a/Example/Cartfile.private b/Example/Cartfile.private index 75c03d0..9569117 100644 --- a/Example/Cartfile.private +++ b/Example/Cartfile.private @@ -1 +1 @@ -github "venmo/QuizTrain" ~> 1.2.0 +github "venmo/QuizTrain" ~> 1.2.1 diff --git a/Example/Cartfile.resolved b/Example/Cartfile.resolved index 9c7896e..fed023b 100644 --- a/Example/Cartfile.resolved +++ b/Example/Cartfile.resolved @@ -1 +1 @@ -github "venmo/QuizTrain" "v1.2.0" +github "venmo/QuizTrain" "v1.2.1" diff --git a/QuizTrain.xcodeproj/project.pbxproj b/QuizTrain.xcodeproj/project.pbxproj index 988ce7b..6002344 100644 --- a/QuizTrain.xcodeproj/project.pbxproj +++ b/QuizTrain.xcodeproj/project.pbxproj @@ -9,6 +9,9 @@ /* Begin PBXBuildFile section */ FE018DC71F85708C001A2FEF /* ModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE018DC61F85708C001A2FEF /* ModelTests.swift */; }; FE11181C1F6C3A4B00D24A5F /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE11181B1F6C3A4B00D24A5F /* Config.swift */; }; + FE12A9CD21822A570010D407 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; + FE12A9CE21822A580010D407 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; + FE12A9CF21822A580010D407 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; FE1474DA1FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474D91FAA698F0049DA84 /* ObjectAPI.RequestErrorDebug.swift */; }; FE1474DC1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DB1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift */; }; FE1474DE1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE1474DD1FAA69B70049DA84 /* ObjectAPI.UpdateRequestErrorDebug.swift */; }; @@ -44,6 +47,26 @@ FE2D018B1FBCE70B00473B84 /* NewPlan.Entry.Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2D018A1FBCE70B00473B84 /* NewPlan.Entry.Run.swift */; }; FE2F1AD51F843AEE00FF9E0C /* AssertJSONDeserializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE58F54B1F7EBF24009A1B4E /* AssertJSONDeserializing.swift */; }; FE2F1AD71F844FCD00FF9E0C /* JSONSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F1AD61F844FCD00FF9E0C /* JSONSerializable.swift */; }; + FE2F6197217691170002A705 /* NewCaseFieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F6196217691170002A705 /* NewCaseFieldType.swift */; }; + FE2F6198217691170002A705 /* NewCaseFieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F6196217691170002A705 /* NewCaseFieldType.swift */; }; + FE2F6199217691170002A705 /* NewCaseFieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F6196217691170002A705 /* NewCaseFieldType.swift */; }; + FE2F619A217691170002A705 /* NewCaseFieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F6196217691170002A705 /* NewCaseFieldType.swift */; }; + FE2F619C217691650002A705 /* NewCaseFieldConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F619B217691650002A705 /* NewCaseFieldConfig.swift */; }; + FE2F619D217691650002A705 /* NewCaseFieldConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F619B217691650002A705 /* NewCaseFieldConfig.swift */; }; + FE2F619E217691650002A705 /* NewCaseFieldConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F619B217691650002A705 /* NewCaseFieldConfig.swift */; }; + FE2F619F217691650002A705 /* NewCaseFieldConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F619B217691650002A705 /* NewCaseFieldConfig.swift */; }; + FE2F61A1217691DE0002A705 /* NewCaseFieldData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61A0217691DE0002A705 /* NewCaseFieldData.swift */; }; + FE2F61A2217691DE0002A705 /* NewCaseFieldData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61A0217691DE0002A705 /* NewCaseFieldData.swift */; }; + FE2F61A3217691DE0002A705 /* NewCaseFieldData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61A0217691DE0002A705 /* NewCaseFieldData.swift */; }; + FE2F61A4217691DE0002A705 /* NewCaseFieldData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61A0217691DE0002A705 /* NewCaseFieldData.swift */; }; + FE2F61A6217692030002A705 /* NewCaseFieldConfigContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61A5217692030002A705 /* NewCaseFieldConfigContext.swift */; }; + FE2F61A7217692030002A705 /* NewCaseFieldConfigContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61A5217692030002A705 /* NewCaseFieldConfigContext.swift */; }; + FE2F61A8217692030002A705 /* NewCaseFieldConfigContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61A5217692030002A705 /* NewCaseFieldConfigContext.swift */; }; + FE2F61A9217692030002A705 /* NewCaseFieldConfigContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61A5217692030002A705 /* NewCaseFieldConfigContext.swift */; }; + FE2F61AB217693020002A705 /* NewCaseFieldConfigOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61AA217693020002A705 /* NewCaseFieldConfigOptions.swift */; }; + FE2F61AC217693020002A705 /* NewCaseFieldConfigOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61AA217693020002A705 /* NewCaseFieldConfigOptions.swift */; }; + FE2F61AD217693020002A705 /* NewCaseFieldConfigOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61AA217693020002A705 /* NewCaseFieldConfigOptions.swift */; }; + FE2F61AE217693020002A705 /* NewCaseFieldConfigOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2F61AA217693020002A705 /* NewCaseFieldConfigOptions.swift */; }; FE30F11D1F6AE4A300AA7761 /* Milestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95C1F59F450007E946D /* Milestone.swift */; }; FE30F11E1F6AED8100AA7761 /* ConfigurationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A9741F5A042D007E946D /* ConfigurationGroup.swift */; }; FE30F11F1F6AEDF600AA7761 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE23A95A1F59F443007E946D /* Configuration.swift */; }; @@ -74,13 +97,49 @@ FE3795E61FA7EA6C0030C395 /* AssertAddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E51FA7EA6C0030C395 /* AssertAddRequestJSON.swift */; }; FE3795E81FA7EB380030C395 /* AddRequestJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795E71FA7EB380030C395 /* AddRequestJSONTests.swift */; }; FE3899171FCF2BDE0032E265 /* GetTemplatesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3899161FCF2BDE0032E265 /* GetTemplatesOperation.swift */; }; - FE4668A120C7467A003BA033 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; - FE4668A220C7467A003BA033 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; - FE4668A320C7467A003BA033 /* ObjectAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4F1F75956C00DF1039 /* ObjectAPITests.swift */; }; FE496B5F1F7D9BCF00AE9454 /* ResultFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE6A6D4A1F75914E00DF1039 /* ResultFieldTests.swift */; }; FE4E11D71F7C4112004A315E /* Plan.Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4E11D61F7C4112004A315E /* Plan.Entry.swift */; }; + FE4F8BFB218112F600F4628B /* NewCheckboxCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BFA218112F600F4628B /* NewCheckboxCaseFieldTests.swift */; }; + FE4F8BFC218112F600F4628B /* NewCheckboxCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BFA218112F600F4628B /* NewCheckboxCaseFieldTests.swift */; }; + FE4F8BFD218112F600F4628B /* NewCheckboxCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BFA218112F600F4628B /* NewCheckboxCaseFieldTests.swift */; }; + FE4F8C1621811CB300F4628B /* NewDateCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C062181133900F4628B /* NewDateCaseFieldTests.swift */; }; + FE4F8C2021811CB300F4628B /* NewDateCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C062181133900F4628B /* NewDateCaseFieldTests.swift */; }; + FE4F8C2A21811CB300F4628B /* NewDateCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C062181133900F4628B /* NewDateCaseFieldTests.swift */; }; + FE4F8C3721811E9C00F4628B /* NewIntegerCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BEE218112A600F4628B /* NewIntegerCaseFieldTests.swift */; }; + FE4F8C3821811E9D00F4628B /* NewIntegerCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BEE218112A600F4628B /* NewIntegerCaseFieldTests.swift */; }; + FE4F8C3921811E9D00F4628B /* NewIntegerCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BEE218112A600F4628B /* NewIntegerCaseFieldTests.swift */; }; + FE4F8C3A21811FA200F4628B /* NewMilestoneCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C0A2181134F00F4628B /* NewMilestoneCaseFieldTests.swift */; }; + FE4F8C3B21811FA200F4628B /* NewMilestoneCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C0A2181134F00F4628B /* NewMilestoneCaseFieldTests.swift */; }; + FE4F8C3C21811FA200F4628B /* NewMilestoneCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C0A2181134F00F4628B /* NewMilestoneCaseFieldTests.swift */; }; + FE4F8C3D21811FC300F4628B /* NewStringCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5669DB217AABFE00530D90 /* NewStringCaseFieldTests.swift */; }; + FE4F8C3E21811FC300F4628B /* NewStringCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5669DB217AABFE00530D90 /* NewStringCaseFieldTests.swift */; }; + FE4F8C3F21811FC400F4628B /* NewStringCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5669DB217AABFE00530D90 /* NewStringCaseFieldTests.swift */; }; + FE4F8C40218120D600F4628B /* NewURLCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BF6218112DB00F4628B /* NewURLCaseFieldTests.swift */; }; + FE4F8C41218120D600F4628B /* NewURLCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BF6218112DB00F4628B /* NewURLCaseFieldTests.swift */; }; + FE4F8C42218120D700F4628B /* NewURLCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BF6218112DB00F4628B /* NewURLCaseFieldTests.swift */; }; + FE4F8C43218121A200F4628B /* NewUserCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C022181132200F4628B /* NewUserCaseFieldTests.swift */; }; + FE4F8C44218121A200F4628B /* NewUserCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C022181132200F4628B /* NewUserCaseFieldTests.swift */; }; + FE4F8C45218121A300F4628B /* NewUserCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C022181132200F4628B /* NewUserCaseFieldTests.swift */; }; + FE4F8C462181221100F4628B /* NewDropdownCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BFE2181130C00F4628B /* NewDropdownCaseFieldTests.swift */; }; + FE4F8C472181221100F4628B /* NewDropdownCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BFE2181130C00F4628B /* NewDropdownCaseFieldTests.swift */; }; + FE4F8C482181221200F4628B /* NewDropdownCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BFE2181130C00F4628B /* NewDropdownCaseFieldTests.swift */; }; FE4F8F391F84361F00447F9E /* NewCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F381F84361F00447F9E /* NewCase.swift */; }; FE4F8F3B1F8437DE00447F9E /* JSONDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8F3A1F8437DE00447F9E /* JSONDeserializable.swift */; }; + FE518DCE218132D40087210E /* NewMultiselectCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C122181137D00F4628B /* NewMultiselectCaseFieldTests.swift */; }; + FE518DCF218132D40087210E /* NewMultiselectCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C122181137D00F4628B /* NewMultiselectCaseFieldTests.swift */; }; + FE518DD0218132D40087210E /* NewMultiselectCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C122181137D00F4628B /* NewMultiselectCaseFieldTests.swift */; }; + FE518DD1218134970087210E /* NewStepsCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C0E2181136A00F4628B /* NewStepsCaseFieldTests.swift */; }; + FE518DD2218134980087210E /* NewStepsCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C0E2181136A00F4628B /* NewStepsCaseFieldTests.swift */; }; + FE518DD3218134980087210E /* NewStepsCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8C0E2181136A00F4628B /* NewStepsCaseFieldTests.swift */; }; + FE518DD4218135DC0087210E /* NewTextCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BF2218112C500F4628B /* NewTextCaseFieldTests.swift */; }; + FE518DD5218135DD0087210E /* NewTextCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BF2218112C500F4628B /* NewTextCaseFieldTests.swift */; }; + FE518DD6218135DD0087210E /* NewTextCaseFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4F8BF2218112C500F4628B /* NewTextCaseFieldTests.swift */; }; + FE518DE321813E400087210E /* AssertCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE518DE221813E400087210E /* AssertCodable.swift */; }; + FE518DE421813E400087210E /* AssertCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE518DE221813E400087210E /* AssertCodable.swift */; }; + FE518DE521813E400087210E /* AssertCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE518DE221813E400087210E /* AssertCodable.swift */; }; + FE518DE72181432E0087210E /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE518DE62181432E0087210E /* CodableTests.swift */; }; + FE518DE82181432E0087210E /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE518DE62181432E0087210E /* CodableTests.swift */; }; + FE518DE92181432E0087210E /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE518DE62181432E0087210E /* CodableTests.swift */; }; FE51EFA21F882454007012E0 /* JSONDictionaryContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE51EFA11F882454007012E0 /* JSONDictionaryContainerTests.swift */; }; FE5259121F82D1AD00E0DDB7 /* JSONKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5259111F82D1AD00E0DDB7 /* JSONKey.swift */; }; FE549C421FBE5147008CDFCE /* NewPlanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3795B71FA799520030C395 /* NewPlanTests.swift */; }; @@ -570,6 +629,10 @@ FEF172631F857F53004FFFFF /* AssertJSONSerializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF172621F857F53004FFFFF /* AssertJSONSerializing.swift */; }; FEF172661F858880004FFFFF /* AssertJSONTwoWaySerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF172641F858230004FFFFF /* AssertJSONTwoWaySerialization.swift */; }; FEF9CE7E1F7C538F0078CD4E /* Plan.EntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE30A6521F7C261400CE7D6B /* Plan.EntryTests.swift */; }; + FEFF4DFC20AE235A00E3613A /* NewCaseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFF4DFB20AE235A00E3613A /* NewCaseField.swift */; }; + FEFF4DFD20AE235A00E3613A /* NewCaseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFF4DFB20AE235A00E3613A /* NewCaseField.swift */; }; + FEFF4DFE20AE235A00E3613A /* NewCaseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFF4DFB20AE235A00E3613A /* NewCaseField.swift */; }; + FEFF4DFF20AE235A00E3613A /* NewCaseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFF4DFB20AE235A00E3613A /* NewCaseField.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -648,6 +711,11 @@ FE2877F91FBBC47B004503FB /* FilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterTests.swift; sourceTree = ""; }; FE2D018A1FBCE70B00473B84 /* NewPlan.Entry.Run.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPlan.Entry.Run.swift; sourceTree = ""; }; FE2F1AD61F844FCD00FF9E0C /* JSONSerializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONSerializable.swift; sourceTree = ""; }; + FE2F6196217691170002A705 /* NewCaseFieldType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseFieldType.swift; sourceTree = ""; }; + FE2F619B217691650002A705 /* NewCaseFieldConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseFieldConfig.swift; sourceTree = ""; }; + FE2F61A0217691DE0002A705 /* NewCaseFieldData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseFieldData.swift; sourceTree = ""; }; + FE2F61A5217692030002A705 /* NewCaseFieldConfigContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseFieldConfigContext.swift; sourceTree = ""; }; + FE2F61AA217693020002A705 /* NewCaseFieldConfigOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseFieldConfigOptions.swift; sourceTree = ""; }; FE30A6521F7C261400CE7D6B /* Plan.EntryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Plan.EntryTests.swift; sourceTree = ""; }; FE331C1B1F6B406300F9A653 /* CustomFieldType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFieldType.swift; sourceTree = ""; }; FE3795A91FA7915C0030C395 /* AssertUpdateRequestJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertUpdateRequestJSON.swift; sourceTree = ""; }; @@ -679,10 +747,23 @@ FE3795E71FA7EB380030C395 /* AddRequestJSONTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRequestJSONTests.swift; sourceTree = ""; }; FE3899161FCF2BDE0032E265 /* GetTemplatesOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetTemplatesOperation.swift; sourceTree = ""; }; FE4E11D61F7C4112004A315E /* Plan.Entry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Plan.Entry.swift; sourceTree = ""; }; + FE4F8BEE218112A600F4628B /* NewIntegerCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIntegerCaseFieldTests.swift; sourceTree = ""; }; + FE4F8BF2218112C500F4628B /* NewTextCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTextCaseFieldTests.swift; sourceTree = ""; }; + FE4F8BF6218112DB00F4628B /* NewURLCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewURLCaseFieldTests.swift; sourceTree = ""; }; + FE4F8BFA218112F600F4628B /* NewCheckboxCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCheckboxCaseFieldTests.swift; sourceTree = ""; }; + FE4F8BFE2181130C00F4628B /* NewDropdownCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDropdownCaseFieldTests.swift; sourceTree = ""; }; + FE4F8C022181132200F4628B /* NewUserCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewUserCaseFieldTests.swift; sourceTree = ""; }; + FE4F8C062181133900F4628B /* NewDateCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDateCaseFieldTests.swift; sourceTree = ""; }; + FE4F8C0A2181134F00F4628B /* NewMilestoneCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewMilestoneCaseFieldTests.swift; sourceTree = ""; }; + FE4F8C0E2181136A00F4628B /* NewStepsCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStepsCaseFieldTests.swift; sourceTree = ""; }; + FE4F8C122181137D00F4628B /* NewMultiselectCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewMultiselectCaseFieldTests.swift; sourceTree = ""; }; FE4F8F381F84361F00447F9E /* NewCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCase.swift; sourceTree = ""; }; FE4F8F3A1F8437DE00447F9E /* JSONDeserializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDeserializable.swift; sourceTree = ""; }; + FE518DE221813E400087210E /* AssertCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertCodable.swift; sourceTree = ""; }; + FE518DE62181432E0087210E /* CodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableTests.swift; sourceTree = ""; }; FE51EFA11F882454007012E0 /* JSONDictionaryContainerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDictionaryContainerTests.swift; sourceTree = ""; }; FE5259111F82D1AD00E0DDB7 /* JSONKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONKey.swift; sourceTree = ""; }; + FE5669DB217AABFE00530D90 /* NewStringCaseFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStringCaseFieldTests.swift; sourceTree = ""; }; FE5869D61F7478D600BE5C5C /* CustomFields.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFields.swift; sourceTree = ""; }; FE58F5471F7EBF04009A1B4E /* AssertProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertProperties.swift; sourceTree = ""; }; FE58F5491F7EBF12009A1B4E /* AssertCustomFields.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertCustomFields.swift; sourceTree = ""; }; @@ -774,6 +855,7 @@ FEEC443F1FB4D3270042BD5A /* QueryItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryItemProvider.swift; sourceTree = ""; }; FEF172621F857F53004FFFFF /* AssertJSONSerializing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertJSONSerializing.swift; sourceTree = ""; }; FEF172641F858230004FFFFF /* AssertJSONTwoWaySerialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssertJSONTwoWaySerialization.swift; sourceTree = ""; }; + FEFF4DFB20AE235A00E3613A /* NewCaseField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseField.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -837,6 +919,7 @@ children = ( FE3795DD1FA7C90F0030C395 /* README.md */, FE3795E71FA7EB380030C395 /* AddRequestJSONTests.swift */, + FE518DE62181432E0087210E /* CodableTests.swift */, FEE774341FA8E2480016AACE /* EquatableTests.swift */, FE3795D11FA7C7960030C395 /* InitTests.swift */, FE3795D31FA7C7A50030C395 /* JSONDeserializingTests.swift */, @@ -1037,6 +1120,19 @@ path = Filters; sourceTree = ""; }; + FE2F6195217691030002A705 /* NewCaseField */ = { + isa = PBXGroup; + children = ( + FEFF4DFB20AE235A00E3613A /* NewCaseField.swift */, + FE2F619B217691650002A705 /* NewCaseFieldConfig.swift */, + FE2F61A5217692030002A705 /* NewCaseFieldConfigContext.swift */, + FE2F61AA217693020002A705 /* NewCaseFieldConfigOptions.swift */, + FE2F61A0217691DE0002A705 /* NewCaseFieldData.swift */, + FE2F6196217691170002A705 /* NewCaseFieldType.swift */, + ); + path = NewCaseField; + sourceTree = ""; + }; FE3795E21FA7DB900030C395 /* Testing Protocols */ = { isa = PBXGroup; children = ( @@ -1064,6 +1160,24 @@ path = Operations; sourceTree = ""; }; + FE4F8BED2181108A00F4628B /* NewCaseFieldTests */ = { + isa = PBXGroup; + children = ( + FE4F8BFA218112F600F4628B /* NewCheckboxCaseFieldTests.swift */, + FE4F8C062181133900F4628B /* NewDateCaseFieldTests.swift */, + FE4F8BFE2181130C00F4628B /* NewDropdownCaseFieldTests.swift */, + FE4F8BEE218112A600F4628B /* NewIntegerCaseFieldTests.swift */, + FE4F8C0A2181134F00F4628B /* NewMilestoneCaseFieldTests.swift */, + FE4F8C122181137D00F4628B /* NewMultiselectCaseFieldTests.swift */, + FE4F8C0E2181136A00F4628B /* NewStepsCaseFieldTests.swift */, + FE5669DB217AABFE00530D90 /* NewStringCaseFieldTests.swift */, + FE4F8BF2218112C500F4628B /* NewTextCaseFieldTests.swift */, + FE4F8BF6218112DB00F4628B /* NewURLCaseFieldTests.swift */, + FE4F8C022181132200F4628B /* NewUserCaseFieldTests.swift */, + ); + path = NewCaseFieldTests; + sourceTree = ""; + }; FE52590D1F82A95100E0DDB7 /* Types */ = { isa = PBXGroup; children = ( @@ -1108,6 +1222,7 @@ isa = PBXGroup; children = ( FE3795E51FA7EA6C0030C395 /* AssertAddRequestJSON.swift */, + FE518DE221813E400087210E /* AssertCodable.swift */, FE58F5491F7EBF12009A1B4E /* AssertCustomFields.swift */, FEE774361FA8EA980016AACE /* AssertEquatable.swift */, FE58F54B1F7EBF24009A1B4E /* AssertJSONDeserializing.swift */, @@ -1171,6 +1286,7 @@ isa = PBXGroup; children = ( FE4F8F381F84361F00447F9E /* NewCase.swift */, + FE2F6195217691030002A705 /* NewCaseField */, FEDCBEEE1F96BEE70083AD46 /* NewConfiguration.swift */, FEDCBEF01F96BEF10083AD46 /* NewConfigurationGroup.swift */, FEDCBEF21F96BF020083AD46 /* NewMilestone.swift */, @@ -1212,6 +1328,7 @@ isa = PBXGroup; children = ( FE3795AF1FA799150030C395 /* NewCaseTests.swift */, + FE4F8BED2181108A00F4628B /* NewCaseFieldTests */, FE3795B31FA799330030C395 /* NewConfigurationGroupTests.swift */, FE3795B11FA799270030C395 /* NewConfigurationTests.swift */, FE3795B51FA799400030C395 /* NewMilestoneTests.swift */, @@ -1755,6 +1872,7 @@ FE9F38A61F69E5AD003BBA36 /* Plan.swift in Sources */, FEBDA3C21FD1C88300124430 /* ObjectAPI.swift in Sources */, FEC214001FD7302900036B17 /* ObjectAPI.MatchErrorDebug.swift in Sources */, + FE2F6197217691170002A705 /* NewCaseFieldType.swift in Sources */, FE23A9671F59F4B3007E946D /* ResultField.swift in Sources */, FE23A9711F59F4E2007E946D /* Test.swift in Sources */, FEDFF2751FE1B09000AEB3D6 /* SingleMatchError.swift in Sources */, @@ -1762,6 +1880,7 @@ FE30F11F1F6AEDF600AA7761 /* Configuration.swift in Sources */, FE1474E51FAA6A0D0049DA84 /* ObjectAPI.ServerErrorDebug.swift in Sources */, FE2245851F75AC82009F2B2B /* CustomFieldsContainer.swift in Sources */, + FE2F61A1217691DE0002A705 /* NewCaseFieldData.swift in Sources */, FE208DF11FBB6BA10065BE88 /* NewCaseResults.swift in Sources */, FE63715F1FD61CA500192CED /* GetConfigurationGroupsOperation.swift in Sources */, FE5869D71F7478D600BE5C5C /* CustomFields.swift in Sources */, @@ -1798,6 +1917,7 @@ FE5259121F82D1AD00E0DDB7 /* JSONKey.swift in Sources */, FE978CEB1F9528400005D181 /* API.RequestErrorDebug.swift in Sources */, FEA348781F5A141300C1E37A /* API.swift in Sources */, + FE2F61AB217693020002A705 /* NewCaseFieldConfigOptions.swift in Sources */, FEB546861F9135D500AA6DA5 /* AddRequestJSONKeys.swift in Sources */, FE331BDE1F6AF98F00F9A653 /* CaseType.swift in Sources */, FEDCBEF71F96BF290083AD46 /* NewProject.swift in Sources */, @@ -1807,7 +1927,9 @@ FE2D018B1FBCE70B00473B84 /* NewPlan.Entry.Run.swift in Sources */, FEDD80791F69D7A700D56EF9 /* Project.swift in Sources */, FEBDA3C11FD1C7F400124430 /* ErrorContainer.swift in Sources */, + FE2F61A6217692030002A705 /* NewCaseFieldConfigContext.swift in Sources */, FE1474E21FAA69F70049DA84 /* ObjectAPI.ClientErrorDebug.swift in Sources */, + FE2F619C217691650002A705 /* NewCaseFieldConfig.swift in Sources */, FEDD807A1F69DE1A00D56EF9 /* Priority.swift in Sources */, FE1474DC1FAA69AA0049DA84 /* ObjectAPI.DataRequestErrorDebug.swift in Sources */, FE331BDF1F6AFE5D00F9A653 /* CaseField.swift in Sources */, @@ -1828,6 +1950,7 @@ FE23A9691F59F4BA007E946D /* Run.swift in Sources */, FEDCBEF11F96BEF10083AD46 /* NewConfigurationGroup.swift in Sources */, FE4F8F391F84361F00447F9E /* NewCase.swift in Sources */, + FEFF4DFC20AE235A00E3613A /* NewCaseField.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1835,15 +1958,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FE518DD4218135DC0087210E /* NewTextCaseFieldTests.swift in Sources */, FE6A6D311F7590C700DF1039 /* PlanTests.swift in Sources */, FE58F5661F7EEA29009A1B4E /* CustomFieldsDataProvider.swift in Sources */, FE3795B41FA799330030C395 /* NewConfigurationGroupTests.swift in Sources */, FE58F5611F7EE91D009A1B4E /* JSONDataProvider.swift in Sources */, FE6A6D2F1F7590BA00DF1039 /* MilestoneTests.swift in Sources */, + FE4F8C40218120D600F4628B /* NewURLCaseFieldTests.swift in Sources */, FE3795D41FA7C7A50030C395 /* JSONDeserializingTests.swift in Sources */, FE3795CA1FA79A070030C395 /* UpdatePlanEntryRunsTests.swift in Sources */, FE3795D21FA7C7960030C395 /* InitTests.swift in Sources */, + FE518DE72181432E0087210E /* CodableTests.swift in Sources */, FE3795CC1FA7A3AD0030C395 /* ObjectProvider.swift in Sources */, + FE4F8C3D21811FC300F4628B /* NewStringCaseFieldTests.swift in Sources */, + FE4F8C43218121A200F4628B /* NewUserCaseFieldTests.swift in Sources */, FE6A6D2D1F7590B000DF1039 /* ConfigurationGroupTests.swift in Sources */, FE3795CE1FA7A4540030C395 /* AddModelTests.swift in Sources */, FE6A6D331F7590D200DF1039 /* PriorityTests.swift in Sources */, @@ -1856,20 +1984,25 @@ FE3795B21FA799270030C395 /* NewConfigurationTests.swift in Sources */, FE3795AA1FA7915C0030C395 /* AssertUpdateRequestJSON.swift in Sources */, FE3795C21FA799B70030C395 /* NewTestResults.ResultTests.swift in Sources */, + FE518DE321813E400087210E /* AssertCodable.swift in Sources */, + FE12A9CD21822A570010D407 /* ObjectAPITests.swift in Sources */, + FE4F8C3721811E9C00F4628B /* NewIntegerCaseFieldTests.swift in Sources */, FE3795E41FA7E1770030C395 /* NewCaseTests.swift in Sources */, FEF172631F857F53004FFFFF /* AssertJSONSerializing.swift in Sources */, + FE4F8C3A21811FA200F4628B /* NewMilestoneCaseFieldTests.swift in Sources */, FE6A6D2B1F7590A100DF1039 /* ConfigurationTests.swift in Sources */, FE6A6D491F75914300DF1039 /* CustomFieldTypeTests.swift in Sources */, + FE4F8C1621811CB300F4628B /* NewDateCaseFieldTests.swift in Sources */, FE018DC71F85708C001A2FEF /* ModelTests.swift in Sources */, FE6A6D391F7590F000DF1039 /* RunTests.swift in Sources */, FE6A6D411F75911A00DF1039 /* TestTests.swift in Sources */, FE3795B61FA799400030C395 /* NewMilestoneTests.swift in Sources */, FEF9CE7E1F7C538F0078CD4E /* Plan.EntryTests.swift in Sources */, + FE4F8C462181221100F4628B /* NewDropdownCaseFieldTests.swift in Sources */, FE3795DA1FA7C7E30030C395 /* VariablePropertyTests.swift in Sources */, FE549C431FBE514A008CDFCE /* NewPlan.EntryTests.swift in Sources */, FE3795D61FA7C7B60030C395 /* JSONSerializingTests.swift in Sources */, FE6A6D271F75908100DF1039 /* CaseTests.swift in Sources */, - FE4668A120C7467A003BA033 /* ObjectAPITests.swift in Sources */, FE5A24F41FE099BD00198848 /* Array+Random.swift in Sources */, FEC2140E1FD7537800036B17 /* AsyncOperationTests.swift in Sources */, FE2877FA1FBBC47B004503FB /* FilterTests.swift in Sources */, @@ -1888,6 +2021,7 @@ FE3795C61FA799E30030C395 /* NewSectionTests.swift in Sources */, FEE774351FA8E2480016AACE /* EquatableTests.swift in Sources */, FE75B2E01F83078A00DF367A /* CustomFieldsContainerTests.swift in Sources */, + FE518DD1218134970087210E /* NewStepsCaseFieldTests.swift in Sources */, FE549C441FBE514C008CDFCE /* NewPlan.Entry.RunTests.swift in Sources */, FEAE7F961F7C5D0C00906FE1 /* Config.ContextTests.swift in Sources */, FE6A6D431F75912400DF1039 /* UserTests.swift in Sources */, @@ -1909,7 +2043,9 @@ FE2877F41FBBBC50004503FB /* NewCaseResultsTests.swift in Sources */, FECF66761FDF593600015CC4 /* UniqueSelectionTests.swift in Sources */, FE3795BC1FA7998F0030C395 /* NewProjectTests.swift in Sources */, + FE4F8BFB218112F600F4628B /* NewCheckboxCaseFieldTests.swift in Sources */, FE791A251F7D9FFA00D7E870 /* Project.SuiteModeTests.swift in Sources */, + FE518DCE218132D40087210E /* NewMultiselectCaseFieldTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1926,6 +2062,7 @@ FEAE999B1FEC42CE00B52CA9 /* CustomFields.swift in Sources */, FEAE9A8E1FEC4F1D00B52CA9 /* ObjectAPI.swift in Sources */, FEAE9A341FEC4F0A00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */, + FE2F619A217691170002A705 /* NewCaseFieldType.swift in Sources */, FEAE9A1D1FEC4EFF00B52CA9 /* Test.swift in Sources */, FEAE9A651FEC4F1400B52CA9 /* NewSection.swift in Sources */, FEAE9A5A1FEC4F1400B52CA9 /* NewMilestone.swift in Sources */, @@ -1933,6 +2070,7 @@ FEAE9A871FEC4F1800B52CA9 /* UpdatePlanEntryRuns.swift in Sources */, FEAE99D71FEC431900B52CA9 /* UpdateRequestJSONKeys.swift in Sources */, FEAE9A3B1FEC4F0A00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */, + FE2F61A4217691DE0002A705 /* NewCaseFieldData.swift in Sources */, FEAE9A8B1FEC4F1D00B52CA9 /* GetProjectOperation.swift in Sources */, FEAE99E71FEC432E00B52CA9 /* CustomFieldType.swift in Sources */, FEAE99DB1FEC431E00B52CA9 /* Validatable.swift in Sources */, @@ -1969,6 +2107,7 @@ FEAE9A331FEC4F0A00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */, FEAE9A391FEC4F0A00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */, FEAE9A0C1FEC4EED00B52CA9 /* Priority.swift in Sources */, + FE2F61AE217693020002A705 /* NewCaseFieldConfigOptions.swift in Sources */, FEAE9A5E1FEC4F1400B52CA9 /* NewProject.swift in Sources */, FEAE9A601FEC4F1400B52CA9 /* NewCaseResults.swift in Sources */, FEAE9A181FEC4EFF00B52CA9 /* Run.swift in Sources */, @@ -1978,7 +2117,9 @@ FEAE99941FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */, FEAE9A2E1FEC4F0600B52CA9 /* API.RequestResultDebug.swift in Sources */, FEAE9A8A1FEC4F1D00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */, + FE2F61A9217692030002A705 /* NewCaseFieldConfigContext.swift in Sources */, FEAE9A5D1FEC4F1400B52CA9 /* NewPlan.Entry.Run.swift in Sources */, + FE2F619F217691650002A705 /* NewCaseFieldConfig.swift in Sources */, FEAE99971FEC42CA00B52CA9 /* AddRequestJSONKeys.swift in Sources */, FEAE9A641FEC4F1400B52CA9 /* NewRun.swift in Sources */, FEAE99ED1FEC433500B52CA9 /* Config.swift in Sources */, @@ -1999,6 +2140,7 @@ FEAE99BB1FEC42F500B52CA9 /* Date+Seconds.swift in Sources */, FEAE99E41FEC432A00B52CA9 /* UniqueSelection.swift in Sources */, FEAE9A591FEC4F1400B52CA9 /* NewConfigurationGroup.swift in Sources */, + FEFF4DFF20AE235A00E3613A /* NewCaseField.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2015,6 +2157,7 @@ FEAE99991FEC42CD00B52CA9 /* CustomFields.swift in Sources */, FEAE9A931FEC4F1E00B52CA9 /* ObjectAPI.swift in Sources */, FEAE9A3E1FEC4F0B00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */, + FE2F6199217691170002A705 /* NewCaseFieldType.swift in Sources */, FEAE9A241FEC4F0000B52CA9 /* Test.swift in Sources */, FEAE9A751FEC4F1500B52CA9 /* NewSection.swift in Sources */, FEAE9A6A1FEC4F1500B52CA9 /* NewMilestone.swift in Sources */, @@ -2022,6 +2165,7 @@ FEAE9A881FEC4F1900B52CA9 /* UpdatePlanEntryRuns.swift in Sources */, FEAE99D81FEC431900B52CA9 /* UpdateRequestJSONKeys.swift in Sources */, FEAE9A451FEC4F0B00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */, + FE2F61A3217691DE0002A705 /* NewCaseFieldData.swift in Sources */, FEAE9A901FEC4F1E00B52CA9 /* GetProjectOperation.swift in Sources */, FEAE99E81FEC432E00B52CA9 /* CustomFieldType.swift in Sources */, FEAE99DC1FEC431F00B52CA9 /* Validatable.swift in Sources */, @@ -2058,6 +2202,7 @@ FEAE9A3D1FEC4F0B00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */, FEAE9A431FEC4F0B00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */, FEAE9A0D1FEC4EED00B52CA9 /* Priority.swift in Sources */, + FE2F61AD217693020002A705 /* NewCaseFieldConfigOptions.swift in Sources */, FEAE9A6E1FEC4F1500B52CA9 /* NewProject.swift in Sources */, FEAE9A701FEC4F1500B52CA9 /* NewCaseResults.swift in Sources */, FEAE9A1F1FEC4F0000B52CA9 /* Run.swift in Sources */, @@ -2067,7 +2212,9 @@ FEAE99931FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */, FEAE9A301FEC4F0600B52CA9 /* API.RequestResultDebug.swift in Sources */, FEAE9A8F1FEC4F1E00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */, + FE2F61A8217692030002A705 /* NewCaseFieldConfigContext.swift in Sources */, FEAE9A6D1FEC4F1500B52CA9 /* NewPlan.Entry.Run.swift in Sources */, + FE2F619E217691650002A705 /* NewCaseFieldConfig.swift in Sources */, FEAE99961FEC42CA00B52CA9 /* AddRequestJSONKeys.swift in Sources */, FEAE9A741FEC4F1500B52CA9 /* NewRun.swift in Sources */, FEAE99EE1FEC433600B52CA9 /* Config.swift in Sources */, @@ -2088,6 +2235,7 @@ FEAE99BC1FEC42F600B52CA9 /* Date+Seconds.swift in Sources */, FEAE99E51FEC432A00B52CA9 /* UniqueSelection.swift in Sources */, FEAE9A691FEC4F1500B52CA9 /* NewConfigurationGroup.swift in Sources */, + FEFF4DFE20AE235A00E3613A /* NewCaseField.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2095,15 +2243,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FE518DD6218135DD0087210E /* NewTextCaseFieldTests.swift in Sources */, FEAE9B1A1FEC501F00B52CA9 /* NewProjectTests.swift in Sources */, FEAE9ADD1FEC501000B52CA9 /* CustomFieldTypeTests.swift in Sources */, FEAE9B0D1FEC501700B52CA9 /* FilterTests.swift in Sources */, FEAE9B161FEC501F00B52CA9 /* NewMilestoneTests.swift in Sources */, FEAE9ADA1FEC500A00B52CA9 /* AsyncOperationTests.swift in Sources */, + FE4F8C42218120D700F4628B /* NewURLCaseFieldTests.swift in Sources */, FEAE9B151FEC501F00B52CA9 /* NewConfigurationTests.swift in Sources */, FEAE9AB81FEC4FF900B52CA9 /* JSONDataProvider.swift in Sources */, FEAE9B1D1FEC501F00B52CA9 /* NewCaseResults.ResultTests.swift in Sources */, + FE518DE92181432E0087210E /* CodableTests.swift in Sources */, FEAE9AEB1FEC501400B52CA9 /* PlanTests.swift in Sources */, + FE4F8C3F21811FC400F4628B /* NewStringCaseFieldTests.swift in Sources */, + FE4F8C45218121A300F4628B /* NewUserCaseFieldTests.swift in Sources */, FEAE9AF21FEC501400B52CA9 /* SectionTests.swift in Sources */, FEAE9AEE1FEC501400B52CA9 /* ProjectTests.swift in Sources */, FEAE9ADE1FEC501000B52CA9 /* Project.SuiteModeTests.swift in Sources */, @@ -2116,20 +2269,25 @@ FEAE9ACF1FEC500200B52CA9 /* JSONDictionaryContainerTests.swift in Sources */, FEAE9ABB1FEC4FFE00B52CA9 /* AddRequestJSONTests.swift in Sources */, FEAE9ABE1FEC4FFE00B52CA9 /* JSONDeserializingTests.swift in Sources */, + FE518DE521813E400087210E /* AssertCodable.swift in Sources */, + FE12A9CF21822A580010D407 /* ObjectAPITests.swift in Sources */, + FE4F8C3921811E9D00F4628B /* NewIntegerCaseFieldTests.swift in Sources */, FEAE9ABA1FEC4FF900B52CA9 /* ValidatableObjectProvider.swift in Sources */, FEAE9B191FEC501F00B52CA9 /* NewPlan.Entry.RunTests.swift in Sources */, + FE4F8C3C21811FA200F4628B /* NewMilestoneCaseFieldTests.swift in Sources */, FEAE9AF11FEC501400B52CA9 /* RunTests.swift in Sources */, FEAE9AF61FEC501400B52CA9 /* TestTests.swift in Sources */, + FE4F8C2A21811CB300F4628B /* NewDateCaseFieldTests.swift in Sources */, FEAE9AD41FEC500600B52CA9 /* Array+RandomTests.swift in Sources */, FEAE9AE41FEC501400B52CA9 /* Config.ContextTests.swift in Sources */, FEAE9A9F1FEC4FF200B52CA9 /* TestCredentials.swift in Sources */, FEAE9AE31FEC501400B52CA9 /* ConfigTests.swift in Sources */, FEAE9AE51FEC501400B52CA9 /* CaseTests.swift in Sources */, + FE4F8C482181221200F4628B /* NewDropdownCaseFieldTests.swift in Sources */, FEAE9ABF1FEC4FFE00B52CA9 /* JSONSerializingTests.swift in Sources */, FEAE9ACE1FEC500200B52CA9 /* ErrorContainerTests.swift in Sources */, FEAE9B181FEC501F00B52CA9 /* NewPlan.EntryTests.swift in Sources */, FEAE9AB01FEC4FF500B52CA9 /* AssertProperties.swift in Sources */, - FE4668A320C7467A003BA033 /* ObjectAPITests.swift in Sources */, FEAE9B201FEC501F00B52CA9 /* NewRunTests.swift in Sources */, FEAE9B131FEC501F00B52CA9 /* NewCaseTests.swift in Sources */, FEAE9ABD1FEC4FFE00B52CA9 /* InitTests.swift in Sources */, @@ -2148,6 +2306,7 @@ FEAE9AEF1FEC501400B52CA9 /* ResultTests.swift in Sources */, FEAE9ABC1FEC4FFE00B52CA9 /* EquatableTests.swift in Sources */, FEAE9B221FEC501F00B52CA9 /* NewSuiteTests.swift in Sources */, + FE518DD3218134980087210E /* NewStepsCaseFieldTests.swift in Sources */, FEAE9AE61FEC501400B52CA9 /* CaseFieldTests.swift in Sources */, FEAE9AAE1FEC4FF500B52CA9 /* AssertJSONSerializing.swift in Sources */, FEAE9AB11FEC4FF500B52CA9 /* AssertUpdateRequestJSON.swift in Sources */, @@ -2169,7 +2328,9 @@ FEAE9AF51FEC501400B52CA9 /* TemplateTests.swift in Sources */, FEAE9AB91FEC4FF900B52CA9 /* ObjectProvider.swift in Sources */, FEAE9B1C1FEC501F00B52CA9 /* NewCaseResultsTests.swift in Sources */, + FE4F8BFD218112F600F4628B /* NewCheckboxCaseFieldTests.swift in Sources */, FEAE9AC21FEC4FFE00B52CA9 /* ValidatableTests.swift in Sources */, + FE518DD0218132D40087210E /* NewMultiselectCaseFieldTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2186,6 +2347,7 @@ FEAE99981FEC42CD00B52CA9 /* CustomFields.swift in Sources */, FEAE9A981FEC4F1E00B52CA9 /* ObjectAPI.swift in Sources */, FEAE9A481FEC4F0B00B52CA9 /* ObjectAPI.DataProcessingErrorDebug.swift in Sources */, + FE2F6198217691170002A705 /* NewCaseFieldType.swift in Sources */, FEAE9A2B1FEC4F0000B52CA9 /* Test.swift in Sources */, FEAE9A851FEC4F1500B52CA9 /* NewSection.swift in Sources */, FEAE9A7A1FEC4F1500B52CA9 /* NewMilestone.swift in Sources */, @@ -2193,6 +2355,7 @@ FEAE9A891FEC4F1A00B52CA9 /* UpdatePlanEntryRuns.swift in Sources */, FEAE99DA1FEC431B00B52CA9 /* UpdateRequestJSONKeys.swift in Sources */, FEAE9A4F1FEC4F0B00B52CA9 /* ObjectAPI.UpdateRequestErrorDebug.swift in Sources */, + FE2F61A2217691DE0002A705 /* NewCaseFieldData.swift in Sources */, FEAE9A951FEC4F1E00B52CA9 /* GetProjectOperation.swift in Sources */, FEAE99E91FEC432F00B52CA9 /* CustomFieldType.swift in Sources */, FEAE99DD1FEC431F00B52CA9 /* Validatable.swift in Sources */, @@ -2229,6 +2392,7 @@ FEAE9A471FEC4F0B00B52CA9 /* ObjectAPI.ClientErrorDebug.swift in Sources */, FEAE9A4D1FEC4F0B00B52CA9 /* ObjectAPI.ServerErrorDebug.swift in Sources */, FEAE9A0E1FEC4EEE00B52CA9 /* Priority.swift in Sources */, + FE2F61AC217693020002A705 /* NewCaseFieldConfigOptions.swift in Sources */, FEAE9A7E1FEC4F1500B52CA9 /* NewProject.swift in Sources */, FEAE9A801FEC4F1500B52CA9 /* NewCaseResults.swift in Sources */, FEAE9A261FEC4F0000B52CA9 /* Run.swift in Sources */, @@ -2238,7 +2402,9 @@ FEAE99921FEC42C600B52CA9 /* AddRequestJSON.swift in Sources */, FEAE9A321FEC4F0700B52CA9 /* API.RequestResultDebug.swift in Sources */, FEAE9A941FEC4F1E00B52CA9 /* GetConfigurationGroupsOperation.swift in Sources */, + FE2F61A7217692030002A705 /* NewCaseFieldConfigContext.swift in Sources */, FEAE9A7D1FEC4F1500B52CA9 /* NewPlan.Entry.Run.swift in Sources */, + FE2F619D217691650002A705 /* NewCaseFieldConfig.swift in Sources */, FEAE99951FEC42C900B52CA9 /* AddRequestJSONKeys.swift in Sources */, FEAE9A841FEC4F1500B52CA9 /* NewRun.swift in Sources */, FEAE99EF1FEC433600B52CA9 /* Config.swift in Sources */, @@ -2259,6 +2425,7 @@ FEAE99BD1FEC42F700B52CA9 /* Date+Seconds.swift in Sources */, FEAE99E61FEC432B00B52CA9 /* UniqueSelection.swift in Sources */, FEAE9A791FEC4F1500B52CA9 /* NewConfigurationGroup.swift in Sources */, + FEFF4DFD20AE235A00E3613A /* NewCaseField.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2266,15 +2433,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FE518DD5218135DD0087210E /* NewTextCaseFieldTests.swift in Sources */, FEAE9B2A1FEC502000B52CA9 /* NewProjectTests.swift in Sources */, FEAE9AE01FEC501100B52CA9 /* CustomFieldTypeTests.swift in Sources */, FEAE9B0E1FEC501800B52CA9 /* FilterTests.swift in Sources */, FEAE9B261FEC502000B52CA9 /* NewMilestoneTests.swift in Sources */, FEAE9AD91FEC500900B52CA9 /* AsyncOperationTests.swift in Sources */, + FE4F8C41218120D600F4628B /* NewURLCaseFieldTests.swift in Sources */, FEAE9B251FEC502000B52CA9 /* NewConfigurationTests.swift in Sources */, FEAE9AB41FEC4FF800B52CA9 /* JSONDataProvider.swift in Sources */, FEAE9B2D1FEC502000B52CA9 /* NewCaseResults.ResultTests.swift in Sources */, + FE518DE82181432E0087210E /* CodableTests.swift in Sources */, FEAE9B001FEC501400B52CA9 /* PlanTests.swift in Sources */, + FE4F8C3E21811FC300F4628B /* NewStringCaseFieldTests.swift in Sources */, + FE4F8C44218121A200F4628B /* NewUserCaseFieldTests.swift in Sources */, FEAE9B071FEC501400B52CA9 /* SectionTests.swift in Sources */, FEAE9B031FEC501400B52CA9 /* ProjectTests.swift in Sources */, FEAE9AE11FEC501100B52CA9 /* Project.SuiteModeTests.swift in Sources */, @@ -2287,20 +2459,25 @@ FEAE9AD21FEC500200B52CA9 /* JSONDictionaryContainerTests.swift in Sources */, FEAE9AC41FEC4FFF00B52CA9 /* AddRequestJSONTests.swift in Sources */, FEAE9AC71FEC4FFF00B52CA9 /* JSONDeserializingTests.swift in Sources */, + FE518DE421813E400087210E /* AssertCodable.swift in Sources */, + FE12A9CE21822A580010D407 /* ObjectAPITests.swift in Sources */, + FE4F8C3821811E9D00F4628B /* NewIntegerCaseFieldTests.swift in Sources */, FEAE9AB61FEC4FF800B52CA9 /* ValidatableObjectProvider.swift in Sources */, FEAE9B291FEC502000B52CA9 /* NewPlan.Entry.RunTests.swift in Sources */, + FE4F8C3B21811FA200F4628B /* NewMilestoneCaseFieldTests.swift in Sources */, FEAE9B061FEC501400B52CA9 /* RunTests.swift in Sources */, FEAE9B0B1FEC501400B52CA9 /* TestTests.swift in Sources */, + FE4F8C2021811CB300F4628B /* NewDateCaseFieldTests.swift in Sources */, FEAE9AD71FEC500700B52CA9 /* Array+RandomTests.swift in Sources */, FEAE9AF91FEC501400B52CA9 /* Config.ContextTests.swift in Sources */, FEAE9A9C1FEC4FF100B52CA9 /* TestCredentials.swift in Sources */, FEAE9AF81FEC501400B52CA9 /* ConfigTests.swift in Sources */, FEAE9AFA1FEC501400B52CA9 /* CaseTests.swift in Sources */, + FE4F8C472181221100F4628B /* NewDropdownCaseFieldTests.swift in Sources */, FEAE9AC81FEC4FFF00B52CA9 /* JSONSerializingTests.swift in Sources */, FEAE9AD11FEC500200B52CA9 /* ErrorContainerTests.swift in Sources */, FEAE9B281FEC502000B52CA9 /* NewPlan.EntryTests.swift in Sources */, FEAE9AA71FEC4FF500B52CA9 /* AssertProperties.swift in Sources */, - FE4668A220C7467A003BA033 /* ObjectAPITests.swift in Sources */, FEAE9B301FEC502000B52CA9 /* NewRunTests.swift in Sources */, FEAE9B231FEC502000B52CA9 /* NewCaseTests.swift in Sources */, FEAE9AC61FEC4FFF00B52CA9 /* InitTests.swift in Sources */, @@ -2319,6 +2496,7 @@ FEAE9B041FEC501400B52CA9 /* ResultTests.swift in Sources */, FEAE9AC51FEC4FFF00B52CA9 /* EquatableTests.swift in Sources */, FEAE9B321FEC502000B52CA9 /* NewSuiteTests.swift in Sources */, + FE518DD2218134980087210E /* NewStepsCaseFieldTests.swift in Sources */, FEAE9AFB1FEC501400B52CA9 /* CaseFieldTests.swift in Sources */, FEAE9AA51FEC4FF500B52CA9 /* AssertJSONSerializing.swift in Sources */, FEAE9AA81FEC4FF500B52CA9 /* AssertUpdateRequestJSON.swift in Sources */, @@ -2340,7 +2518,9 @@ FEAE9B0A1FEC501400B52CA9 /* TemplateTests.swift in Sources */, FEAE9AB51FEC4FF800B52CA9 /* ObjectProvider.swift in Sources */, FEAE9B2C1FEC502000B52CA9 /* NewCaseResultsTests.swift in Sources */, + FE4F8BFC218112F600F4628B /* NewCheckboxCaseFieldTests.swift in Sources */, FEAE9ACB1FEC4FFF00B52CA9 /* ValidatableTests.swift in Sources */, + FE518DCF218132D40087210E /* NewMultiselectCaseFieldTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-iOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-iOS.xcscheme index e1ee477..75e6cb9 100644 --- a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-iOS.xcscheme +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-iOS.xcscheme @@ -10,9 +10,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -23,6 +22,41 @@ BlueprintName = "QuizTrainTests-iOS" ReferencedContainer = "container:QuizTrain.xcodeproj"> + + + + + + + + + + + + + + + + + + + + + + + + @@ -32,7 +66,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-macOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-macOS.xcscheme index 5e4b8d9..73a764d 100644 --- a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-macOS.xcscheme +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-macOS.xcscheme @@ -10,9 +10,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -23,6 +22,41 @@ BlueprintName = "QuizTrainTests-macOS" ReferencedContainer = "container:QuizTrain.xcodeproj"> + + + + + + + + + + + + + + + + + + + + + + + + @@ -32,7 +66,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-tvOS.xcscheme b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-tvOS.xcscheme index f6e6896..adaf2b7 100644 --- a/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-tvOS.xcscheme +++ b/QuizTrain.xcodeproj/xcshareddata/xcschemes/QuizTrainTests-tvOS.xcscheme @@ -10,9 +10,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -23,6 +22,41 @@ BlueprintName = "QuizTrainTests-tvOS" ReferencedContainer = "container:QuizTrain.xcodeproj"> + + + + + + + + + + + + + + + + + + + + + + + + @@ -32,7 +66,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/QuizTrain/Info.plist b/QuizTrain/Info.plist index 6db6fe4..c6732b7 100644 --- a/QuizTrain/Info.plist +++ b/QuizTrain/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.1.1 + 1.2.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/QuizTrain/Models/CaseField.swift b/QuizTrain/Models/CaseField.swift index 53062f2..aa453a4 100644 --- a/QuizTrain/Models/CaseField.swift +++ b/QuizTrain/Models/CaseField.swift @@ -49,8 +49,29 @@ extension CaseField: JSONDeserializable { init?(json: JSONDictionary) { - guard let configsJson = json[JSONKeys.configs.rawValue] as? [JSONDictionary], - let configs: [Config] = CaseField.deserialized(configsJson), + /* + The formatting of JSONKeys.configs returned by the API differs: + + - Get requests return a [JSONDictionary]. + - Add requests return a String which can be converted into a + [JSONDictionary]. + */ + let configsJson: [JSONDictionary] + if let jsonArray = json[JSONKeys.configs.rawValue] as? [JSONDictionary] { + configsJson = jsonArray + } else if let jsonString = json[JSONKeys.configs.rawValue] as? String { + do { + guard let data = jsonString.data(using: .utf8) else { return nil } + guard let jsonObject = try JSONSerialization.jsonObject(with: data) as? [JSONDictionary] else { return nil } + configsJson = jsonObject + } catch { + return nil + } + } else { + return nil + } + + guard let configs: [Config] = CaseField.deserialized(configsJson), let displayOrder = json[JSONKeys.displayOrder.rawValue] as? Int, let id = json[JSONKeys.id.rawValue] as? Id, let includeAll = json[JSONKeys.includeAll.rawValue] as? Bool, diff --git a/QuizTrain/Network/API.swift b/QuizTrain/Network/API.swift index 8cceb67..4e35fee 100644 --- a/QuizTrain/Network/API.swift +++ b/QuizTrain/Network/API.swift @@ -217,6 +217,13 @@ final public class API { // MARK: - Case Fields + /* + http://docs.gurock.com/testrail-api2/reference-cases-fields#add_case_field + */ + @discardableResult public func addCaseField(data: Data, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask { + return post("add_case_field", data: data, completionHandler: completionHandler) + } + /* http://docs.gurock.com/testrail-api2/reference-cases-fields#get_case_fields */ diff --git a/QuizTrain/Network/Models/Add/NewCaseField/NewCaseField.swift b/QuizTrain/Network/Models/Add/NewCaseField/NewCaseField.swift new file mode 100644 index 0000000..2fa39c8 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewCaseField/NewCaseField.swift @@ -0,0 +1,424 @@ +// MARK: - Models + +public struct NewCaseField: Hashable { + + public var configs: [DataType.Configs] { return data.configs } + public var data: DataType + public var description: String? + public var label: String + public var name: String + public var includeAll: Bool + public var templateIds: [Int] + public var type: NewCaseFieldType { return data.type } + + fileprivate init(data: DataType, description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = []) { + self.data = data + self.description = description + self.includeAll = includeAll + self.label = label + self.name = name + self.templateIds = templateIds + } + +} + +// MARK: - Initializers + +extension NewCaseField where DataType == NewCaseFieldStringData { + + /** + Initializes a new string CaseField. Pass this to ObjectAPI's addCaseField() + to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - defaultValue: Optional default value of this case field. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, defaultValue: String? = nil) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigStringOptions(isRequired: isRequired, defaultValue: defaultValue) + let config = NewCaseFieldStringConfig(context: context, options: options) + let data = NewCaseFieldStringData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldIntegerData { + + /** + Initializes a new integer CaseField. Pass this to ObjectAPI's + addCaseField() to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - defaultValue: Optional default value of this case field. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, defaultValue: Int? = nil) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigIntegerOptions(isRequired: isRequired, defaultValue: defaultValue) + let config = NewCaseFieldIntegerConfig(context: context, options: options) + let data = NewCaseFieldIntegerData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldTextData { + + /** + Initializes a new text CaseField. Pass this to ObjectAPI's addCaseField() + to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - defaultValue: Optional default value of this case field. + - format: Text format allowed for this case field. Pass either .plain or .markdown. + - rows: Maximum number of lines of text allowed for this case field. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, defaultValue: String? = nil, format: NewCaseFieldConfigTextOptions.Format, rows: NewCaseFieldConfigTextOptions.Rows) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigTextOptions(isRequired: isRequired, defaultValue: defaultValue, format: format, rows: rows) + let config = NewCaseFieldTextConfig(context: context, options: options) + let data = NewCaseFieldTextData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldURLData { + + /** + Initializes a new URL CaseField. Pass this to ObjectAPI's addCaseField() to + create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - defaultValue: Optional default value of this case field. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, defaultValue: URL? = nil) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigURLOptions(isRequired: isRequired, defaultValue: defaultValue) + let config = NewCaseFieldURLConfig(context: context, options: options) + let data = NewCaseFieldURLData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldCheckboxData { + + /** + Initializes a new checkbox CaseField. Pass this to ObjectAPI's + addCaseField() to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - defaultValue: Required default value of this case field. True means checked, false means unchecked. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, defaultValue: Bool) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigCheckboxOptions(isRequired: isRequired, defaultValue: defaultValue) + let config = NewCaseFieldCheckboxConfig(context: context, options: options) + let data = NewCaseFieldCheckboxData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldDropdownData { + + /** + Initializes a new dropdown CaseField. Pass this to ObjectAPI's + addCaseField() to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - items: Required list of 1+ selectable items which will appear in the dropdown. This will throw an error if you pass an empty list. + - defaultValue: Required index of default item to use from |items|. This will throw an error if you pass an invalid index. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, items: [NewCaseFieldConfigDropdownOptions.Item], defaultValue: Int) throws { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = try NewCaseFieldConfigDropdownOptions(items: items, isRequired: isRequired, defaultValue: defaultValue) // Throws if `defaultValue` is out of bounds of `items` or if `items` is empty. + let config = NewCaseFieldDropdownConfig(context: context, options: options) + let data = NewCaseFieldDropdownData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldUserData { + + /** + Initializes a new user CaseField. Pass this to ObjectAPI's addCaseField() + to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - defaultValue: Optional default value of this case field. If passed this must match a valid User.Id. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, defaultValue: Int? = nil) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigUserOptions(isRequired: isRequired, defaultValue: defaultValue) + let config = NewCaseFieldUserConfig(context: context, options: options) + let data = NewCaseFieldUserData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldDateData { + + /** + Initializes a new date CaseField. Pass this to ObjectAPI's addCaseField() + to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigDateOptions(isRequired: isRequired) + let config = NewCaseFieldDateConfig(context: context, options: options) + let data = NewCaseFieldDateData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldMilestoneData { + + /** + Initializes a new milestone CaseField. Pass this to ObjectAPI's + addCaseField() to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigMilestoneOptions(isRequired: isRequired) + let config = NewCaseFieldMilestoneConfig(context: context, options: options) + let data = NewCaseFieldMilestoneData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldStepsData { + + /** + Initializes a new steps CaseField. Pass this to ObjectAPI's addCaseField() + to create it on your TestRail instance. + + TestRail limits you to no more than on steps CaseField across your entire + instance. If you attempt to create a second a 400 error will be returned by + the API. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - format: Text format allowed to be used in each step for this case field. Pass either .plain or .markdown. + - hasExpected: Pass true to add a separate expected result field for each step. + - rows: Maximum number of steps allowed for this case field. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, format: NewCaseFieldConfigStepsOptions.Format, hasExpected: Bool, rows: NewCaseFieldConfigStepsOptions.Rows) { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = NewCaseFieldConfigStepsOptions(isRequired: isRequired, format: format, hasExpected: hasExpected, rows: rows) + let config = NewCaseFieldStepsConfig(context: context, options: options) + let data = NewCaseFieldStepsData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +extension NewCaseField where DataType == NewCaseFieldMultiselectData { + + /** + Initializes a new multiselect CaseField. Pass this to ObjectAPI's + addCaseField() to create it on your TestRail instance. + + - parameters: + - description: Optional description. + - label: Human readable name of this case field. + - name: System name of case field. Lowercase letters a...z and _ only. Do not prefix with "custom_". Must be unique across your TestRail instance. + - includeAll: True applies this case field to all templates. Pass false to specify specific templateIds. + - templateIds: Applies this case field only to these templates. You must set includeAll to false if you specify specific templateIds. + - isGlobal: True applies to all projects. Pass false to specify specific projectIds. + - projectIds: Applies this case field only to these projects. You must set isGlobal to false if you specify specific projectIds. + - isRequired: True means this case field must be filled out when creating new cases to which this case field applies. + - items: Required list of 1+ selectable items which will appear as multiselect options. This will throw an error if you pass an empty list. + */ + public init(description: String? = nil, label: String, name: String, includeAll: Bool, templateIds: [Int] = [], isGlobal: Bool, projectIds: [Int] = [], isRequired: Bool, items: [NewCaseFieldConfigMultiselectOptions.Item]) throws { + let context = NewCaseFieldConfigContext(isGlobal: isGlobal, projectIds: projectIds) + let options = try NewCaseFieldConfigMultiselectOptions(items: items, isRequired: isRequired) // Throws if `items` is empty. + let config = NewCaseFieldMultiselectConfig(context: context, options: options) + let data = NewCaseFieldMultiselectData(configs: [config]) + self.init(data: data, description: description, label: label, name: name, includeAll: includeAll, templateIds: templateIds) + } + +} + +// MARK: - Codable + +extension NewCaseField: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case configs + case description + case includeAll = "include_all" + case label + case name + case templateIds = "template_ids" + case type + } + + // swiftlint:disable:next cyclomatic_complexity + public init(from decoder: Decoder) throws { + + let values = try decoder.container(keyedBy: CodingKeys.self) + + description = try values.decodeIfPresent(String.self, forKey: .description) + includeAll = try values.decode(Bool.self, forKey: .includeAll) + label = try values.decode(String.self, forKey: .label) + name = try values.decode(String.self, forKey: .name) + templateIds = try values.decode([Int].self, forKey: .templateIds) + + let type = try values.decode(NewCaseFieldType.self, forKey: .type) + switch type { + case .string: + let configs = try values.decode([NewCaseFieldStringConfig].self, forKey: .configs) + data = NewCaseFieldStringData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .integer: + let configs = try values.decode([NewCaseFieldIntegerConfig].self, forKey: .configs) + data = NewCaseFieldIntegerData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .text: + let configs = try values.decode([NewCaseFieldTextConfig].self, forKey: .configs) + data = NewCaseFieldTextData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .url: + let configs = try values.decode([NewCaseFieldURLConfig].self, forKey: .configs) + data = NewCaseFieldURLData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .checkbox: + let configs = try values.decode([NewCaseFieldCheckboxConfig].self, forKey: .configs) + data = NewCaseFieldCheckboxData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .dropdown: + let configs = try values.decode([NewCaseFieldDropdownConfig].self, forKey: .configs) + data = NewCaseFieldDropdownData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .user: + let configs = try values.decode([NewCaseFieldUserConfig].self, forKey: .configs) + data = NewCaseFieldUserData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .date: + let configs = try values.decode([NewCaseFieldDateConfig].self, forKey: .configs) + data = NewCaseFieldDateData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .milestone: + let configs = try values.decode([NewCaseFieldMilestoneConfig].self, forKey: .configs) + data = NewCaseFieldMilestoneData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .steps: + let configs = try values.decode([NewCaseFieldStepsConfig].self, forKey: .configs) + data = NewCaseFieldStepsData(configs: configs) as! DataType // swiftlint:disable:this force_cast + case .multiselect: + let configs = try values.decode([NewCaseFieldMultiselectConfig].self, forKey: .configs) + data = NewCaseFieldMultiselectData(configs: configs) as! DataType // swiftlint:disable:this force_cast + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(configs, forKey: .configs) + try container.encodeIfPresent(description, forKey: .description) + try container.encode(includeAll, forKey: .includeAll) + try container.encode(label, forKey: .label) + try container.encode(name, forKey: .name) + try container.encode(templateIds, forKey: .templateIds) + try container.encode(type, forKey: .type) + } + +} + +// MARK: - AddRequestJSONKeys | JSONSerializable | AddRequestJSON + +extension NewCaseField: AddRequestJSONKeys { + var addRequestJSONKeys: [JSONKey] { + return CodingKeys.allCases.map { $0.rawValue } + } +} + +extension NewCaseField: JSONSerializable { + func serialized() -> JSONDictionary { + let encoder = JSONEncoder() + let data = try! encoder.encode(self) // swiftlint:disable:this force_try + let dict = try! JSONSerialization.jsonObject(with: data) as! JSONDictionary // swiftlint:disable:this force_try force_cast + return dict + } +} + +extension NewCaseField: AddRequestJSON { } diff --git a/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfig.swift b/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfig.swift new file mode 100644 index 0000000..3581030 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfig.swift @@ -0,0 +1,64 @@ +// MARK: - Protocols + +public protocol NewCaseFieldConfig: Codable, Hashable { + associatedtype Options + var context: NewCaseFieldConfigContext { get } + var options: Options { get } +} + +// MARK: - Models + +public struct NewCaseFieldStringConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigStringOptions +} + +public struct NewCaseFieldIntegerConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigIntegerOptions +} + +public struct NewCaseFieldTextConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigTextOptions +} + +public struct NewCaseFieldURLConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigURLOptions +} + +public struct NewCaseFieldCheckboxConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigCheckboxOptions +} + +public struct NewCaseFieldDropdownConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigDropdownOptions +} + +public struct NewCaseFieldUserConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigUserOptions +} + +public struct NewCaseFieldDateConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigDateOptions +} + +public struct NewCaseFieldMilestoneConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigMilestoneOptions +} + +public struct NewCaseFieldStepsConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigStepsOptions +} + +public struct NewCaseFieldMultiselectConfig: NewCaseFieldConfig { + public var context: NewCaseFieldConfigContext + public var options: NewCaseFieldConfigMultiselectOptions +} diff --git a/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfigContext.swift b/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfigContext.swift new file mode 100644 index 0000000..027a868 --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfigContext.swift @@ -0,0 +1,15 @@ +// MARK: - Models + +public struct NewCaseFieldConfigContext: Hashable { + public var isGlobal: Bool + public var projectIds: [Int] +} + +// MARK: - Codable + +extension NewCaseFieldConfigContext: Codable { + enum CodingKeys: String, CodingKey { + case isGlobal = "is_global" + case projectIds = "project_ids" + } +} diff --git a/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfigOptions.swift b/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfigOptions.swift new file mode 100644 index 0000000..acecc7e --- /dev/null +++ b/QuizTrain/Network/Models/Add/NewCaseField/NewCaseFieldConfigOptions.swift @@ -0,0 +1,457 @@ +// MARK: - Models + +public struct NewCaseFieldConfigStringOptions: Hashable { + public var isRequired: Bool + public var defaultValue: String? +} + +public struct NewCaseFieldConfigIntegerOptions: Hashable { + public var isRequired: Bool + public var defaultValue: Int? +} + +public struct NewCaseFieldConfigTextOptions: Hashable { + + public var isRequired: Bool + public var defaultValue: String? + public var format: Format + public var rows: Rows + + public enum Format: String, Codable, Hashable { + case plain + case markdown + } + + public enum Rows: String, Codable, Hashable { + case unspecified = "" + case three = "3" + case four = "4" + case five = "5" + case six = "6" + case seven = "7" + case eight = "8" + case nine = "9" + case ten = "10" + } +} + +public struct NewCaseFieldConfigURLOptions: Hashable { + public var isRequired: Bool + public var defaultValue: URL? +} + +public struct NewCaseFieldConfigCheckboxOptions: Hashable { + public var isRequired: Bool + public var defaultValue: Bool +} + +public struct NewCaseFieldConfigDropdownOptions: Hashable { + + public typealias Item = String + + private var _items: [Item] + private var _defaultValue: Int + + public var items: [Item] { return _items } + public var isRequired: Bool + public var defaultValue: Int { return _defaultValue } // A valid index in .items. + + public enum DefaultValueError: Error { + case defaultValueOutOfRange + case itemsCannotBeEmpty + } + + public init(items: [Item], isRequired: Bool, defaultValue: Int) throws { + self.isRequired = isRequired + _items = []; _defaultValue = 0 + try set(items: items, withDefaultValue: defaultValue) + } + + /*** + Sets .items and .defaultValue properties. Enforces that |items| is not + empty and |defaultValue| is a valid index in |items|. + */ + public mutating func set(items: [Item], withDefaultValue defaultValue: Int) throws { + guard !items.isEmpty else { + throw DefaultValueError.itemsCannotBeEmpty + } + guard items.count > defaultValue else { + throw DefaultValueError.defaultValueOutOfRange + } + _items = items + _defaultValue = defaultValue + } +} + +public struct NewCaseFieldConfigUserOptions: Hashable { + public var isRequired: Bool + public var defaultValue: Int? // User.Id +} + +public struct NewCaseFieldConfigDateOptions: Hashable { + public var isRequired: Bool +} + +public struct NewCaseFieldConfigMilestoneOptions: Hashable { + public var isRequired: Bool +} + +public struct NewCaseFieldConfigStepsOptions: Hashable { + + public var isRequired: Bool + public var format: Format + public var hasExpected: Bool // true means "Use a separate Expected Result field for each step" + public var rows: Rows + + public enum Format: String, Codable, Hashable { + case plain + case markdown + } + + public enum Rows: String, Codable, Hashable { + case unspecified = "" + case three = "3" + case four = "4" + case five = "5" + case six = "6" + case seven = "7" + case eight = "8" + case nine = "9" + case ten = "10" + } +} + +public struct NewCaseFieldConfigMultiselectOptions: Hashable { + + public typealias Item = String + private var _items: [Item] + public var items: [Item] { return _items } + public var isRequired: Bool + + public init(items: [Item], isRequired: Bool) throws { + self.isRequired = isRequired + _items = [] + try set(items: items) + } + + public enum DefaultValueError: Error { + case itemsCannotBeEmpty + } + + /*** + Sets .items property enforcing it's not empty. + */ + public mutating func set(items: [Item]) throws { + guard !items.isEmpty else { + throw DefaultValueError.itemsCannotBeEmpty + } + _items = items + } +} + +// MARK: - Codable + +extension NewCaseFieldConfigStringOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + case defaultValue = "default_value" + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + isRequired = try values.decode(Bool.self, forKey: .isRequired) + let defaultValue = try values.decode(String.self, forKey: .defaultValue) + self.defaultValue = defaultValue.isEmpty ? nil : defaultValue + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + try container.encode(defaultValue ?? "", forKey: .defaultValue) + } + +} + +extension NewCaseFieldConfigIntegerOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + case defaultValue = "default_value" + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + isRequired = try values.decode(Bool.self, forKey: .isRequired) + let defaultValue = try values.decode(String.self, forKey: .defaultValue) + self.defaultValue = defaultValue.isEmpty ? nil : Int(defaultValue) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + let defaultValue: String + if let integer = self.defaultValue { + defaultValue = String(integer) + } else { + defaultValue = "" + } + try container.encode(defaultValue, forKey: .defaultValue) + } + +} + +extension NewCaseFieldConfigTextOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + case defaultValue = "default_value" + case format + case rows + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + isRequired = try values.decode(Bool.self, forKey: .isRequired) + let defaultValue = try values.decode(String.self, forKey: .defaultValue) + self.defaultValue = defaultValue.isEmpty ? nil : defaultValue + format = try values.decode(Format.self, forKey: .format) + rows = try values.decode(Rows.self, forKey: .rows) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + try container.encode(defaultValue ?? "", forKey: .defaultValue) + try container.encode(format, forKey: .format) + try container.encode(rows, forKey: .rows) + } + +} + +extension NewCaseFieldConfigURLOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + case defaultValue = "default_value" + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + isRequired = try values.decode(Bool.self, forKey: .isRequired) + let defaultValue = try values.decode(String.self, forKey: .defaultValue) + self.defaultValue = URL(string: defaultValue) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + let defaultValue: String + if let url = self.defaultValue { + defaultValue = url.absoluteString + } else { + defaultValue = "" + } + try container.encode(defaultValue, forKey: .defaultValue) + } + +} + +extension NewCaseFieldConfigCheckboxOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + case defaultValue = "default_value" + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + isRequired = try values.decode(Bool.self, forKey: .isRequired) + let defaultValue = try values.decode(String.self, forKey: .defaultValue) + switch defaultValue.lowercased() { + case "0", "false": + self.defaultValue = false + case "1", "true": + self.defaultValue = true + default: + throw DecodingError.dataCorruptedError(forKey: .defaultValue, in: values, debugDescription: "Invalid value: \(defaultValue)") + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + try container.encode(String(defaultValue), forKey: .defaultValue) + } + +} + +extension NewCaseFieldConfigDropdownOptions: Codable, ItemsConverter { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case items + case isRequired = "is_required" + case defaultValue = "default_value" + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + let isRequired = try values.decode(Bool.self, forKey: .isRequired) + let itemsString = try values.decode(String.self, forKey: .items) + guard let items = NewCaseFieldConfigDropdownOptions.items(from: itemsString) else { + throw DecodingError.dataCorruptedError(forKey: .items, in: values, debugDescription: "Invalid value: \(itemsString)") + } + let defaultValueString = try values.decode(String.self, forKey: .defaultValue) + guard var defaultValue = Int(defaultValueString) else { + throw DecodingError.dataCorruptedError(forKey: .defaultValue, in: values, debugDescription: "Invalid value: \(defaultValueString)") + } + defaultValue -= 1 // 1 indexed to 0 indexed + try self.init(items: items, isRequired: isRequired, defaultValue: defaultValue) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + try container.encode(NewCaseFieldConfigDropdownOptions.string(from: items), forKey: .items) + let defaultValue = String(self.defaultValue + 1) // 0 indexed to 1 indexed. + try container.encode(defaultValue, forKey: .defaultValue) + } + +} + +extension NewCaseFieldConfigUserOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + case defaultValue = "default_value" + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + isRequired = try values.decode(Bool.self, forKey: .isRequired) + let defaultValue = try values.decode(String.self, forKey: .defaultValue) + self.defaultValue = defaultValue.isEmpty ? nil : Int(defaultValue) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + let defaultValue: String + if let integer = self.defaultValue { + defaultValue = String(integer) + } else { + defaultValue = "" + } + try container.encode(defaultValue, forKey: .defaultValue) + } + +} + +extension NewCaseFieldConfigDateOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + } + +} + +extension NewCaseFieldConfigMilestoneOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + } + +} + +extension NewCaseFieldConfigStepsOptions: Codable { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case isRequired = "is_required" + case format + case hasExpected = "has_expected" + case rows + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + isRequired = try values.decode(Bool.self, forKey: .isRequired) + format = try values.decode(Format.self, forKey: .format) + hasExpected = try values.decode(Bool.self, forKey: .hasExpected) + rows = try values.decode(Rows.self, forKey: .rows) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + try container.encode(hasExpected, forKey: .hasExpected) + try container.encode(format, forKey: .format) + try container.encode(rows, forKey: .rows) + } + +} + +extension NewCaseFieldConfigMultiselectOptions: Codable, ItemsConverter { + + fileprivate enum CodingKeys: String, CodingKey, CaseIterable { + case items + case isRequired = "is_required" + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + let isRequired = try values.decode(Bool.self, forKey: .isRequired) + let itemsString = try values.decode(String.self, forKey: .items) + guard let items = NewCaseFieldConfigMultiselectOptions.items(from: itemsString) else { + throw DecodingError.dataCorruptedError(forKey: .items, in: values, debugDescription: "Invalid value: \(itemsString)") + } + try self.init(items: items, isRequired: isRequired) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isRequired, forKey: .isRequired) + try container.encode(NewCaseFieldConfigMultiselectOptions.string(from: items), forKey: .items) + } + +} + +// MARK: - Items + +protocol ItemsConverter { + associatedtype Item + static func string(from items: [Item]) -> String + static func items(from string: String) -> [Item]? +} + +extension ItemsConverter where Self.Item == String { + + /// ["One", "Two", "Three"] ---> "1, One\n2, Two\n3, Three" + static func string(from items: [Item]) -> String { + var itemsString = "" + for i in 0.. ["One", "Two", "Three"] + static func items(from string: String) -> [Item]? { + let stringSplit = string.split(separator: "\n") + var items = [Item]() + for i in 0.. Bool { + return rhs == lhs + } + + // swiftlint:disable:next cyclomatic_complexity + static func==(lhs: NewCaseFieldType, rhs: CustomFieldType) -> Bool { + switch lhs { + case .string: + return rhs == CustomFieldType.string + case .integer: + return rhs == CustomFieldType.integer + case .text: + return rhs == CustomFieldType.text + case .url: + return rhs == CustomFieldType.url + case .checkbox: + return rhs == CustomFieldType.checkbox + case .dropdown: + return rhs == CustomFieldType.dropdown + case .user: + return rhs == CustomFieldType.user + case .date: + return rhs == CustomFieldType.date + case .milestone: + return rhs == CustomFieldType.milestone + case .steps: + return rhs == CustomFieldType.steps + case .multiselect: + return rhs == CustomFieldType.multiSelect + } + } + +} diff --git a/QuizTrain/Network/ObjectAPI.swift b/QuizTrain/Network/ObjectAPI.swift index eb6fcfb..782ea8e 100644 --- a/QuizTrain/Network/ObjectAPI.swift +++ b/QuizTrain/Network/ObjectAPI.swift @@ -152,7 +152,7 @@ extension ObjectAPI { private typealias RequestOutcome = Outcome - /* + /** Converts an API.RequestOutcome into a more strongly-defined RequestOutcome. */ private static func requestOutcome(from apiRequestOutcome: API.RequestOutcome) -> RequestOutcome { @@ -191,7 +191,7 @@ extension ObjectAPI { private typealias JSONOutcome = Outcome - /* + /** Attempts to extract JSON from `apiRequestOutcome` and return it. If extraction fails a failed JSONOutcome is returned. */ @@ -227,7 +227,7 @@ extension ObjectAPI { private typealias ObjectOutcome = Outcome private typealias ObjectsOutcome = Outcome<[ObjectType], DataRequestError> - /* + /** Attempts to deserialize and return an object of ObjectType from `apiRequestOutcome`. If deserializing fails a failed ObjectOutcome is returned. @@ -255,7 +255,7 @@ extension ObjectAPI { return .succeeded(object) } - /* + /** Attempts to deserialize and return 0+ objects of ObjectType from `apiRequestOutcome`. If deserializing fails a failed ObjectsOutcome is returned. @@ -285,7 +285,7 @@ extension ObjectAPI { // MARK: Rate Limit - /* + /** Helper for processing 429 Too Many Requests errors. */ private struct RateLimitReached { @@ -303,7 +303,7 @@ extension ObjectAPI { // MARK: Processing - /* + /** These methods handle processing an API.RequestOutcome calling one of the handler closures when complete. Swift inference auto-detects which process(...) method to call. @@ -320,7 +320,7 @@ extension ObjectAPI { In all other cases the `completionHandler` is called when complete. */ - /* + /** Process API.RequestOutcome's to delete an object. - Outcome.succeeded receives an API.DataResponse. @@ -349,7 +349,7 @@ extension ObjectAPI { } } - /* + /** Process API.RequestOutcome's to get a single object. - Outcome.succeeded receives an object deserialized from @@ -379,7 +379,7 @@ extension ObjectAPI { } } - /* + /** Process API.RequestOutcome's to get multiple objects. - Outcome.succeeded receives 0+ object(s) deserialized from @@ -409,7 +409,7 @@ extension ObjectAPI { } } - /* + /** Process API.RequestOutcome's to add or update an object returning a new added/updated object if successful. @@ -444,7 +444,7 @@ extension ObjectAPI { } } - /* + /** Process API.RequestOutcome's to add or update multiple objects returning an array of the added/updated objects if successful. @@ -514,7 +514,7 @@ extension ObjectAPI { return .succeeded(data) } - /* + /** Combines JSON from all `objects` and returns Data from it. Any overlapping JSON keys are overriden by the last-most object in `objects`. */ @@ -544,7 +544,7 @@ extension ObjectAPI { // MARK: - ConfigurationGroup - /* + /** Queries all Project's matching |projectIds| asynchronously to get their ConfigurationGroups. Passes a dictionary of outcomes to the completionHandler mapping projectIds to each outcome when complete: @@ -601,7 +601,7 @@ extension ObjectAPI { // MARK: - Project - /* + /** Asynchronously GETs each project in |projectIds|. Passes a dictionary of outcomes to the completionHandler mapping projectIds to each outcome when complete: @@ -662,7 +662,7 @@ extension ObjectAPI { // MARK: - Template - /* + /** Queries all Project's matching |projectIds| asynchronously to get their Templates. Passes a dictionary of outcomes to the completionHandler mapping projectIds to each outcome when complete: @@ -725,15 +725,15 @@ extension ObjectAPI { // MARK: - Case - /* - http://docs.gurock.com/testrail-api2/reference-cases#add_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases#add_case) */ public func addCase(_ newCase: NewCase, to section: Section, completionHandler: @escaping (Outcome) -> Void) { addCase(newCase, toSectionWithId: section.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-cases#add_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases#add_case) */ public func addCase(_ newCase: NewCase, toSectionWithId sectionId: Section.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -756,15 +756,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-cases#delete_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases#delete_case) */ public func deleteCase(_ case: Case, completionHandler: @escaping (Outcome) -> Void) { deleteCase(`case`.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-cases#delete_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases#delete_case) */ public func deleteCase(_ caseId: Case.Id, completionHandler: @escaping (Outcome) -> Void) { api.deleteCase(caseId: caseId) { [weak self] (apiRequestOutcome) in @@ -781,8 +781,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-cases#get_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases#get_case) */ public func getCase(_ caseId: Case.Id, completionHandler: @escaping (Outcome) -> Void) { api.getCase(caseId: caseId) { [weak self] (apiRequestOutcome) in @@ -794,8 +794,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-cases#get_cases + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases#get_cases) suite is required if the project is not running in single suite mode. */ @@ -803,8 +803,8 @@ extension ObjectAPI { getCases(inProjectWithId: project.id, inSuiteWithId: suite?.id, inSectionWithId: section?.id, filteredBy: filters, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-cases#get_cases + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases#get_cases) suiteId is required if the project is not running in single suite mode. */ @@ -818,8 +818,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-cases#update_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases#update_case) */ public func updateCase(_ case: Case, completionHandler: @escaping (Outcome) -> Void) { @@ -844,8 +844,32 @@ extension ObjectAPI { // MARK: - CaseField - /* - http://docs.gurock.com/testrail-api2/reference-cases-fields#get_case_fields + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases-fields#get_case_fields) + */ + public func addCaseField(_ newCaseField: NewCaseField, completionHandler: @escaping (Outcome) -> Void) { + + let dataOutcome = self.data(from: newCaseField) + let data: Data + switch dataOutcome { + case .failed(let error): + completionHandler(.failed(.objectConversionError(error))) + return + case .succeeded(let aData): + data = aData + } + + api.addCaseField(data: data) { [weak self] (apiRequestOutcome) in + self?.process(apiRequestOutcome, retryHandler: { + self?.addCaseField(newCaseField, completionHandler: completionHandler) + }, completionHandler: { (processedOutcome) in + completionHandler(processedOutcome) + }) + } + } + + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases-fields#get_case_fields) */ public func getCaseFields(completionHandler: @escaping (Outcome<[CaseField], GetError>) -> Void) { api.getCaseFields { [weak self] (apiRequestOutcome) in @@ -859,8 +883,8 @@ extension ObjectAPI { // MARK: - CaseType - /* - http://docs.gurock.com/testrail-api2/reference-cases-types#get_case_types + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-cases-types#get_case_types) */ public func getCaseTypes(completionHandler: @escaping (Outcome<[CaseType], GetError>) -> Void) { api.getCaseTypes { [weak self] (apiRequestOutcome) in @@ -874,15 +898,15 @@ extension ObjectAPI { // MARK: - Configuration - /* - http://docs.gurock.com/testrail-api2/reference-configs#add_config + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#add_config) */ public func addConfiguration(_ newConfiguration: NewConfiguration, to configurationGroup: ConfigurationGroup, completionHandler: @escaping (Outcome) -> Void) { addConfiguration(newConfiguration, toConfigurationGroupWithId: configurationGroup.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-configs#add_config + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#add_config) */ public func addConfiguration(_ newConfiguration: NewConfiguration, toConfigurationGroupWithId configurationGroupId: ConfigurationGroup.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -905,15 +929,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-configs#delete_config + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#delete_config) */ public func deleteConfiguration(_ configuration: Configuration, completionHandler: @escaping (Outcome) -> Void) { deleteConfiguration(configuration.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-configs#delete_config + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#delete_config) */ public func deleteConfiguration(_ configurationId: Configuration.Id, completionHandler: @escaping (Outcome) -> Void) { api.deleteConfiguration(configurationId: configurationId) { [weak self] (apiRequestOutcome) in @@ -930,8 +954,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-configs#update_config + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#update_config) */ public func updateConfiguration(_ configuration: Configuration, completionHandler: @escaping (Outcome) -> Void) { @@ -956,15 +980,15 @@ extension ObjectAPI { // MARK: - ConfigurationGroup - /* - http://docs.gurock.com/testrail-api2/reference-configs#add_config_group + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#add_config_group) */ public func addConfigurationGroup(_ newConfigurationGroup: NewConfigurationGroup, to project: Project, completionHandler: @escaping (Outcome) -> Void) { addConfigurationGroup(newConfigurationGroup, toProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-configs#add_config_group + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#add_config_group) */ public func addConfigurationGroup(_ newConfigurationGroup: NewConfigurationGroup, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -987,15 +1011,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-configs#delete_config_group + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#delete_config_group) */ public func deleteConfigurationGroup(_ configurationGroup: ConfigurationGroup, completionHandler: @escaping (Outcome) -> Void) { deleteConfigurationGroup(configurationGroup.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-configs#delete_config_group + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#delete_config_group) */ public func deleteConfigurationGroup(_ configurationGroupId: ConfigurationGroup.Id, completionHandler: @escaping (Outcome) -> Void) { api.deleteConfigurationGroup(configurationGroupId: configurationGroupId) { [weak self] (apiRequestOutcome) in @@ -1012,7 +1036,7 @@ extension ObjectAPI { } } - /* + /** Gets all ConfigurationGroups from all Projects. - Returns a de-duped array of 0+ ConfigurationGroups from all Projects upon @@ -1064,15 +1088,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-configs#get_configs + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#get_configs) */ public func getConfigurationGroups(in project: Project, completionHandler: @escaping (Outcome<[ConfigurationGroup], GetError>) -> Void) { getConfigurationGroups(inProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-configs#get_configs + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#get_configs) */ public func getConfigurationGroups(inProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome<[ConfigurationGroup], GetError>) -> Void) { api.getConfigurations(projectId: projectId) { [weak self] (apiRequestOutcome) in @@ -1084,8 +1108,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-configs#update_config_group + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-configs#update_config_group) */ public func updateConfigurationGroup(_ configurationGroup: ConfigurationGroup, completionHandler: @escaping (Outcome) -> Void) { @@ -1110,15 +1134,15 @@ extension ObjectAPI { // MARK: - Milestone - /* - http://docs.gurock.com/testrail-api2/reference-milestones#add_milestone + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-milestones#add_milestone) */ public func addMilestone(_ newMilestone: NewMilestone, to project: Project, completionHandler: @escaping (Outcome) -> Void) { addMilestone(newMilestone, toProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-milestones#add_milestone + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-milestones#add_milestone) */ public func addMilestone(_ newMilestone: NewMilestone, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -1141,15 +1165,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-milestones#delete_milestone + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-milestones#delete_milestone) */ public func deleteMilestone(_ milestone: Milestone, completionHandler: @escaping (Outcome) -> Void) { deleteMilestone(milestone.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-milestones#delete_milestone + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-milestones#delete_milestone) */ public func deleteMilestone(_ milestoneId: Milestone.Id, completionHandler: @escaping (Outcome) -> Void) { api.deleteMilestone(milestoneId: milestoneId) { [weak self] (apiRequestOutcome) in @@ -1166,8 +1190,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-milestones#get_milestone + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-milestones#get_milestone) */ public func getMilestone(_ milestoneId: Milestone.Id, completionHandler: @escaping (Outcome) -> Void) { api.getMilestone(milestoneId: milestoneId) { [weak self] (apiRequestOutcome) in @@ -1179,8 +1203,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-milestones#get_milestones + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-milestones#get_milestones) NOTE: This API call does not include .milestones for the Milestone's. For that behavior use getMilestone(...). @@ -1189,8 +1213,8 @@ extension ObjectAPI { getMilestones(inProjectWithId: project.id, filteredBy: filters, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-milestones#get_milestones + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-milestones#get_milestones) NOTE: This API call does not include .milestones for the Milestone's. For that behavior use getMilestone(...). @@ -1205,8 +1229,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-milestones#update_milestone + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-milestones#update_milestone) */ public func updateMilestone(_ milestone: Milestone, completionHandler: @escaping (Outcome) -> Void) { @@ -1231,15 +1255,15 @@ extension ObjectAPI { // MARK: - Plan - /* - http://docs.gurock.com/testrail-api2/reference-plans#add_plan + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#add_plan) */ public func addPlan(_ newPlan: NewPlan, to project: Project, completionHandler: @escaping (Outcome) -> Void) { addPlan(newPlan, toProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-plans#add_plan + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#add_plan) */ public func addPlan(_ newPlan: NewPlan, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -1262,15 +1286,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-plans#close_plan + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#close_plan) */ public func closePlan(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { closePlan(plan.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-plans#close_plan + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#close_plan) */ public func closePlan(_ planId: Plan.Id, completionHandler: @escaping (Outcome) -> Void) { api.closePlan(planId: planId) { [weak self] (apiRequestOutcome) in @@ -1282,15 +1306,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-plans#delete_plan + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#delete_plan) */ public func deletePlan(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { deletePlan(plan.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-plans#delete_plan + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#delete_plan) */ public func deletePlan(_ planId: Plan.Id, completionHandler: @escaping (Outcome) -> Void) { api.deletePlan(planId: planId) { [weak self] (apiRequestOutcome) in @@ -1307,8 +1331,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-plans#get_plan + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#get_plan) */ public func getPlan(_ planId: Plan.Id, completionHandler: @escaping (Outcome) -> Void) { api.getPlan(planId: planId) { [weak self] (apiRequestOutcome) in @@ -1320,8 +1344,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-plans#get_plans + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#get_plans) This API call does not include .entries for the Plan's. For that behavior use getPlan(...). @@ -1330,8 +1354,8 @@ extension ObjectAPI { getPlans(inProjectWithId: project.id, filteredBy: filters, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-plans#get_plans + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#get_plans) This API call does not include .entries for the Plan's. For that behavior use getPlan(...). @@ -1346,8 +1370,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-plans#update_plan + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#update_plan) */ public func updatePlan(_ plan: Plan, completionHandler: @escaping (Outcome) -> Void) { @@ -1372,8 +1396,8 @@ extension ObjectAPI { // MARK: - Plan.Entry - /* - http://docs.gurock.com/testrail-api2/reference-plans#add_plan_entry + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#add_plan_entry) Upon success only the newly added run(s) are included in Plan.Entry.runs. Any other runs are not included. To get all runs call getPlan() after this @@ -1386,8 +1410,8 @@ extension ObjectAPI { addPlanEntry(newPlanEntry, toPlanWithId: plan.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-plans#add_plan_entry + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#add_plan_entry) Upon success only the newly added run(s) are included in Plan.Entry.runs. Any other runs are not included. To get all runs call getPlan() after this @@ -1417,8 +1441,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-plans#delete_plan_entry + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#delete_plan_entry) Upon success `plan`.entries will be stale; use getPlan() to get a fresh Plan. @@ -1427,8 +1451,8 @@ extension ObjectAPI { deletePlanEntry(planEntry.id, fromPlanWithId: plan.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-plans#delete_plan_entry + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#delete_plan_entry) Upon success Plan.entries for `planId` will be stale; use getPlan() to get a fresh Plan. @@ -1448,8 +1472,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-plans#update_plan_entry + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#update_plan_entry) Upon success this returns a new Plan.Entry including all of its test Run's. `plan`.entries will be stale; use getPlan() to get a fresh Plan. @@ -1463,8 +1487,8 @@ extension ObjectAPI { updatePlanEntry(planEntry, inPlanWithId: plan.id, with: planEntryRuns, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-plans#update_plan_entry + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-plans#update_plan_entry) Upon success this returns a new Plan.Entry including all of its test Run's. Plan.entries for `planId` will be stale; use getPlan() to get a fresh Plan. @@ -1503,8 +1527,8 @@ extension ObjectAPI { // MARK: - Priority - /* - http://docs.gurock.com/testrail-api2/reference-priorities#get_priorities + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-priorities#get_priorities) */ public func getPriorities(completionHandler: @escaping (Outcome<[Priority], GetError>) -> Void) { api.getPriorities { [weak self] (apiRequestOutcome) in @@ -1518,8 +1542,8 @@ extension ObjectAPI { // MARK: - Project - /* - http://docs.gurock.com/testrail-api2/reference-projects#add_project + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-projects#add_project) */ public func addProject(_ newProject: NewProject, completionHandler: @escaping (Outcome) -> Void) { @@ -1542,15 +1566,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-projects#delete_project + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-projects#delete_project) */ public func deleteProject(_ project: Project, completionHandler: @escaping (Outcome) -> Void) { deleteProject(project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-projects#delete_project + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-projects#delete_project) */ public func deleteProject(_ projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { api.deleteProject(projectId: projectId) { [weak self] (apiRequestOutcome) in @@ -1567,8 +1591,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-projects#get_project + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-projects#get_project) You must have at least Read-only access to the project otherwise a 403 error will be returned. @@ -1583,8 +1607,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-projects#get_projects + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-projects#get_projects) Returns all projects which you have at least Read-only access to. All other projects will be silently omitted. @@ -1603,8 +1627,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-projects#update_project + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-projects#update_project) */ public func updateProject(_ project: Project, completionHandler: @escaping (Outcome) -> Void) { @@ -1629,15 +1653,15 @@ extension ObjectAPI { // MARK: - Result - /* - http://docs.gurock.com/testrail-api2/reference-results#add_result + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#add_result) */ public func addResult(_ newResult: NewResult, to test: Test, completionHandler: @escaping (Outcome) -> Void) { addResult(newResult, toTestWithId: test.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-results#add_result + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#add_result) */ public func addResult(_ newResult: NewResult, toTestWithId testId: Test.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -1660,15 +1684,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-results#add_result_for_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#add_result_for_case) */ public func addResultForCase(_ newResult: NewResult, to run: Run, to case: Case, completionHandler: @escaping (Outcome) -> Void) { addResultForCase(newResult, toRunWithId: run.id, toCaseWithId: `case`.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-results#add_result_for_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#add_result_for_case) */ public func addResultForCase(_ newResult: NewResult, toRunWithId runId: Run.Id, toCaseWithId caseId: Case.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -1691,15 +1715,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-results#add_results + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#add_results) */ public func addResults(_ newTestResults: NewTestResults, to run: Run, completionHandler: @escaping (Outcome<[Result], AddError>) -> Void) { addResults(newTestResults, toRunWithId: run.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-results#add_results + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#add_results) */ public func addResults(_ newTestResults: NewTestResults, toRunWithId runId: Run.Id, completionHandler: @escaping (Outcome<[Result], AddError>) -> Void) { @@ -1722,15 +1746,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-results#add_results_for_cases + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#add_results_for_cases) */ public func addResultsForCases(_ newCaseResults: NewCaseResults, to run: Run, completionHandler: @escaping (Outcome<[Result], AddError>) -> Void) { addResultsForCases(newCaseResults, toRunWithId: run.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-results#add_results_for_cases + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#add_results_for_cases) */ public func addResultsForCases(_ newCaseResults: NewCaseResults, toRunWithId runId: Run.Id, completionHandler: @escaping (Outcome<[Result], AddError>) -> Void) { @@ -1753,15 +1777,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-results#get_results + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#get_results) */ public func getResultsForTest(_ test: Test, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { getResultsForTest(test.id, filteredBy: filters, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-results#get_results + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#get_results) */ public func getResultsForTest(_ testId: Test.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { api.getResults(testId: testId, filters: filters) { [weak self] (apiRequestOutcome) in @@ -1773,15 +1797,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-results#get_results_for_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#get_results_for_case) */ public func getResultsForCase(_ case: Case, in run: Run, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { getResultsForCase(`case`.id, inRunWithId: run.id, filteredBy: filters, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-results#get_results_for_case + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#get_results_for_case) */ public func getResultsForCase(_ caseId: Case.Id, inRunWithId runId: Run.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { api.getResultsForCase(runId: runId, caseId: caseId, filters: filters) { [weak self] (apiRequestOutcome) in @@ -1793,15 +1817,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-results#get_results_for_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#get_results_for_run) */ public func getResultsForRun(_ run: Run, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { getResultsForRun(run.id, filteredBy: filters, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-results#get_results_for_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results#get_results_for_run) */ public func getResultsForRun(_ runId: Run.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Result], GetError>) -> Void) { api.getResultsForRun(runId: runId, filters: filters) { [weak self] (apiRequestOutcome) in @@ -1815,8 +1839,8 @@ extension ObjectAPI { // MARK: - ResultField - /* - http://docs.gurock.com/testrail-api2/reference-results-fields#get_result_fields + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-results-fields#get_result_fields) */ public func getResultFields(completionHandler: @escaping (Outcome<[ResultField], GetError>) -> Void) { api.getResultFields { [weak self] (apiRequestOutcome) in @@ -1830,15 +1854,15 @@ extension ObjectAPI { // MARK: - Run - /* - http://docs.gurock.com/testrail-api2/reference-runs#add_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#add_run) */ public func addRun(_ newRun: NewRun, to project: Project, completionHandler: @escaping (Outcome) -> Void) { addRun(newRun, toProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-runs#add_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#add_run) */ public func addRun(_ newRun: NewRun, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -1861,15 +1885,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-runs#close_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#close_run) */ public func closeRun(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { closeRun(run.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-runs#close_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#close_run) */ public func closeRun(_ runId: Run.Id, completionHandler: @escaping (Outcome) -> Void) { api.closeRun(runId: runId) { [weak self] (apiRequestOutcome) in @@ -1881,15 +1905,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-runs#delete_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#delete_run) */ public func deleteRun(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { deleteRun(run.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-runs#delete_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#delete_run) */ public func deleteRun(_ runId: Run.Id, completionHandler: @escaping (Outcome) -> Void) { api.deleteRun(runId: runId) { [weak self] (apiRequestOutcome) in @@ -1906,8 +1930,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-runs#get_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#get_run) */ public func getRun(_ runId: Run.Id, completionHandler: @escaping (Outcome) -> Void) { api.getRun(runId: runId) { [weak self] (apiRequestOutcome) in @@ -1919,8 +1943,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-runs#get_runs + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#get_runs) This only returns Runs which are not part of a Plan. */ @@ -1928,8 +1952,8 @@ extension ObjectAPI { getRuns(inProjectWithId: project.id, filteredBy: filters, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-runs#get_runs + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#get_runs) This only returns Runs which are not part of a Plan. */ @@ -1943,8 +1967,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-runs#update_run + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-runs#update_run) */ public func updateRun(_ run: Run, completionHandler: @escaping (Outcome) -> Void) { @@ -1969,15 +1993,15 @@ extension ObjectAPI { // MARK: - Section - /* - http://docs.gurock.com/testrail-api2/reference-sections#add_section + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-sections#add_section) */ public func addSection(_ newSection: NewSection, to project: Project, completionHandler: @escaping (Outcome) -> Void) { addSection(newSection, toProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-sections#add_section + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-sections#add_section) */ public func addSection(_ newSection: NewSection, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -2000,15 +2024,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-sections#delete_section + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-sections#delete_section) */ public func deleteSection(_ section: Section, completionHandler: @escaping (Outcome) -> Void) { deleteSection(section.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-sections#delete_section + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-sections#delete_section) */ public func deleteSection(_ sectionId: Section.Id, completionHandler: @escaping (Outcome) -> Void) { api.deleteSection(sectionId: sectionId) { [weak self] (apiRequestOutcome) in @@ -2025,8 +2049,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-sections#get_section + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-sections#get_section) */ public func getSection(_ sectionId: Section.Id, completionHandler: @escaping (Outcome) -> Void) { api.getSection(sectionId: sectionId) { [weak self] (apiRequestOutcome) in @@ -2038,15 +2062,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-sections#get_sections + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-sections#get_sections) */ public func getSections(in project: Project, in suite: Suite? = nil, completionHandler: @escaping (Outcome<[Section], GetError>) -> Void) { getSections(inProjectWithId: project.id, inSuiteWithId: suite?.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-sections#get_sections + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-sections#get_sections) */ public func getSections(inProjectWithId projectId: Project.Id, inSuiteWithId suiteId: Suite.Id? = nil, completionHandler: @escaping (Outcome<[Section], GetError>) -> Void) { api.getSections(projectId: projectId, suiteId: suiteId) { [weak self] (apiRequestOutcome) in @@ -2058,8 +2082,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-sections#update_section + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-sections#update_section) */ public func updateSection(_ section: Section, completionHandler: @escaping (Outcome) -> Void) { @@ -2084,8 +2108,8 @@ extension ObjectAPI { // MARK: - Status - /* - http://docs.gurock.com/testrail-api2/reference-statuses#get_statuses + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-statuses#get_statuses) */ public func getStatuses(completionHandler: @escaping (Outcome<[Status], GetError>) -> Void) { api.getStatuses { [weak self] (apiRequestOutcome) in @@ -2099,15 +2123,15 @@ extension ObjectAPI { // MARK: - Suite - /* - http://docs.gurock.com/testrail-api2/reference-suites#add_suite + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-suites#add_suite) */ public func addSuite(_ newSuite: NewSuite, to project: Project, completionHandler: @escaping (Outcome) -> Void) { addSuite(newSuite, toProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-suites#add_suite + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-suites#add_suite) */ public func addSuite(_ newSuite: NewSuite, toProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome) -> Void) { @@ -2130,15 +2154,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-suites#delete_suite + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-suites#delete_suite) */ public func deleteSuite(_ suite: Suite, completionHandler: @escaping (Outcome) -> Void) { deleteSuite(suite.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-suites#delete_suite + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-suites#delete_suite) */ public func deleteSuite(_ suiteId: Suite.Id, completionHandler: @escaping (Outcome) -> Void) { api.deleteSuite(suiteId: suiteId) { [weak self] (apiRequestOutcome) in @@ -2155,8 +2179,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-suites#get_suite + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-suites#get_suite) */ public func getSuite(_ suiteId: Suite.Id, completionHandler: @escaping (Outcome) -> Void) { api.getSuite(suiteId: suiteId) { [weak self] (apiRequestOutcome) in @@ -2168,15 +2192,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-suites#get_suites + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-suites#get_suites) */ public func getSuites(in project: Project, completionHandler: @escaping (Outcome<[Suite], GetError>) -> Void) { getSuites(inProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-suites#get_suites + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-suites#get_suites) */ public func getSuites(inProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome<[Suite], GetError>) -> Void) { api.getSuites(projectId: projectId) { [weak self] (apiRequestOutcome) in @@ -2188,8 +2212,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-suites#update_suite + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-suites#update_suite) */ public func updateSuite(_ suite: Suite, completionHandler: @escaping (Outcome) -> Void) { @@ -2214,7 +2238,7 @@ extension ObjectAPI { // MARK: - Template - /* + /** Gets all Templates from all Projects. A template may appear in some or all projects. @@ -2266,15 +2290,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-templates#get_templates + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-templates#get_templates) */ public func getTemplates(in project: Project, completionHandler: @escaping (Outcome<[Template], GetError>) -> Void) { getTemplates(inProjectWithId: project.id, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-templates#get_templates + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-templates#get_templates) */ public func getTemplates(inProjectWithId projectId: Project.Id, completionHandler: @escaping (Outcome<[Template], GetError>) -> Void) { api.getTemplates(projectId: projectId) { [weak self] (apiRequestOutcome) in @@ -2288,8 +2312,8 @@ extension ObjectAPI { // MARK: - Test - /* - http://docs.gurock.com/testrail-api2/reference-tests#get_test + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-tests#get_test) */ public func getTest(_ testId: Test.Id, completionHandler: @escaping (Outcome) -> Void) { api.getTest(testId: testId) { [weak self] (apiRequestOutcome) in @@ -2301,15 +2325,15 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-tests#get_tests + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-tests#get_tests) */ public func getTests(in run: Run, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Test], GetError>) -> Void) { getTests(inRunWithId: run.id, filteredBy: filters, completionHandler: completionHandler) } - /* - http://docs.gurock.com/testrail-api2/reference-tests#get_tests + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-tests#get_tests) */ public func getTests(inRunWithId runId: Run.Id, filteredBy filters: [Filter]? = nil, completionHandler: @escaping (Outcome<[Test], GetError>) -> Void) { api.getTests(runId: runId, filters: filters) { [weak self] (apiRequestOutcome) in @@ -2323,8 +2347,8 @@ extension ObjectAPI { // MARK: - User - /* - http://docs.gurock.com/testrail-api2/reference-users#get_user + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-users#get_user) */ public func getUser(_ userId: User.Id, completionHandler: @escaping (Outcome) -> Void) { api.getUser(userId: userId) { [weak self] (apiRequestOutcome) in @@ -2336,8 +2360,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-users#get_user_by_email + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-users#get_user_by_email) */ public func getUserByEmail(_ email: String, completionHandler: @escaping (Outcome) -> Void) { api.getUserByEmail(email) { [weak self] (apiRequestOutcome) in @@ -2349,8 +2373,8 @@ extension ObjectAPI { } } - /* - http://docs.gurock.com/testrail-api2/reference-users#get_users + /** + [API Reference](http://docs.gurock.com/testrail-api2/reference-users#get_users) */ public func getUsers(completionHandler: @escaping (Outcome<[User], GetError>) -> Void) { api.getUsers { [weak self] (apiRequestOutcome) in @@ -2368,7 +2392,7 @@ extension ObjectAPI { extension ObjectAPI { - /* + /** - Upon success returns an Outcome with items from |items| matching all |ids|. Requires every id in |ids| to match an item. - Upon failure returns an Outcome indicating if no matches were found, or @@ -2564,7 +2588,7 @@ extension ObjectAPI { // MARK: - Config - /* + /** Calls the projects() method handling/stripping the MultipleMatchError. Upon success this effectively returns all projects accessible to the current API user while silently omitting any which are missing. Failure only occurs if @@ -2591,7 +2615,7 @@ extension ObjectAPI { } } - /* + /** Asynchronously gets and returns projects for a Config. The outcome passed to the completionHandler varies based on: diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewCheckboxCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewCheckboxCaseFieldTests.swift new file mode 100644 index 0000000..93ef6d5 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewCheckboxCaseFieldTests.swift @@ -0,0 +1,194 @@ +import XCTest +@testable import QuizTrain + +class NewCheckboxCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewCheckboxCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + static let defaultValue = false + } + + struct Optional { + static let description = "Description" + } + + } + +} + +// MARK: - Objects + +extension NewCheckboxCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: Properties.Required.defaultValue) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: Properties.Required.defaultValue) + } + +} + +// MARK: - Assertions + +extension NewCheckboxCaseFieldTests: AssertAddRequestJSON { } + +extension NewCheckboxCaseFieldTests: AssertCodable { } + +extension NewCheckboxCaseFieldTests: AssertEquatable { } + +extension NewCheckboxCaseFieldTests: AssertJSONSerializing { } + +extension NewCheckboxCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.checkbox) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + XCTAssertEqual(config.options.defaultValue, Properties.Required.defaultValue) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + config.options.defaultValue = true + + // Update Context/Options through .data. + object.data = NewCaseFieldCheckboxData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.defaultValue, Properties.Required.defaultValue) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.defaultValue, true) + XCTAssertEqual(object.configs[0].options.isRequired, true) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewDateCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewDateCaseFieldTests.swift new file mode 100644 index 0000000..8b12ab7 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewDateCaseFieldTests.swift @@ -0,0 +1,187 @@ +import XCTest +@testable import QuizTrain + +class NewDateCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewDateCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + } + + struct Optional { + static let description = "Description" + } + + } + +} + +// MARK: - Objects + +extension NewDateCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired) + } + +} + +// MARK: - Assertions + +extension NewDateCaseFieldTests: AssertAddRequestJSON { } + +extension NewDateCaseFieldTests: AssertCodable { } + +extension NewDateCaseFieldTests: AssertEquatable { } + +extension NewDateCaseFieldTests: AssertJSONSerializing { } + +extension NewDateCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.date) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + + // Update Context/Options through .data. + object.data = NewCaseFieldDateData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.isRequired, true) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewDropdownCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewDropdownCaseFieldTests.swift new file mode 100644 index 0000000..904c581 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewDropdownCaseFieldTests.swift @@ -0,0 +1,231 @@ +import XCTest +@testable import QuizTrain + +class NewDropdownCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + + func testThrows() { + + XCTAssertThrowsError(try Object(description: nil, label: "", name: "", includeAll: true, templateIds: [], isGlobal: true, projectIds: [], isRequired: true, items: [], defaultValue: 0)) + XCTAssertNoThrow(try Object(description: nil, label: "", name: "", includeAll: true, templateIds: [], isGlobal: true, projectIds: [], isRequired: true, items: ["A"], defaultValue: 0)) + + XCTAssertThrowsError(try Object(description: nil, label: "", name: "", includeAll: true, templateIds: [], isGlobal: true, projectIds: [], isRequired: true, items: ["A"], defaultValue: 1)) + XCTAssertNoThrow(try Object(description: nil, label: "", name: "", includeAll: true, templateIds: [], isGlobal: true, projectIds: [], isRequired: true, items: ["A", "B"], defaultValue: 1)) + + continueAfterFailure = false + let object = objectWithRequiredProperties + guard var config = object.configs.first else { + XCTFail("Config cannot be nil.") + return + } + continueAfterFailure = true + + XCTAssertThrowsError(try config.options.set(items: [], withDefaultValue: 0)) + XCTAssertNoThrow(try config.options.set(items: ["A"], withDefaultValue: 0)) + + XCTAssertThrowsError(try config.options.set(items: ["A", "B", "C"], withDefaultValue: 3)) + XCTAssertNoThrow(try config.options.set(items: ["A", "B", "C"], withDefaultValue: 2)) + } + +} + +// MARK: - Data + +extension NewDropdownCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + static let items = ["One", "Two", "Three"] + static let defaultValue = 2 + } + + struct Optional { + static let description = "Description" + } + + } + +} + +// MARK: - Objects + +extension NewDropdownCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + // swiftlint:disable:next force_try + return try! Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + items: Properties.Required.items, + defaultValue: Properties.Required.defaultValue) + } + + static var objectWithRequiredAndOptionalProperties: Object { + // swiftlint:disable:next force_try + return try! Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + items: Properties.Required.items, + defaultValue: Properties.Required.defaultValue) + } + +} + +// MARK: - Assertions + +extension NewDropdownCaseFieldTests: AssertAddRequestJSON { } + +extension NewDropdownCaseFieldTests: AssertCodable { } + +extension NewDropdownCaseFieldTests: AssertEquatable { } + +extension NewDropdownCaseFieldTests: AssertJSONSerializing { } + +extension NewDropdownCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.dropdown) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + XCTAssertEqual(config.options.items, Properties.Required.items) + XCTAssertEqual(config.options.defaultValue, Properties.Required.defaultValue) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + continueAfterFailure = false + do { + try config.options.set(items: ["A", "B"], withDefaultValue: 1) + } catch { + XCTFail(String(describing: error)) + } + continueAfterFailure = true + + // Update Context/Options through .data. + object.data = NewCaseFieldDropdownData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + XCTAssertNotEqual(object.configs[0].options.items, Properties.Required.items) + XCTAssertNotEqual(object.configs[0].options.defaultValue, Properties.Required.defaultValue) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.isRequired, true) + XCTAssertEqual(object.configs[0].options.items, ["A", "B"]) + XCTAssertEqual(object.configs[0].options.defaultValue, 1) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewIntegerCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewIntegerCaseFieldTests.swift new file mode 100644 index 0000000..b968825 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewIntegerCaseFieldTests.swift @@ -0,0 +1,201 @@ +import XCTest +@testable import QuizTrain + +class NewIntegerCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewIntegerCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + } + + struct Optional { + static let description = "Description" + static let defaultValue: Int? = nil + } + + } + +} + +// MARK: - Objects + +extension NewIntegerCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: Properties.Optional.defaultValue) + } + +} + +// MARK: - Assertions + +extension NewIntegerCaseFieldTests: AssertAddRequestJSON { } + +extension NewIntegerCaseFieldTests: AssertCodable { } + +extension NewIntegerCaseFieldTests: AssertEquatable { } + +extension NewIntegerCaseFieldTests: AssertJSONSerializing { } + +extension NewIntegerCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.integer) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(config.options.defaultValue) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(config.options.defaultValue, Properties.Optional.defaultValue) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + config.options.defaultValue = 999 + + // Update Context/Options through .data. + object.data = NewCaseFieldIntegerData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.defaultValue, Properties.Optional.defaultValue) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.defaultValue, 999) + XCTAssertEqual(object.configs[0].options.isRequired, true) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewMilestoneCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewMilestoneCaseFieldTests.swift new file mode 100644 index 0000000..0a1d715 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewMilestoneCaseFieldTests.swift @@ -0,0 +1,187 @@ +import XCTest +@testable import QuizTrain + +class NewMilestoneCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewMilestoneCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + } + + struct Optional { + static let description = "Description" + } + + } + +} + +// MARK: - Objects + +extension NewMilestoneCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired) + } + +} + +// MARK: - Assertions + +extension NewMilestoneCaseFieldTests: AssertAddRequestJSON { } + +extension NewMilestoneCaseFieldTests: AssertCodable { } + +extension NewMilestoneCaseFieldTests: AssertEquatable { } + +extension NewMilestoneCaseFieldTests: AssertJSONSerializing { } + +extension NewMilestoneCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.milestone) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + + // Update Context/Options through .data. + object.data = NewCaseFieldMilestoneData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.isRequired, true) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewMultiselectCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewMultiselectCaseFieldTests.swift new file mode 100644 index 0000000..45e7958 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewMultiselectCaseFieldTests.swift @@ -0,0 +1,219 @@ +import XCTest +@testable import QuizTrain + +class NewMultiselectCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + + func testThrows() { + + XCTAssertThrowsError(try Object(description: nil, label: "", name: "", includeAll: true, templateIds: [], isGlobal: true, projectIds: [], isRequired: true, items: [])) + XCTAssertNoThrow(try Object(description: nil, label: "", name: "", includeAll: true, templateIds: [], isGlobal: true, projectIds: [], isRequired: true, items: ["A"])) + + continueAfterFailure = false + let object = objectWithRequiredProperties + guard var config = object.configs.first else { + XCTFail("Config cannot be nil.") + return + } + continueAfterFailure = true + + XCTAssertThrowsError(try config.options.set(items: [])) + XCTAssertNoThrow(try config.options.set(items: ["A"])) + } + +} + +// MARK: - Data + +extension NewMultiselectCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + static let items = ["A", "B", "C"] + } + + struct Optional { + static let description = "Description" + } + + } + +} + +// MARK: - Objects + +extension NewMultiselectCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + // swiftlint:disable:next force_try + return try! Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + items: Properties.Required.items) + } + + static var objectWithRequiredAndOptionalProperties: Object { + // swiftlint:disable:next force_try + return try! Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + items: Properties.Required.items) + } + +} + +// MARK: - Assertions + +extension NewMultiselectCaseFieldTests: AssertAddRequestJSON { } + +extension NewMultiselectCaseFieldTests: AssertCodable { } + +extension NewMultiselectCaseFieldTests: AssertEquatable { } + +extension NewMultiselectCaseFieldTests: AssertJSONSerializing { } + +extension NewMultiselectCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.multiselect) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + XCTAssertEqual(config.options.items, Properties.Required.items) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + continueAfterFailure = false + do { + try config.options.set(items: ["One", "Two", "Three"]) + } catch { + XCTFail(String(describing: error)) + } + continueAfterFailure = true + + // Update Context/Options through .data. + object.data = NewCaseFieldMultiselectData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + XCTAssertNotEqual(object.configs[0].options.items, Properties.Required.items) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.isRequired, true) + XCTAssertEqual(object.configs[0].options.items, ["One", "Two", "Three"]) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewStepsCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewStepsCaseFieldTests.swift new file mode 100644 index 0000000..6971728 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewStepsCaseFieldTests.swift @@ -0,0 +1,207 @@ +import XCTest +@testable import QuizTrain + +class NewStepsCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewStepsCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + static let format = NewCaseFieldConfigStepsOptions.Format.plain + static let hasExpected = false + static let rows = NewCaseFieldConfigStepsOptions.Rows.five + } + + struct Optional { + static let description = "Description" + } + + } + +} + +// MARK: - Objects + +extension NewStepsCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + format: Properties.Required.format, + hasExpected: Properties.Required.hasExpected, + rows: Properties.Required.rows) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + format: Properties.Required.format, + hasExpected: Properties.Required.hasExpected, + rows: Properties.Required.rows) + } + +} + +// MARK: - Assertions + +extension NewStepsCaseFieldTests: AssertAddRequestJSON { } + +extension NewStepsCaseFieldTests: AssertCodable { } + +extension NewStepsCaseFieldTests: AssertEquatable { } + +extension NewStepsCaseFieldTests: AssertJSONSerializing { } + +extension NewStepsCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.steps) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + XCTAssertEqual(config.options.format, Properties.Required.format) + XCTAssertEqual(config.options.hasExpected, Properties.Required.hasExpected) + XCTAssertEqual(config.options.rows, Properties.Required.rows) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + if areNil { + XCTAssertNil(object.description) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + config.options.format = .markdown + config.options.isRequired = true + config.options.rows = .unspecified + + // Update Context/Options through .data. + object.data = NewCaseFieldStepsData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + XCTAssertNotEqual(object.configs[0].options.format, Properties.Required.format) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + XCTAssertNotEqual(object.configs[0].options.rows, Properties.Required.rows) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.isRequired, true) + XCTAssertEqual(object.configs[0].options.format, .markdown) + XCTAssertEqual(object.configs[0].options.rows, .unspecified) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewStringCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewStringCaseFieldTests.swift new file mode 100644 index 0000000..60b3830 --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewStringCaseFieldTests.swift @@ -0,0 +1,201 @@ +import XCTest +@testable import QuizTrain + +class NewStringCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewStringCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + } + + struct Optional { + static let description = "Description" + static let defaultValue = "Hello QuizTrain!" + } + + } + +} + +// MARK: - Objects + +extension NewStringCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: Properties.Optional.defaultValue) + } + +} + +// MARK: - Assertions + +extension NewStringCaseFieldTests: AssertAddRequestJSON { } + +extension NewStringCaseFieldTests: AssertCodable { } + +extension NewStringCaseFieldTests: AssertEquatable { } + +extension NewStringCaseFieldTests: AssertJSONSerializing { } + +extension NewStringCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.string) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(config.options.defaultValue) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(config.options.defaultValue, Properties.Optional.defaultValue) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + config.options.defaultValue = "Goodbye QuizTrain!" + + // Update Context/Options through .data. + object.data = NewCaseFieldStringData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.defaultValue, Properties.Optional.defaultValue) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.defaultValue, "Goodbye QuizTrain!") + XCTAssertEqual(object.configs[0].options.isRequired, true) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewTextCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewTextCaseFieldTests.swift new file mode 100644 index 0000000..1997dde --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewTextCaseFieldTests.swift @@ -0,0 +1,215 @@ +import XCTest +@testable import QuizTrain + +class NewTextCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewTextCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + static let format = NewCaseFieldConfigTextOptions.Format.plain + static let rows = NewCaseFieldConfigTextOptions.Rows.five + } + + struct Optional { + static let description = "Description" + static let defaultValue = "Hello QuizTrain!" + } + + } + +} + +// MARK: - Objects + +extension NewTextCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: nil, + format: Properties.Required.format, + rows: Properties.Required.rows) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: Properties.Optional.defaultValue, + format: Properties.Required.format, + rows: Properties.Required.rows) + } + +} + +// MARK: - Assertions + +extension NewTextCaseFieldTests: AssertAddRequestJSON { } + +extension NewTextCaseFieldTests: AssertCodable { } + +extension NewTextCaseFieldTests: AssertEquatable { } + +extension NewTextCaseFieldTests: AssertJSONSerializing { } + +extension NewTextCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.text) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + XCTAssertEqual(config.options.format, Properties.Required.format) + XCTAssertEqual(config.options.rows, Properties.Required.rows) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(config.options.defaultValue) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(config.options.defaultValue, Properties.Optional.defaultValue) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + config.options.defaultValue = "Goodbye QuizTrain!" + config.options.format = .markdown + config.options.rows = .unspecified + + // Update Context/Options through .data. + object.data = NewCaseFieldTextData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + XCTAssertNotEqual(object.configs[0].options.defaultValue, Properties.Optional.defaultValue) + XCTAssertNotEqual(object.configs[0].options.format, Properties.Required.format) + XCTAssertNotEqual(object.configs[0].options.rows, Properties.Required.rows) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.isRequired, true) + XCTAssertEqual(object.configs[0].options.defaultValue, "Goodbye QuizTrain!") + XCTAssertEqual(object.configs[0].options.format, .markdown) + XCTAssertEqual(object.configs[0].options.rows, .unspecified) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewURLCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewURLCaseFieldTests.swift new file mode 100644 index 0000000..872cd4e --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewURLCaseFieldTests.swift @@ -0,0 +1,201 @@ +import XCTest +@testable import QuizTrain + +class NewURLCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewURLCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + } + + struct Optional { + static let description = "Description" + static let defaultValue = URL(string: "https://github.com/venmo/QuizTrain/") + } + + } + +} + +// MARK: - Objects + +extension NewURLCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: Properties.Optional.defaultValue) + } + +} + +// MARK: - Assertions + +extension NewURLCaseFieldTests: AssertAddRequestJSON { } + +extension NewURLCaseFieldTests: AssertCodable { } + +extension NewURLCaseFieldTests: AssertEquatable { } + +extension NewURLCaseFieldTests: AssertJSONSerializing { } + +extension NewURLCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.url) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(config.options.defaultValue) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(config.options.defaultValue, Properties.Optional.defaultValue) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + config.options.defaultValue = URL(string: "https://www.venmo.com/") + + // Update Context/Options through .data. + object.data = NewCaseFieldURLData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.defaultValue, Properties.Optional.defaultValue) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.defaultValue, URL(string: "https://www.venmo.com/")) + XCTAssertEqual(object.configs[0].options.isRequired, true) + } + +} diff --git a/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewUserCaseFieldTests.swift b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewUserCaseFieldTests.swift new file mode 100644 index 0000000..739a95f --- /dev/null +++ b/QuizTrainTests/Network/Models/Add/NewCaseFieldTests/NewUserCaseFieldTests.swift @@ -0,0 +1,201 @@ +import XCTest +@testable import QuizTrain + +class NewUserCaseFieldTests: XCTestCase, AddModelTests, CodableTests { + + typealias Object = NewCaseField + + func testAddRequestJSON() { + _testAddRequestJSON() + } + + func testEquatable() { + _testEquatable() + } + + func testInit() { + _testInit() + } + + func testInitWithOptionalProperties() { + _testInitWithOptionalProperties() + } + + func testJSONSerializingSingleObjects() { + _testJSONSerializingSingleObjects() + } + + func testJSONSerializingMultipleObjects() { + _testJSONSerializingMultipleObjects() + } + + func testVariableProperties() { + _testVariableProperties() + } + + func testCodable() { + _testCodable() + } + +} + +// MARK: - Data + +extension NewUserCaseFieldTests { + + struct Properties { + + struct Required { + static let label = "Label" + static let name = "quiztrain_name" + static let includeAll = true + static let templateIds = [Int]() + static let isGlobal = true + static let projectIds = [Int]() + static let isRequired = false + } + + struct Optional { + static let description = "Description" + static let defaultValue = 5 + } + + } + +} + +// MARK: - Objects + +extension NewUserCaseFieldTests: ObjectProvider { + + static var objectWithRequiredProperties: Object { + return Object(description: nil, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: nil) + } + + static var objectWithRequiredAndOptionalProperties: Object { + return Object(description: Properties.Optional.description, + label: Properties.Required.label, + name: Properties.Required.name, + includeAll: Properties.Required.includeAll, + templateIds: Properties.Required.templateIds, + isGlobal: Properties.Required.isGlobal, + projectIds: Properties.Required.projectIds, + isRequired: Properties.Required.isRequired, + defaultValue: Properties.Optional.defaultValue) + } + +} + +// MARK: - Assertions + +extension NewUserCaseFieldTests: AssertAddRequestJSON { } + +extension NewUserCaseFieldTests: AssertCodable { } + +extension NewUserCaseFieldTests: AssertEquatable { } + +extension NewUserCaseFieldTests: AssertJSONSerializing { } + +extension NewUserCaseFieldTests: AssertProperties { + + func assertRequiredProperties(in object: Object) { + + // NewCaseField + XCTAssertEqual(object.label, Properties.Required.label) + XCTAssertEqual(object.name, Properties.Required.name) + XCTAssertEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertEqual(object.templateIds, Properties.Required.templateIds) + + // NewCaseField.Configs + XCTAssertEqual(object.type, NewCaseFieldType.user) + + // NewCaseField.Configs[0] + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + // NewCaseField.Configs[0].Context + XCTAssertEqual(config.context.isGlobal, Properties.Required.isGlobal) + XCTAssertEqual(config.context.projectIds, Properties.Required.projectIds) + + // NewCaseField.Configs[0].Options + XCTAssertEqual(config.options.isRequired, Properties.Required.isRequired) + } + + func assertOptionalProperties(in object: Object, areNil: Bool) { + + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + let config = object.configs[0] + + if areNil { + XCTAssertNil(object.description) + XCTAssertNil(config.options.defaultValue) + } else { + XCTAssertEqual(object.description, Properties.Optional.description) + XCTAssertEqual(config.options.defaultValue, Properties.Optional.defaultValue) + } + } + + func assertVariablePropertiesCanBeChanged(in object: inout Object) { + + // Properties + + // NewCaseField + object.description = "New Description" + object.label = "New Label" + object.name = "new_name" + object.includeAll = false + object.templateIds = [1, 2, 3] + + // NewCaseField.Configs + continueAfterFailure = false + XCTAssertNotNil(object.configs.first) + continueAfterFailure = true + var config = object.configs[0] + + // NewCaseField.Configs[0].Context + config.context.isGlobal = false + config.context.projectIds = [1, 2, 3] + + // NewCaseField.Configs[0].Options + config.options.isRequired = true + config.options.defaultValue = 999 + + // Update Context/Options through .data. + object.data = NewCaseFieldUserData(configs: [config]) + + // Assertions + + XCTAssertNotEqual(object.description, Properties.Optional.description) + XCTAssertNotEqual(object.label, Properties.Required.label) + XCTAssertNotEqual(object.name, Properties.Required.name) + XCTAssertNotEqual(object.includeAll, Properties.Required.includeAll) + XCTAssertNotEqual(object.templateIds, Properties.Required.templateIds) + XCTAssertNotEqual(object.configs[0].context.isGlobal, Properties.Required.isGlobal) + XCTAssertNotEqual(object.configs[0].context.projectIds, Properties.Required.projectIds) + XCTAssertNotEqual(object.configs[0].options.defaultValue, Properties.Optional.defaultValue) + XCTAssertNotEqual(object.configs[0].options.isRequired, Properties.Required.isRequired) + + XCTAssertEqual(object.description, "New Description") + XCTAssertEqual(object.label, "New Label") + XCTAssertEqual(object.name, "new_name") + XCTAssertEqual(object.includeAll, false) + XCTAssertEqual(object.templateIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].context.isGlobal, false) + XCTAssertEqual(object.configs[0].context.projectIds, [1, 2, 3]) + XCTAssertEqual(object.configs[0].options.defaultValue, 999) + XCTAssertEqual(object.configs[0].options.isRequired, true) + } + +} diff --git a/QuizTrainTests/Network/ObjectAPITests.swift b/QuizTrainTests/Network/ObjectAPITests.swift index e946929..6021a13 100644 --- a/QuizTrainTests/Network/ObjectAPITests.swift +++ b/QuizTrainTests/Network/ObjectAPITests.swift @@ -1,8 +1,8 @@ import XCTest @testable import QuizTrain -/* - ObjectAPI systems tests. These tests run against a real-world TestRail +/** + ObjectAPI end-to-end tests. These tests run against a real-world TestRail instance. Sections: - Initialization, deinit, stored properties. @@ -26,6 +26,12 @@ import XCTest names will be prefixed with "QuizTrainTests". It is safe to delete them so long as you have no production projects starting with the same name. + If any testAdd*CaseField() tests are run then you will have to manually remove + any CaseField's created by tests using your TestRail administration portal. + Tests are unable to remove these automatically because there is no public API + endpoint to do so. Because of this these tests are disabled by default in all + testing schemes. See the QuizTrainTests README.md for more details. + For tests to run you must populate TestCredentials.json with all properties required by TestCredentials.swift. @@ -347,6 +353,338 @@ extension ObjectAPITests { return newCase } + func newCaseFieldString(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: String", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + defaultValue: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + newCaseField.data.configs[0].options.defaultValue = "Hello QuizTrain" + } + + return newCaseField + } + + func newCaseFieldInteger(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: Integer", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + defaultValue: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + newCaseField.data.configs[0].options.defaultValue = 999 + } + + return newCaseField + } + + func newCaseFieldText(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: Text", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + defaultValue: nil, + format: .markdown, + rows: .unspecified) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + newCaseField.data.configs[0].options.defaultValue = "Hello QuizTrain" + newCaseField.data.configs[0].options.rows = .three + } + + return newCaseField + } + + func newCaseFieldURL(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: URL", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + defaultValue: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + newCaseField.data.configs[0].options.defaultValue = URL(string: "https://github.com/venmo/QuizTrain/") + } + + return newCaseField + } + + func newCaseFieldCheckbox(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: Checkbox", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + defaultValue: false) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + } + + return newCaseField + } + + func newCaseFieldDropdown(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + // Dropdown will throw if `defaultValue` is of out of bounds of `items` or if `items` is empty. + var newCaseField: NewCaseField + do { + let items = ["One", "Two", "Three"] + newCaseField = try NewCaseField(description: nil, + label: "Test Add: Dropdown", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + items: items, + defaultValue: (items.count - 1)) + } catch { + fatalError(error.localizedDescription) + } + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + } + + return newCaseField + } + + func newCaseFieldUser(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: User", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + defaultValue: nil) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + newCaseField.data.configs[0].options.defaultValue = testProject.user.id + } + + return newCaseField + } + + func newCaseFieldDate(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: Date", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + } + + return newCaseField + } + + func newCaseFieldMilestone(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: Milestone", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + } + + return newCaseField + } + + func newCaseFieldSteps(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + var newCaseField = NewCaseField(description: nil, + label: "Test Add: Steps", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + format: .markdown, + hasExpected: false, + rows: .unspecified) + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + newCaseField.data.configs[0].options.hasExpected = true + newCaseField.data.configs[0].options.rows = .five + } + + return newCaseField + } + + func newCaseFieldMultiselect(with properties: Properties) -> NewCaseField { + + precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") + + // Multiselect will throw if `items` is empty. + var newCaseField: NewCaseField + do { + newCaseField = try NewCaseField(description: nil, + label: "Test Add: Multiselect", + name: "quiztraintests_\(randomString())", + includeAll: true, + templateIds: [], + isGlobal: true, + projectIds: [], + isRequired: false, + items: ["A", "B", "C"]) + } catch { + fatalError(error.localizedDescription) + } + + switch properties { + case .requiredProperties: + break + case .requiredAndOptionalProperties: + newCaseField.description = "Test Add: Description" + newCaseField.includeAll = false // Must be false if templateIds is not empty. + newCaseField.templateIds = [testProject.templates[0].id] + newCaseField.data.configs[0].context.isGlobal = false // Must be false if projectIds is not empty. + newCaseField.data.configs[0].context.projectIds = [testProject.project.id] + } + + return newCaseField + } + func newConfiguration() -> NewConfiguration { precondition(testProject != nil, "The Test Project must be setup before invoking \(#function)") return NewConfiguration(name: "Test Add: Configuration Name") @@ -700,6 +1038,28 @@ extension ObjectAPITests { } +// MARK: - Data Provider Helpers + +extension ObjectAPITests { + + fileprivate func randomString(length: UInt = 8) -> String { + let characters = "abcdefghijklmnopqrstuvwxyz" + let randomCharacters = (0..(_ newCaseField: NewCaseField) -> CaseField? { + + let expectation = XCTestExpectation(description: "Add Case Field") + + var caseField: CaseField? + objectAPI.addCaseField(newCaseField) { (outcome) in + caseField = self.assertOutcomeSucceeded(outcome) + expectation.fulfill() + } + + wait(for: [expectation], timeout: timeout) + + XCTAssertNotNil(caseField) + + if let caseField = caseField { + + // Common + XCTAssertEqual(caseField.description, newCaseField.description) + XCTAssertEqual(caseField.includeAll, newCaseField.includeAll) + XCTAssertEqual(caseField.label, newCaseField.label) + XCTAssertEqual(caseField.name, newCaseField.name) + XCTAssertEqual(caseField.templateIds, newCaseField.templateIds) + XCTAssertTrue(caseField.typeId == newCaseField.type) + + // Common Config.Context + XCTAssertEqual(caseField.configs[0].context.isGlobal, newCaseField.configs[0].context.isGlobal) + XCTAssertEqual((caseField.configs[0].context.projectIds ?? []), newCaseField.configs[0].context.projectIds) + + // Type Specific Config.Options + if caseField.typeId == newCaseField.type { + switch newCaseField.type { + case .string: + let data = newCaseField.data as! NewCaseFieldStringData + let options = data.configs[0].options + let defaultValue = options.defaultValue ?? "" + XCTAssertEqual(defaultValue, caseField.configs[0].options["default_value"] as? String) + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + case .integer: + let data = newCaseField.data as! NewCaseFieldIntegerData + let options = data.configs[0].options + let defaultValue = options.defaultValue != nil ? String(options.defaultValue!) : "" + XCTAssertEqual(defaultValue, caseField.configs[0].options["default_value"] as? String) + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + case .text: + let data = newCaseField.data as! NewCaseFieldTextData + let options = data.configs[0].options + let defaultValue = options.defaultValue ?? "" + XCTAssertEqual(defaultValue, caseField.configs[0].options["default_value"] as? String) + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + XCTAssertEqual(options.format.rawValue, caseField.configs[0].options["format"] as? String) + XCTAssertEqual(options.rows.rawValue, caseField.configs[0].options["rows"] as? String) + case .url: + let data = newCaseField.data as! NewCaseFieldURLData + let options = data.configs[0].options + let defaultValue = options.defaultValue?.absoluteString ?? "" + XCTAssertEqual(defaultValue, caseField.configs[0].options["default_value"] as? String) + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + case .checkbox: + let data = newCaseField.data as! NewCaseFieldCheckboxData + let options = data.configs[0].options + XCTAssertEqual(options.defaultValue, caseField.configs[0].options["default_value"] as? Bool) + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + case .dropdown: + let data = newCaseField.data as! NewCaseFieldDropdownData + let options = data.configs[0].options + let defaultValue = String((options.defaultValue + 1)) // 0 indexed ---> 1 indexed + XCTAssertEqual(defaultValue, caseField.configs[0].options["default_value"] as? String) + if let itemsString = caseField.configs[0].options["items"] as? String, + let items = CaseFieldItemsConverter.items(from: itemsString) { + XCTAssertEqual(options.items, items) + } else { + XCTFail("Invalid items: \(String(describing: caseField.configs[0].options["items"]))") + } + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + case .user: + let data = newCaseField.data as! NewCaseFieldUserData + let options = data.configs[0].options + let defaultValue = options.defaultValue != nil ? String(options.defaultValue!) : "" + XCTAssertEqual(defaultValue, caseField.configs[0].options["default_value"] as? String) + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + case .date: + let data = newCaseField.data as! NewCaseFieldDateData + let options = data.configs[0].options + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + case .milestone: + let data = newCaseField.data as! NewCaseFieldMilestoneData + let options = data.configs[0].options + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + case .steps: + let data = newCaseField.data as! NewCaseFieldStepsData + let options = data.configs[0].options + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + XCTAssertEqual(options.format.rawValue, caseField.configs[0].options["format"] as? String) + XCTAssertEqual(options.hasExpected, caseField.configs[0].options["has_expected"] as? Bool) + XCTAssertEqual(options.rows.rawValue, caseField.configs[0].options["rows"] as? String) + case .multiselect: + let data = newCaseField.data as! NewCaseFieldMultiselectData + let options = data.configs[0].options + if let itemsString = caseField.configs[0].options["items"] as? String, + let items = CaseFieldItemsConverter.items(from: itemsString) { + XCTAssertEqual(options.items, items) + } else { + XCTFail("Invalid items: \(String(describing: caseField.configs[0].options["items"]))") + } + XCTAssertEqual(options.isRequired, caseField.configs[0].options["is_required"] as? Bool) + } + } + } + + return caseField + } + // MARK: CaseType @discardableResult func assertGetCaseTypes() -> [CaseType]? { diff --git a/QuizTrainTests/README.md b/QuizTrainTests/README.md index 3a96581..885e7aa 100644 --- a/QuizTrainTests/README.md +++ b/QuizTrainTests/README.md @@ -13,3 +13,23 @@ QuizTrainTests provides unit tests and systems tests against a real TestRail ins 4. In the Xcode menu: `Product -> Test` For [ObjectAPITests.swift](Network/ObjectAPITests.swift) to run you must set any custom **Case Fields** and **Result Fields** in your TestRail instance either as not-required, or required with a default value, for the duration of testing. Otherwise tests might not be able to setup necessary test projects since they do not know of required customizations specific to your instance. + +## NewCaseField Tests + +The following NewCaseField tests in [ObjectAPITests](Network/ObjectAPITests.swift) are disabled by default in all testing schemes since they cannot be deleted using the API after tests complete: + + testAddCheckboxCaseField() + testAddDateCaseField() + testAddDropdownCaseField() + testAddIntegerCaseField() + testAddMilestoneCaseField() + testAddMultiselectCaseField() + testAddStepsCaseField() + testAddStringCaseField() + testAddTextCaseField() + testAddURLCaseField() + testAddUserCaseField() + +If you run or enable these tests you must manually delete the Case Fields they create in your TestRail administration dashboard. Their system name will be prefixed with `quiztraintests_` followed by eight random letters a-z, and their label will be the name of the test method. + +Note `testAddStepsCaseField()` will fail with a 400 error if you have already created a Case Field of type `NewCaseFieldType.steps`. As of writing only one of that type is allowed across your entire TestRail instance. diff --git a/QuizTrainTests/Testing Protocols/Asserts/AssertCodable.swift b/QuizTrainTests/Testing Protocols/Asserts/AssertCodable.swift new file mode 100644 index 0000000..017d216 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Asserts/AssertCodable.swift @@ -0,0 +1,67 @@ +import XCTest +@testable import QuizTrain + +protocol AssertCodable { + func assertTwoWayJSONCodable(_ object: Object) + func assertTwoWayPlistCodable(_ object: Object) +} + +extension AssertCodable { + + func assertTwoWayJSONCodable(_ object: Object) { + + let encoder = JSONEncoder() + let decoder = JSONDecoder() + + // Object -> JSON + + let data: Data + do { + data = try encoder.encode(object) + } catch { + XCTFail("Encoding \(object) failed with error: \(error)") + return + } + + // JSON -> Object + + let decodedObject: Object + do { + decodedObject = try decoder.decode(Object.self, from: data) + } catch { + XCTFail("Decoding \(object) failed with error: \(error)") + return + } + + XCTAssertEqual(object, decodedObject) + } + + func assertTwoWayPlistCodable(_ object: Object) { + + let encoder = PropertyListEncoder() + let decoder = PropertyListDecoder() + + // Object -> .plist + + let data: Data + do { + data = try encoder.encode(object) + } catch { + XCTFail("Encoding \(object) failed with error: \(error)") + return + } + + // .plist -> Object + + let decodedObject: Object + do { + decodedObject = try decoder.decode(Object.self, from: data) + } catch { + XCTFail("Decoding \(object) failed with error: \(error)") + return + } + + XCTAssertEqual(object, decodedObject) + } + +} diff --git a/QuizTrainTests/Testing Protocols/Tests/CodableTests.swift b/QuizTrainTests/Testing Protocols/Tests/CodableTests.swift new file mode 100644 index 0000000..9741725 --- /dev/null +++ b/QuizTrainTests/Testing Protocols/Tests/CodableTests.swift @@ -0,0 +1,19 @@ +import XCTest + +protocol CodableTests { + + func testCodable() + func _testCodable() + +} + +extension CodableTests where Self: AssertCodable & ObjectProvider, Self.Object: Codable & Equatable { + + func _testCodable() { + assertTwoWayJSONCodable(objectWithRequiredProperties) + assertTwoWayPlistCodable(objectWithRequiredProperties) + assertTwoWayJSONCodable(objectWithRequiredAndOptionalProperties) + assertTwoWayPlistCodable(objectWithRequiredAndOptionalProperties) + } + +} diff --git a/README.md b/README.md index a770782..9a261f0 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ QuizTrain is open source software released under the MIT License. See the [LICEN [Carthage](https://github.com/Carthage/Carthage) is the recommended way to install QuizTrain. Add the following to your `Cartfile` or `Cartfile.private` file: - github "venmo/QuizTrain" ~> 1.2.0 + github "venmo/QuizTrain" ~> 1.2.1 See [Adding frameworks to an application](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) for further instructions. Once complete `import QuizTrain` in any Swift files you wish to use QuizTrain in.