From 7ba50b6f030b8a05d4fd2bd47e00018ea11cf201 Mon Sep 17 00:00:00 2001 From: Blazej SLEBODA Date: Mon, 21 Sep 2020 21:40:03 +0200 Subject: [PATCH 1/3] Add UseTraitCollections rule --- Rules.md | 8 +++ Sources/IBLinterKit/Config/Config.swift | 6 ++ .../Config/UseTraitCollectionsConfig.swift | 25 +++++++ Sources/IBLinterKit/Rule.swift | 1 + .../Rules/UseTraitCollections.swift | 57 ++++++++++++++++ .../UseTraitCollectionsNoTest.storyboard | 8 +++ .../UseTraitCollectionsYesTest.storyboard | 8 +++ .../Rules/UseTraitCollectionsTests.swift | 65 +++++++++++++++++++ 8 files changed, 178 insertions(+) create mode 100644 Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift create mode 100644 Sources/IBLinterKit/Rules/UseTraitCollections.swift create mode 100644 Tests/IBLinterKitTest/Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsNoTest.storyboard create mode 100644 Tests/IBLinterKitTest/Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsYesTest.storyboard create mode 100644 Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift diff --git a/Rules.md b/Rules.md index c6f5e6d1..53b83b62 100644 --- a/Rules.md +++ b/Rules.md @@ -131,3 +131,11 @@ Check that ReuseIdentifier same as class name. | Default Rule | `false` | Check if named color resources are valid. + +## UseTraitCollectionsRule + +| identifier | `use_trait_collections` | +|:---------------:|:---------------:| +| Default Rule | `false` | + +Check if a document useTraitCollections is enabled or disabled diff --git a/Sources/IBLinterKit/Config/Config.swift b/Sources/IBLinterKit/Config/Config.swift index c398670a..aef44338 100644 --- a/Sources/IBLinterKit/Config/Config.swift +++ b/Sources/IBLinterKit/Config/Config.swift @@ -16,6 +16,7 @@ public struct Config: Codable { public let customModuleRule: [CustomModuleConfig] public let useBaseClassRule: [UseBaseClassConfig] public let viewAsDeviceRule: ViewAsDeviceConfig? + public let useTraitCollectionsRule: UseTraitCollectionsConfig? public let reporter: String public let disableWhileBuildingForIB: Bool public let ignoreCache: Bool @@ -28,6 +29,7 @@ public struct Config: Codable { case customModuleRule = "custom_module_rule" case useBaseClassRule = "use_base_class_rule" case viewAsDeviceRule = "view_as_device_rule" + case useTraitCollectionsRule = "use_trait_collections_rule" case reporter = "reporter" case disableWhileBuildingForIB = "disable_while_building_for_ib" case ignoreCache = "ignore_cache" @@ -44,6 +46,7 @@ public struct Config: Codable { customModuleRule = [] useBaseClassRule = [] viewAsDeviceRule = nil + useTraitCollectionsRule = nil reporter = "xcode" disableWhileBuildingForIB = true ignoreCache = false @@ -54,6 +57,7 @@ public struct Config: Codable { customModuleRule: [CustomModuleConfig] = [], baseClassRule: [UseBaseClassConfig] = [], viewAsDeviceRule: ViewAsDeviceConfig? = nil, + useTraitCollectionsRule: UseTraitCollectionsConfig? = nil, reporter: String = "xcode", disableWhileBuildingForIB: Bool = true, ignoreCache: Bool = false) { self.disabledRules = disabledRules @@ -62,6 +66,7 @@ public struct Config: Codable { self.included = included self.customModuleRule = customModuleRule self.useBaseClassRule = baseClassRule + self.useTraitCollectionsRule = useTraitCollectionsRule self.viewAsDeviceRule = viewAsDeviceRule self.reporter = reporter self.disableWhileBuildingForIB = disableWhileBuildingForIB @@ -76,6 +81,7 @@ public struct Config: Codable { included = try container.decodeIfPresent(Optional<[String]>.self, forKey: .included).flatMap { $0 } ?? [] customModuleRule = try container.decodeIfPresent(Optional<[CustomModuleConfig]>.self, forKey: .customModuleRule).flatMap { $0 } ?? [] useBaseClassRule = try container.decodeIfPresent(Optional<[UseBaseClassConfig]>.self, forKey: .useBaseClassRule)?.flatMap { $0 } ?? [] + useTraitCollectionsRule = try container.decodeIfPresent(Optional.self, forKey: .useTraitCollectionsRule) ?? nil viewAsDeviceRule = try container.decodeIfPresent(Optional.self, forKey: .viewAsDeviceRule) ?? nil reporter = try container.decodeIfPresent(String.self, forKey: .reporter) ?? "xcode" disableWhileBuildingForIB = try container.decodeIfPresent(Bool.self, forKey: .disableWhileBuildingForIB) ?? true diff --git a/Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift b/Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift new file mode 100644 index 00000000..93ee4889 --- /dev/null +++ b/Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift @@ -0,0 +1,25 @@ +// +// UseTraitCollectionsConfig.swift +// IBLinterKit +// +// Created by Blazej SLEBODA on 13/09/2020 +// + +import Foundation + +public struct UseTraitCollectionsConfig: Codable { + public let useTraitCollections: Bool + + enum CodingKeys: String, CodingKey { + case useTraitCollections = "use_trait_collections" + } + + init(useTraitCollections: Bool) { + self.useTraitCollections = useTraitCollections + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + useTraitCollections = try container.decode(Bool.self, forKey: .useTraitCollections) + } +} diff --git a/Sources/IBLinterKit/Rule.swift b/Sources/IBLinterKit/Rule.swift index aebedcfc..ce7cb287 100644 --- a/Sources/IBLinterKit/Rule.swift +++ b/Sources/IBLinterKit/Rule.swift @@ -36,6 +36,7 @@ public struct Rules { CustomModuleRule.self, UseBaseClassRule.self, AmbiguousViewRule.self, + UseTraitCollections.self, ViewAsDeviceRule.self, ReuseIdentifierRule.self, ColorResourcesRule.self diff --git a/Sources/IBLinterKit/Rules/UseTraitCollections.swift b/Sources/IBLinterKit/Rules/UseTraitCollections.swift new file mode 100644 index 00000000..5502fc61 --- /dev/null +++ b/Sources/IBLinterKit/Rules/UseTraitCollections.swift @@ -0,0 +1,57 @@ +// +// UseTraitCollections.swift +// IBLinterKit +// +// Created by Blazej SLEBODA on 13/09/2020 +// + +import IBDecodable + +extension Rules { + + struct UseTraitCollections: Rule { + + static var identifier = "use_trait_collections" + static var description = "Check id document useTraitCollections is enabled or diasbled" + let useTraitCollections: Bool + + init(context: Context) { + if let configUseTraitCollectionRule = context.config.useTraitCollectionsRule { + useTraitCollections = configUseTraitCollectionRule.useTraitCollections + } else { + useTraitCollections = true + } + } + + func validate(storyboard: StoryboardFile) -> [Violation] { + Rules.violation(useTraitCollections, file: storyboard) + } + + func validate(xib: XibFile) -> [Violation] { + Rules.violation(useTraitCollections, file: xib) + } + + } + + fileprivate static func violation(_ useTraitCollection: Bool, file: T) -> [Violation] { + + guard let document = file.document as? InterfaceBuilderDocument else { + return [] + } + + if useTraitCollection { + if document.useTraitCollections == .some(true) { + return [] + } else { + return [Violation(pathString: file.pathString, message: "useTraitCollection must be enabled", level: .warning)] + } + } else { + if [Bool?(false), .none].contains(document.useTraitCollections) { + return [] + } else { + return [Violation(pathString: file.pathString, message: "useTraitCollection must be disabled", level: .warning)] + } + } + } + +} diff --git a/Tests/IBLinterKitTest/Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsNoTest.storyboard b/Tests/IBLinterKitTest/Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsNoTest.storyboard new file mode 100644 index 00000000..2efc525a --- /dev/null +++ b/Tests/IBLinterKitTest/Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsNoTest.storyboard @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/IBLinterKitTest/Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsYesTest.storyboard b/Tests/IBLinterKitTest/Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsYesTest.storyboard new file mode 100644 index 00000000..09784c37 --- /dev/null +++ b/Tests/IBLinterKitTest/Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsYesTest.storyboard @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift b/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift new file mode 100644 index 00000000..90158579 --- /dev/null +++ b/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift @@ -0,0 +1,65 @@ +// +// UseTraitCollectionsTests.swift +// IBLinterKitTest +// +// Created by Blazej SLEBODA on 13/09/2020 +// + +@testable import IBLinterKit +import XCTest +import IBDecodable + +class UseTraitCollectionsTests: XCTestCase { + + let fixture = Fixture() + + var noUrl: URL! + var yesUrl: URL! + + override func setUp() { + super.setUp() + noUrl = fixture.path("Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsNoTest.storyboard") + yesUrl = fixture.path("Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsYesTest.storyboard") + } + + func testUseTraitCollectionsWithEmptyConfig() { + let contextMock = Context.mock(from: .default) + XCTAssertNil(contextMock.config.useTraitCollectionsRule) + XCTAssertFalse(Rules.UseTraitCollections.isDefault) + } + + func testUseTraitCollectionsEnabledWithDefaults() { + let defaultContext = Context.mock(from: Config.init(enabledRules: [Rules.UseTraitCollections.identifier])) + let ngRule = Rules.UseTraitCollections(context: defaultContext) + let ngViolations = try! ngRule.validate(xib: XibFile(url: noUrl)) + XCTAssertEqual(ngViolations.count, 1) + + let okConfig = Config(useTraitCollectionsRule: UseTraitCollectionsConfig(useTraitCollections: true)) + let okRule = Rules.UseTraitCollections(context: .mock(from: okConfig)) + let okViolations = try! okRule.validate(xib: XibFile(url: yesUrl)) + XCTAssertTrue(okViolations.isEmpty) + } + + func testUseTraitCollectionsEnabledWithTrue() { + let ngConfig = Config(useTraitCollectionsRule: UseTraitCollectionsConfig(useTraitCollections: true)) + let ngRule = Rules.UseTraitCollections(context: .mock(from: ngConfig)) + let ngViolations = try! ngRule.validate(xib: XibFile(url: noUrl)) + XCTAssertEqual(ngViolations.count, 1) + + let okConfig = Config(useTraitCollectionsRule: UseTraitCollectionsConfig(useTraitCollections: true)) + let okRule = Rules.UseTraitCollections(context: .mock(from: okConfig)) + let okViolations = try! okRule.validate(xib: XibFile(url: yesUrl)) + XCTAssertTrue(okViolations.isEmpty) + } + + func testUseTraitCollectionsEnabledWithFalse() { + let config = Config(useTraitCollectionsRule: .some(.init(useTraitCollections: false))) + let ngRule = Rules.UseTraitCollections(context: .mock(from: config)) + let ngViolations = try! ngRule.validate(xib: XibFile(url: noUrl)) + XCTAssertTrue(ngViolations.isEmpty) + + let okRule = Rules.UseTraitCollections(context: .mock(from: config)) + let okViolations = try! okRule.validate(xib: XibFile(url: yesUrl)) + XCTAssertEqual(okViolations.count, 1) + } +} From 188a23225b084b845be962265ce4c439c0b5fbf8 Mon Sep 17 00:00:00 2001 From: Blazej SLEBODA Date: Tue, 22 Sep 2020 23:44:11 +0200 Subject: [PATCH 2/3] Update README and refactor a tests --- README.md | 11 +++ .../Rules/UseTraitCollectionsTests.swift | 71 +++++++++---------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index ac6e6fda..17b91c6c 100644 --- a/README.md +++ b/README.md @@ -140,10 +140,19 @@ You can configure `view_as_device` rule by `ViewAsDeviceConfig`. If there are no | `iPhone XR` | `retina6_1` | | `iPhone XS Max` | `retina6_5` | +### UseTraitCollectionsConfig + +You can configure `use_trait_collections rule by `UseTraitCollectionsConfig`. If there is no config then use_trait_collections is set to true + +| key | description | +|:---------------:|:---------------:| +| use_trait_collections | `true` | + ```yaml enabled_rules: - relative_to_margin + - use_trait_collections disabled_rules: - custom_class_name excluded: @@ -164,4 +173,6 @@ use_base_class_rule: - SecondaryLabel view_as_device_rule: device_id: retina4_0 +use_trait_collections_rule: + use_trait_collections: true ``` diff --git a/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift b/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift index 90158579..485a7ab2 100644 --- a/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift +++ b/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift @@ -13,53 +13,52 @@ class UseTraitCollectionsTests: XCTestCase { let fixture = Fixture() - var noUrl: URL! - var yesUrl: URL! + var storyboardUseTraitCollectionsYes: StoryboardFile! + var storyboardUseTraitCollectionsNo: StoryboardFile! override func setUp() { super.setUp() - noUrl = fixture.path("Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsNoTest.storyboard") - yesUrl = fixture.path("Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsYesTest.storyboard") - } - - func testUseTraitCollectionsWithEmptyConfig() { - let contextMock = Context.mock(from: .default) - XCTAssertNil(contextMock.config.useTraitCollectionsRule) - XCTAssertFalse(Rules.UseTraitCollections.isDefault) + + let disabledUrl = fixture.path("Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsNoTest.storyboard") + let enabledUrl = fixture.path("Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsYesTest.storyboard") + + storyboardUseTraitCollectionsYes = try! .init(url: enabledUrl) + storyboardUseTraitCollectionsNo = try! .init(url: disabledUrl) + } - func testUseTraitCollectionsEnabledWithDefaults() { - let defaultContext = Context.mock(from: Config.init(enabledRules: [Rules.UseTraitCollections.identifier])) - let ngRule = Rules.UseTraitCollections(context: defaultContext) - let ngViolations = try! ngRule.validate(xib: XibFile(url: noUrl)) - XCTAssertEqual(ngViolations.count, 1) + func testUseTraitCollectionsNoConfig() { + let ibLinterConfig = Config.default + let rule = Rules.UseTraitCollections(context: .mock(from: ibLinterConfig)) + + let noViolations = rule.validate(storyboard: storyboardUseTraitCollectionsNo) + XCTAssertEqual(noViolations.count, 1) - let okConfig = Config(useTraitCollectionsRule: UseTraitCollectionsConfig(useTraitCollections: true)) - let okRule = Rules.UseTraitCollections(context: .mock(from: okConfig)) - let okViolations = try! okRule.validate(xib: XibFile(url: yesUrl)) - XCTAssertTrue(okViolations.isEmpty) + let yesViolations = rule.validate(storyboard: storyboardUseTraitCollectionsYes) + XCTAssertTrue(yesViolations.isEmpty) } - func testUseTraitCollectionsEnabledWithTrue() { - let ngConfig = Config(useTraitCollectionsRule: UseTraitCollectionsConfig(useTraitCollections: true)) - let ngRule = Rules.UseTraitCollections(context: .mock(from: ngConfig)) - let ngViolations = try! ngRule.validate(xib: XibFile(url: noUrl)) - XCTAssertEqual(ngViolations.count, 1) + func testUseTraitCollectionsConfigWithTrue() { + let useTraitCollectionsConfig = UseTraitCollectionsConfig(useTraitCollections: true) + let ibLinterConfig = Config(useTraitCollectionsRule: useTraitCollectionsConfig) + let rule = Rules.UseTraitCollections(context: .mock(from: ibLinterConfig)) - let okConfig = Config(useTraitCollectionsRule: UseTraitCollectionsConfig(useTraitCollections: true)) - let okRule = Rules.UseTraitCollections(context: .mock(from: okConfig)) - let okViolations = try! okRule.validate(xib: XibFile(url: yesUrl)) - XCTAssertTrue(okViolations.isEmpty) + let noViolations = rule.validate(storyboard: storyboardUseTraitCollectionsNo) + XCTAssertEqual(noViolations.count, 1) + + let yesViolations = rule.validate(storyboard: storyboardUseTraitCollectionsYes) + XCTAssertTrue(yesViolations.isEmpty) } - func testUseTraitCollectionsEnabledWithFalse() { - let config = Config(useTraitCollectionsRule: .some(.init(useTraitCollections: false))) - let ngRule = Rules.UseTraitCollections(context: .mock(from: config)) - let ngViolations = try! ngRule.validate(xib: XibFile(url: noUrl)) - XCTAssertTrue(ngViolations.isEmpty) + func testUseTraitCollectionsConfigWithFalse() { + let useTraitCollectionsConfig = UseTraitCollectionsConfig(useTraitCollections: false) + let ibLinterConfig = Config(useTraitCollectionsRule: useTraitCollectionsConfig) + let rule = Rules.UseTraitCollections(context: .mock(from: ibLinterConfig)) + + let noViolations = rule.validate(storyboard: storyboardUseTraitCollectionsNo) + XCTAssertTrue(noViolations.isEmpty) - let okRule = Rules.UseTraitCollections(context: .mock(from: config)) - let okViolations = try! okRule.validate(xib: XibFile(url: yesUrl)) - XCTAssertEqual(okViolations.count, 1) + let yesViolations = rule.validate(storyboard: storyboardUseTraitCollectionsYes) + XCTAssertEqual(yesViolations.count, 1) } } From bec4f9470e2720aa4b3d13aba0bc122a02f4fc7b Mon Sep 17 00:00:00 2001 From: Blazej SLEBODA Date: Fri, 25 Sep 2020 18:07:01 +0200 Subject: [PATCH 3/3] Rename a config parameter --- README.md | 4 ++-- .../Config/UseTraitCollectionsConfig.swift | 10 +++++----- .../IBLinterKit/Rules/UseTraitCollections.swift | 14 +++++++------- .../Rules/UseTraitCollectionsTests.swift | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 17b91c6c..74a1d74c 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ You can configure `use_trait_collections rule by `UseTraitCollectionsConfig`. If | key | description | |:---------------:|:---------------:| -| use_trait_collections | `true` | +| enabled | `true` | ```yaml @@ -174,5 +174,5 @@ use_base_class_rule: view_as_device_rule: device_id: retina4_0 use_trait_collections_rule: - use_trait_collections: true + enabled: false ``` diff --git a/Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift b/Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift index 93ee4889..547f15d0 100644 --- a/Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift +++ b/Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift @@ -8,18 +8,18 @@ import Foundation public struct UseTraitCollectionsConfig: Codable { - public let useTraitCollections: Bool + public let enabled: Bool enum CodingKeys: String, CodingKey { - case useTraitCollections = "use_trait_collections" + case enabled } - init(useTraitCollections: Bool) { - self.useTraitCollections = useTraitCollections + init(enabled: Bool) { + self.enabled = enabled } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - useTraitCollections = try container.decode(Bool.self, forKey: .useTraitCollections) + enabled = try container.decode(Bool.self, forKey: .enabled) } } diff --git a/Sources/IBLinterKit/Rules/UseTraitCollections.swift b/Sources/IBLinterKit/Rules/UseTraitCollections.swift index 5502fc61..95df96fe 100644 --- a/Sources/IBLinterKit/Rules/UseTraitCollections.swift +++ b/Sources/IBLinterKit/Rules/UseTraitCollections.swift @@ -13,33 +13,33 @@ extension Rules { static var identifier = "use_trait_collections" static var description = "Check id document useTraitCollections is enabled or diasbled" - let useTraitCollections: Bool + let enabled: Bool init(context: Context) { if let configUseTraitCollectionRule = context.config.useTraitCollectionsRule { - useTraitCollections = configUseTraitCollectionRule.useTraitCollections + enabled = configUseTraitCollectionRule.enabled } else { - useTraitCollections = true + enabled = true } } func validate(storyboard: StoryboardFile) -> [Violation] { - Rules.violation(useTraitCollections, file: storyboard) + Rules.violation(enabled, file: storyboard) } func validate(xib: XibFile) -> [Violation] { - Rules.violation(useTraitCollections, file: xib) + Rules.violation(enabled, file: xib) } } - fileprivate static func violation(_ useTraitCollection: Bool, file: T) -> [Violation] { + fileprivate static func violation(_ enabled: Bool, file: T) -> [Violation] { guard let document = file.document as? InterfaceBuilderDocument else { return [] } - if useTraitCollection { + if enabled { if document.useTraitCollections == .some(true) { return [] } else { diff --git a/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift b/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift index 485a7ab2..1ecebc54 100644 --- a/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift +++ b/Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift @@ -39,7 +39,7 @@ class UseTraitCollectionsTests: XCTestCase { } func testUseTraitCollectionsConfigWithTrue() { - let useTraitCollectionsConfig = UseTraitCollectionsConfig(useTraitCollections: true) + let useTraitCollectionsConfig = UseTraitCollectionsConfig(enabled: true) let ibLinterConfig = Config(useTraitCollectionsRule: useTraitCollectionsConfig) let rule = Rules.UseTraitCollections(context: .mock(from: ibLinterConfig)) @@ -51,7 +51,7 @@ class UseTraitCollectionsTests: XCTestCase { } func testUseTraitCollectionsConfigWithFalse() { - let useTraitCollectionsConfig = UseTraitCollectionsConfig(useTraitCollections: false) + let useTraitCollectionsConfig = UseTraitCollectionsConfig(enabled: false) let ibLinterConfig = Config(useTraitCollectionsRule: useTraitCollectionsConfig) let rule = Rules.UseTraitCollections(context: .mock(from: ibLinterConfig))