diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 438ef9d..fe496a8 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Danger run: | - git clone https://github.com/MaatheusGois/DangerSwift && rm -rf DangerSwift/.git + git clone https://github.com/MaatheusGois/DangerSwift && rm -rf DangerSwift/.git Readme.md mv DangerSwift/* . - name: Test Stage diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..0beddc5 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,98 @@ +included: + - Plugins + - Source + - Tests + +excluded: + - Tests + +analyzer_rules: + - unused_declaration + - unused_import + +opt_in_rules: + - all + +disabled_rules: + - anonymous_argument_in_multiline_closure + - anyobject_protocol + - closure_body_length + - conditional_returns_on_newline + - convenience_type + - discouraged_optional_collection + - explicit_acl + - explicit_enum_raw_value + - explicit_top_level_acl + - explicit_type_interface + - file_types_order + - final_test_case + - force_unwrapping + - function_default_parameter_at_end + - implicit_return + - implicitly_unwrapped_optional + - indentation_width + - inert_defer + - missing_docs + - multiline_arguments + - multiline_arguments_brackets + - multiline_function_chains + - multiline_literal_brackets + - multiline_parameters + - multiline_parameters_brackets + - no_extension_access_modifier + - no_fallthrough_only + - no_grouping_extension + - no_magic_numbers + - one_declaration_per_file + - prefer_nimble + - prefer_self_in_static_references + - prefixed_toplevel_constant + - redundant_self_in_closure + - required_deinit + - self_binding + - static_over_final_class + - shorthand_argument + - sorted_enum_cases + - strict_fileprivate + - switch_case_on_newline + - todo + - trailing_closure + - type_contents_order + - unused_capture_list + - vertical_whitespace_between_cases + +attributes: + always_on_line_above: + - "@ConfigurationElement" + - "@OptionGroup" + - "@RuleConfigurationDescriptionBuilder" + +identifier_name: + excluded: + - id +large_tuple: 3 + +number_separator: + minimum_length: 5 + +file_name: + excluded: + - Exports.swift + - GeneratedTests.swift + - RuleConfigurationMacros.swift + - SwiftSyntax+SwiftLint.swift + - TestHelpers.swift + +unneeded_override: + affect_initializers: true + +balanced_xctest_lifecycle: &unit_test_configuration + test_parent_classes: + - SwiftLintTestCase + - XCTestCase + +empty_xctest_method: *unit_test_configuration +single_test_class: *unit_test_configuration + +function_body_length: 60 +type_body_length: 400 diff --git a/Dangerfile.swift b/Dangerfile.swift new file mode 100644 index 0000000..c2fe141 --- /dev/null +++ b/Dangerfile.swift @@ -0,0 +1,223 @@ +// MARK: Imports + +import Danger +import DangerSwiftCoverage +import DangerXCodeSummary +import Foundation + +// MARK: Validate + +Validator.shared.validate() + +// MARK: Lint + +SwiftLint.lint(configFile: ".swiftlint.yml") + +// MARK: Validation rules + +internal class Validator { + // MARK: Lifecycle + // Private initializer and shared instance for Validator. + + private init() {} + internal static let shared = Validator() + private var danger = Danger() + + // MARK: Properties + // Properties related to PR details and changes. + + private lazy var additions = danger.github.pullRequest.additions! + private lazy var deletions = danger.github.pullRequest.deletions! + private lazy var changedFiles = danger.github.pullRequest.changedFiles! + + private lazy var modified = danger.git.modifiedFiles + private lazy var editedFiles = modified + danger.git.createdFiles + private lazy var prTitle = danger.github.pullRequest.title + + private lazy var branchHeadName = danger.github.pullRequest.head.ref + private lazy var branchBaseName = danger.github.pullRequest.base.ref + + // Methods + // Methods for various validation checks. + + internal func validate() { + checkSize() + checkDescription() + checkUnitTest() + checkTitle() + checkAssignee() + checkModifiedFiles() + checkFails() + + logResume() + } +} + +internal class DescriptionValidator { + // MARK: Lifecycle + // Private initializer and shared instance for DescriptionValidator. + + private init() {} + internal static let shared = DescriptionValidator() + private var danger = Danger() + + // MARK: Properties + // Property to store the PR body. + + private lazy var body = danger.github.pullRequest.body ?? "" + + // Methods + // Method to validate PR description. + + internal func validate() { + let message = "PR does not have a description. You must provide a description of the changes made." + + guard !body.isEmpty else { + return fail(message) + } + } +} + +internal class UnitTestValidator { + // MARK: Lifecycle + // Private initializer and shared instance for UnitTestValidator. + + private init() {} + internal static let shared = UnitTestValidator() + private var danger = Danger() + + // Methods + // Methods for unit test validation. + + internal func validate() { + checkUnitTestSummary() + checkUnitTestCoverage() + } +} + +// MARK: Validator Methods +// Extension with methods for Validator class. + +fileprivate extension Validator { + func checkSize() { + if (additions + deletions) > ValidationRules.bigPRThreshold { + let message = + """ + The size of the PR seems relatively large. \ + If possible, in the future if the PR contains multiple changes, split each into a separate PR. \ + This helps in faster and easier review. + """ + warn(message) + } + } + + func checkDescription() { + DescriptionValidator.shared.validate() + } + + func checkUnitTest() { + UnitTestValidator.shared.validate() + } + + func checkTitle() { + let result = prTitle.range( + of: #"\[[A-zÀ-ú0-9 ]*\][A-zÀ-ú0-9- ]+"#, + options: .regularExpression + ) != nil + + if !result { + let message = "The PR title should be: [Feature or Flow] What flow was done" + warn(message) + } + } + + func checkAssignee() { + if danger.github.pullRequest.assignee == nil { + warn("Please assign yourself to the PR.") + } + } + + func checkModifiedFiles() { + if changedFiles > ValidationRules.maxChangedFiles { + let message = + """ + PR contains too many changed files. If possible, next time try to split into smaller features. + """ + warn(message) + } + } + + func checkFails() { + if !danger.fails.isEmpty { + _ = danger.utils.exec("touch Danger-has-fails.swift") + } + } + + func logResume() { + let overview = + """ + The PR added \(additions) and removed \(deletions) lines. \(changedFiles) file(s) changed. + """ + + let seeOurDocumentation = + """ + Example Link:
\ + \ + Link + """ + + message(seeOurDocumentation) + message(overview) + } +} + +// MARK: Constants +// Constants related to validation rules. + +private enum ValidationRules { + static let maxChangedFiles = 20 + static let bigPRThreshold = 3000 +} + +// MARK: Extensions +// Extension with additional file-related methods. + +fileprivate extension Danger.File { + var isInSources: Bool { hasPrefix("Sources/") } + var isInTests: Bool { hasPrefix("Tests/") } + + var isSourceFile: Bool { + hasSuffix(".swift") || hasSuffix(".h") || hasSuffix(".m") + } + + var isSwiftPackageDefintion: Bool { + hasPrefix("Package") && hasSuffix(".swift") + } + + var isDangerfile: Bool { + self == "Dangerfile.swift" + } +} + +// MARK: UnitTestValidator Methods +// Extension with methods for UnitTestValidator class. + +fileprivate extension UnitTestValidator { + func checkUnitTestSummary() { + let file = "build/reports/errors.json" + if FileManager.default.fileExists(atPath: file) { + let summary = XCodeSummary(filePath: file) { result in + result.category != .warning + } + summary.report() + } + } + + func checkUnitTestCoverage() { + Coverage.xcodeBuildCoverage( + .xcresultBundle("fastlane/test_output/Example.xcresult"), + minimumCoverage: 70, + excludedTargets: ["DangerSwiftCoverageTests.xctest"] + ) + } +} diff --git a/Example.xcodeproj/project.pbxproj b/Example.xcodeproj/project.pbxproj index 424c127..45cefb6 100644 --- a/Example.xcodeproj/project.pbxproj +++ b/Example.xcodeproj/project.pbxproj @@ -13,12 +13,11 @@ 2E0B58C52BC046CE00049F34 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E0B58C42BC046CE00049F34 /* Assets.xcassets */; }; 2E0B58C92BC046CE00049F34 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E0B58C82BC046CE00049F34 /* Preview Assets.xcassets */; }; 2E0B58EC2BC0483C00049F34 /* ContentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E0B58EB2BC0483C00049F34 /* ContentViewModel.swift */; }; - 2EC7DFDA2C126860005E58DF /* ViewModel.Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EC7DFC72C1267F1005E58DF /* ViewModel.Tests.swift */; }; - 2EC7DFDB2C126860005E58DF /* Tests+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EC7DFC82C1267F1005E58DF /* Tests+Helpers.swift */; }; + 2EC7DFE42C12726D005E58DF /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EC7DFE32C12726D005E58DF /* Tests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 2EC7DFD52C12685B005E58DF /* PBXContainerItemProxy */ = { + 2EC7DFE52C12726D005E58DF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 2E0B58B32BC046CC00049F34 /* Project object */; proxyType = 1; @@ -36,9 +35,9 @@ 2E0B58C62BC046CE00049F34 /* Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example.entitlements; sourceTree = ""; }; 2E0B58C82BC046CE00049F34 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 2E0B58EB2BC0483C00049F34 /* ContentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewModel.swift; sourceTree = ""; }; - 2EC7DFC72C1267F1005E58DF /* ViewModel.Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModel.Tests.swift; sourceTree = ""; }; - 2EC7DFC82C1267F1005E58DF /* Tests+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Tests+Helpers.swift"; sourceTree = ""; }; - 2EC7DFD12C12685B005E58DF /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 2EC7DFE12C12726D005E58DF /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 2EC7DFE32C12726D005E58DF /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; + 2EC7DFEA2C1272BA005E58DF /* Tests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Tests.xctestplan; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,7 +48,7 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2EC7DFCE2C12685B005E58DF /* Frameworks */ = { + 2EC7DFDE2C12726D005E58DF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -63,7 +62,7 @@ isa = PBXGroup; children = ( 2E0B58BD2BC046CC00049F34 /* Example */, - 2EC7DFD22C12685B005E58DF /* Tests */, + 2EC7DFE22C12726D005E58DF /* Tests */, 2E0B58BC2BC046CC00049F34 /* Products */, ); sourceTree = ""; @@ -72,7 +71,7 @@ isa = PBXGroup; children = ( 2E0B58BB2BC046CC00049F34 /* Example.app */, - 2EC7DFD12C12685B005E58DF /* Tests.xctest */, + 2EC7DFE12C12726D005E58DF /* Tests.xctest */, ); name = Products; sourceTree = ""; @@ -107,11 +106,11 @@ path = Content; sourceTree = ""; }; - 2EC7DFD22C12685B005E58DF /* Tests */ = { + 2EC7DFE22C12726D005E58DF /* Tests */ = { isa = PBXGroup; children = ( - 2EC7DFC72C1267F1005E58DF /* ViewModel.Tests.swift */, - 2EC7DFC82C1267F1005E58DF /* Tests+Helpers.swift */, + 2EC7DFEA2C1272BA005E58DF /* Tests.xctestplan */, + 2EC7DFE32C12726D005E58DF /* Tests.swift */, ); path = Tests; sourceTree = ""; @@ -136,22 +135,22 @@ productReference = 2E0B58BB2BC046CC00049F34 /* Example.app */; productType = "com.apple.product-type.application"; }; - 2EC7DFD02C12685B005E58DF /* Tests */ = { + 2EC7DFE02C12726D005E58DF /* Tests */ = { isa = PBXNativeTarget; - buildConfigurationList = 2EC7DFD72C12685B005E58DF /* Build configuration list for PBXNativeTarget "Tests" */; + buildConfigurationList = 2EC7DFE72C12726D005E58DF /* Build configuration list for PBXNativeTarget "Tests" */; buildPhases = ( - 2EC7DFCD2C12685B005E58DF /* Sources */, - 2EC7DFCE2C12685B005E58DF /* Frameworks */, - 2EC7DFCF2C12685B005E58DF /* Resources */, + 2EC7DFDD2C12726D005E58DF /* Sources */, + 2EC7DFDE2C12726D005E58DF /* Frameworks */, + 2EC7DFDF2C12726D005E58DF /* Resources */, ); buildRules = ( ); dependencies = ( - 2EC7DFD62C12685B005E58DF /* PBXTargetDependency */, + 2EC7DFE62C12726D005E58DF /* PBXTargetDependency */, ); name = Tests; productName = Tests; - productReference = 2EC7DFD12C12685B005E58DF /* Tests.xctest */; + productReference = 2EC7DFE12C12726D005E58DF /* Tests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -167,7 +166,7 @@ 2E0B58BA2BC046CC00049F34 = { CreatedOnToolsVersion = 15.3; }; - 2EC7DFD02C12685B005E58DF = { + 2EC7DFE02C12726D005E58DF = { CreatedOnToolsVersion = 15.4; TestTargetID = 2E0B58BA2BC046CC00049F34; }; @@ -187,7 +186,7 @@ projectRoot = ""; targets = ( 2E0B58BA2BC046CC00049F34 /* Example */, - 2EC7DFD02C12685B005E58DF /* Tests */, + 2EC7DFE02C12726D005E58DF /* Tests */, ); }; /* End PBXProject section */ @@ -202,7 +201,7 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2EC7DFCF2C12685B005E58DF /* Resources */ = { + 2EC7DFDF2C12726D005E58DF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -223,22 +222,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2EC7DFCD2C12685B005E58DF /* Sources */ = { + 2EC7DFDD2C12726D005E58DF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2EC7DFDA2C126860005E58DF /* ViewModel.Tests.swift in Sources */, - 2EC7DFDB2C126860005E58DF /* Tests+Helpers.swift in Sources */, + 2EC7DFE42C12726D005E58DF /* Tests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 2EC7DFD62C12685B005E58DF /* PBXTargetDependency */ = { + 2EC7DFE62C12726D005E58DF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 2E0B58BA2BC046CC00049F34 /* Example */; - targetProxy = 2EC7DFD52C12685B005E58DF /* PBXContainerItemProxy */; + targetProxy = 2EC7DFE52C12726D005E58DF /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -301,6 +299,7 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -357,6 +356,7 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; @@ -366,9 +366,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Example/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; @@ -388,12 +390,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.maatheusgois.Example; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -402,9 +405,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Example/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; @@ -424,53 +429,56 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.maatheusgois.Example; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; - 2EC7DFD82C12685B005E58DF /* Debug */ = { + 2EC7DFE82C12726D005E58DF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.maatheusgois.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Example"; }; name = Debug; }; - 2EC7DFD92C12685B005E58DF /* Release */ = { + 2EC7DFE92C12726D005E58DF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.maatheusgois.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Example"; }; name = Release; @@ -485,7 +493,7 @@ 2E0B58E12BC046CF00049F34 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; 2E0B58E22BC046CF00049F34 /* Build configuration list for PBXNativeTarget "Example" */ = { isa = XCConfigurationList; @@ -494,16 +502,16 @@ 2E0B58E42BC046CF00049F34 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; - 2EC7DFD72C12685B005E58DF /* Build configuration list for PBXNativeTarget "Tests" */ = { + 2EC7DFE72C12726D005E58DF /* Build configuration list for PBXNativeTarget "Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2EC7DFD82C12685B005E58DF /* Debug */, - 2EC7DFD92C12685B005E58DF /* Release */, + 2EC7DFE82C12726D005E58DF /* Debug */, + 2EC7DFE92C12726D005E58DF /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; /* End XCConfigurationList section */ }; diff --git a/Example.xcodeproj/project.xcworkspace/xcuserdata/goisma.xcuserdatad/UserInterfaceState.xcuserstate b/Example.xcodeproj/project.xcworkspace/xcuserdata/goisma.xcuserdatad/UserInterfaceState.xcuserstate index a99c4c7..207f882 100644 Binary files a/Example.xcodeproj/project.xcworkspace/xcuserdata/goisma.xcuserdatad/UserInterfaceState.xcuserstate and b/Example.xcodeproj/project.xcworkspace/xcuserdata/goisma.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme b/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme index 2ef4d6a..e7a6c0e 100644 --- a/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme +++ b/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme @@ -40,7 +40,7 @@ parallelizable = "YES"> @@ -51,18 +51,7 @@ parallelizable = "YES"> - - - - @@ -97,6 +86,15 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> + + + + diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..b2225ff --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 MaatheusGois + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe59365 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# DangerSwift Actions + +image + +# How to setup the code in your repo + +## 1. Create the Secrets + +Create this: + +image + +With this permission: + +image + +### Environment + +`TOKEN` + +Named by the git of `GITHUB_TOKEN` + +### Repository secrets +`DANGER_GITHUB_API_TOKEN` + +Screenshot 2024-06-06 at 20 02 52 +Screenshot 2024-06-06 at 20 03 59 + +--- + +## 2. Create or copy from the example to your github: + +- `.github/workflows/danger.yml` +- `.swiftlint.yml` +- `Dangerfile.swift` + +## 3. Contribute +We welcome contributions to improve this project! To contribute, please follow these steps: + +1. Fork the repository. +2. Create a new branch for your feature or bug fix. +3. Make your changes and commit them. +4. Push your changes to your forked repository. +5. Open a pull request to the main repository. + +Please ensure that your code follows the project's coding conventions and includes appropriate tests. + +## 4. License +This project is licensed under the MIT License. You can find the full license text in the [LICENSE](https://github.com/MaatheusGois/DangerSwiftExample/blob/main/LICENSE) file. + diff --git a/Tests/Tests+Helpers.swift b/Tests/Tests+Helpers.swift deleted file mode 100644 index d71207e..0000000 --- a/Tests/Tests+Helpers.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Tests.Helpers.swift -// Example -// -// Created by Matheus Gois on 05/04/24. -// - -import XCTest -import SwiftData -@testable import Example - -protocol ViewModelTestable { - init( - modelContext: ModelContext - ) -} - -extension XCTestCase { - @MainActor func make( - viewModel: T.Type - ) throws -> T { - let config = ModelConfiguration( - isStoredInMemoryOnly: true - ) - let container = try ModelContainer( - for: Item.self, - configurations: config - ) - return T( - modelContext: container.mainContext - ) - } -} diff --git a/Tests/Tests.swift b/Tests/Tests.swift index 6b1d5bd..c2e8039 100644 --- a/Tests/Tests.swift +++ b/Tests/Tests.swift @@ -6,30 +6,90 @@ // import XCTest +import SwiftData +@testable import Example -final class Tests: XCTestCase { +extension ContentView.ViewModel: ViewModelTestable {} - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } +final class ViewModelTests: XCTestCase { + @MainActor func testAppStartsEmpty() throws { + // Given + let sut = try make( + viewModel: ContentView.ViewModel.self + ) - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. + // When & Then + XCTAssertEqual( + sut.items.count, + 0, + "There should be 0 items when the app is first launched." + ) } - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + @MainActor func testCreatingSamplesWorks() throws { + // Given + let sut = try make( + viewModel: ContentView.ViewModel.self + ) + + // When + sut.addItem() + sut.addItem() + sut.addItem() + + // Then + XCTAssertEqual( + sut.items.count, + 3, + "There should be 3 items after adding sample data." + ) } - func testPerformanceExample() throws { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. - } + @MainActor func testCreaatingAndRemoving() throws { + // Given + let sut = try make( + viewModel: ContentView.ViewModel.self + ) + + // When + sut.addItem() + sut.addItem() + sut.deleteItems( + offsets: .init( + integer: 0 + ) + ) + + // Then + XCTAssertEqual( + sut.items.count, + 1, + "There should be 1 items after adding sample data." + ) } +} + +// MARK: - Helpers +protocol ViewModelTestable { + init( + modelContext: ModelContext + ) +} + +extension XCTestCase { + @MainActor func make( + viewModel: T.Type + ) throws -> T { + let config = ModelConfiguration( + isStoredInMemoryOnly: true + ) + let container = try ModelContainer( + for: Item.self, + configurations: config + ) + return T( + modelContext: container.mainContext + ) + } } diff --git a/Tests/Tests.xctestplan b/Tests/Tests.xctestplan new file mode 100644 index 0000000..1f4315e --- /dev/null +++ b/Tests/Tests.xctestplan @@ -0,0 +1,25 @@ +{ + "configurations" : [ + { + "id" : "C1740AFA-F6BF-4690-8C40-DD3519A8467B", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + "testTimeoutsEnabled" : true + }, + "testTargets" : [ + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:Example.xcodeproj", + "identifier" : "2EC7DFE02C12726D005E58DF", + "name" : "Tests" + } + } + ], + "version" : 1 +} diff --git a/Tests/ViewModel.Tests.swift b/Tests/ViewModel.Tests.swift deleted file mode 100644 index 9d2c77a..0000000 --- a/Tests/ViewModel.Tests.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// Tests_CITests.swift -// Example -// -// Created by Matheus Gois on 05/04/24. -// - -import XCTest -import SwiftData -@testable import Example - -extension ContentView.ViewModel: ViewModelTestable {} - -final class ViewModelTests: XCTestCase { - @MainActor func testAppStartsEmpty() throws { - // Given - let sut = try make( - viewModel: ContentView.ViewModel.self - ) - - // When & Then - XCTAssertEqual( - sut.items.count, - 0, - "There should be 0 items when the app is first launched." - ) - } - - @MainActor func testCreatingSamplesWorks() throws { - // Given - let sut = try make( - viewModel: ContentView.ViewModel.self - ) - - // When - sut.addItem() - sut.addItem() - sut.addItem() - - // Then - XCTAssertEqual( - sut.items.count, - 3, - "There should be 3 items after adding sample data." - ) - } - - @MainActor func testCreaatingAndRemoving() throws { - // Given - let sut = try make( - viewModel: ContentView.ViewModel.self - ) - - // When - sut.addItem() - sut.addItem() - sut.deleteItems( - offsets: .init( - integer: 0 - ) - ) - - // Then - XCTAssertEqual( - sut.items.count, - 1, - "There should be 1 items after adding sample data." - ) - } -}