diff --git a/.github/workflows/record-screenshots.yml b/.github/workflows/record-screenshots.yml index b44bd83d8..32fc4854e 100644 --- a/.github/workflows/record-screenshots.yml +++ b/.github/workflows/record-screenshots.yml @@ -36,7 +36,7 @@ jobs: - name: Enable screenshots recording run: | - find . -type f -name "*.swift" -exec sed -i '' 's/isRecording = false/isRecording = true/' {} + + find . -type f -name "*.swift" -exec sed -i '' 's/record: .never/record: .failed/' {} + - name: Launch tests and record screenshots run: make test @@ -44,7 +44,7 @@ jobs: - name: Disable screenshots recording run: | - find . -type f -name "*.swift" -exec sed -i '' 's/isRecording = true/isRecording = false/' {} + + find . -type f -name "*.swift" -exec sed -i '' 's/record: .failed/record: .never/' {} + - name: Commit Changes uses: stefanzweifel/git-auto-commit-action@v5 diff --git a/MisticaCatalog/MisticaCatalog.xcodeproj/project.pbxproj b/MisticaCatalog/MisticaCatalog.xcodeproj/project.pbxproj index ff2a1c0f8..7f9592c0d 100644 --- a/MisticaCatalog/MisticaCatalog.xcodeproj/project.pbxproj +++ b/MisticaCatalog/MisticaCatalog.xcodeproj/project.pbxproj @@ -630,6 +630,7 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -685,6 +686,7 @@ SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 6.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -710,7 +712,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.tid.mistica.enterprise; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "development-mistica"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -735,7 +737,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.tid.mistica.enterprise; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "distribution-mistica"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Release; }; diff --git a/MisticaCatalog/Source/Catalog/CatalogList.swift b/MisticaCatalog/Source/Catalog/CatalogList.swift index 375a090c5..e1abbd954 100644 --- a/MisticaCatalog/Source/Catalog/CatalogList.swift +++ b/MisticaCatalog/Source/Catalog/CatalogList.swift @@ -58,7 +58,7 @@ struct CatalogList: View { } private extension CatalogRow { - @ViewBuilder + @MainActor @ViewBuilder var swiftUIComponent: some View { switch self { case .badge: @@ -105,7 +105,7 @@ private extension CatalogRow { } } - @ViewBuilder + @MainActor @ViewBuilder var uiKitComponent: some View { switch self { case .buttons: diff --git a/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogButtonsViewController.swift b/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogButtonsViewController.swift index 05eaeb27a..a007d4fc8 100644 --- a/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogButtonsViewController.swift +++ b/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogButtonsViewController.swift @@ -45,7 +45,7 @@ class UICatalogButtonsViewController: UITableViewController { ] } - static var additionalButtonSections: [(name: String, buttons: [Button], contentMode: UIView.ContentMode)] = [ + @MainActor static let additionalButtonSections: [(name: String, buttons: [Button], contentMode: UIView.ContentMode)] = [ ("Min width", [ Button(style: .primary, title: "OK"), Button(style: .primary, title: "OK", isSmall: true), @@ -215,7 +215,7 @@ private class LoadSimulationButton: Button { } private extension Button.State { - func makeButton( + @MainActor func makeButton( style: Button.Style, title _: String, loadingTitle _: String, diff --git a/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogEmptyStateViewController.swift b/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogEmptyStateViewController.swift index 6af89e910..83b5ef484 100644 --- a/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogEmptyStateViewController.swift +++ b/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogEmptyStateViewController.swift @@ -199,8 +199,10 @@ extension UICatalogEmptyStateViewController: UITableViewDataSource, UITableViewD view.endEditing(true) let actions: EmptyStateConfiguration.EmptyStateActions - let handler: () -> Void = { - CroutonController.shared.showCrouton(config: SnackbarConfig(title: "The user has tapped any button", dismissInterval: .fiveSeconds)) + let handler: @Sendable() -> Void = { + Task { @MainActor in + CroutonController.shared.showCrouton(config: SnackbarConfig(title: "The user has tapped any button", dismissInterval: .fiveSeconds)) + } } switch buttonsCell.segmentedControl.selectedSegmentIndex { case 0: diff --git a/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogFeedbacksViewController.swift b/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogFeedbacksViewController.swift index 0b833de16..2abc12cee 100644 --- a/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogFeedbacksViewController.swift +++ b/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogFeedbacksViewController.swift @@ -264,7 +264,9 @@ private extension UICatalogFeedbacksViewController { return .none case 1: let primaryActionCompletion: FeedbackCompletion = { [weak self] in - self?.showAlert(withTitle: "Primary Action", message: nil, cancelActionTitle: "OK") + Task { @MainActor in + self?.showAlert(withTitle: "Primary Action", message: nil, cancelActionTitle: "OK") + } } return .button(title: title, completion: primaryActionCompletion) case 2: @@ -284,7 +286,9 @@ private extension UICatalogFeedbacksViewController { func buildSecondaryAction(for selectedIndex: Int, title: String) -> FeedbackSecondaryAction { let secondaryActionCompletion: FeedbackCompletion = { [weak self] in - self?.showAlert(withTitle: "Secondary Action", message: nil, cancelActionTitle: "OK") + Task { @MainActor in + self?.showAlert(withTitle: "Secondary Action", message: nil, cancelActionTitle: "OK") + } } switch selectedIndex { diff --git a/MisticaCatalog/Source/Catalog/MisticaSwiftUI/Components/ButtonCatalogView.swift b/MisticaCatalog/Source/Catalog/MisticaSwiftUI/Components/ButtonCatalogView.swift index ef599aadc..16e3754fe 100644 --- a/MisticaCatalog/Source/Catalog/MisticaSwiftUI/Components/ButtonCatalogView.swift +++ b/MisticaCatalog/Source/Catalog/MisticaSwiftUI/Components/ButtonCatalogView.swift @@ -11,6 +11,7 @@ import MisticaSwiftUI import SwiftUI struct ButtonsCatalogView: View { + @MainActor private enum Constants { static var styles: [Style] { [ diff --git a/MisticaCatalog/Source/ColorsView.swift b/MisticaCatalog/Source/ColorsView.swift index 5f12bd5af..e60f0e862 100644 --- a/MisticaCatalog/Source/ColorsView.swift +++ b/MisticaCatalog/Source/ColorsView.swift @@ -101,7 +101,7 @@ struct Searchable: ViewModifier { } extension UIImage { - func bordered(borderWidth: CGFloat = 1, color: UIColor) -> UIImage { + @MainActor func bordered(borderWidth: CGFloat = 1, color: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) let imageRect = CGRect(x: 0, y: 0, width: size.width, height: size.height) draw(in: imageRect) diff --git a/MisticaCatalog/Source/Common/Views/UIStepperTableViewCell.swift b/MisticaCatalog/Source/Common/Views/UIStepperTableViewCell.swift index dd0138b91..3e591f42f 100644 --- a/MisticaCatalog/Source/Common/Views/UIStepperTableViewCell.swift +++ b/MisticaCatalog/Source/Common/Views/UIStepperTableViewCell.swift @@ -15,7 +15,7 @@ public class UIStepperTableViewCell: UITableViewCell { private let numberOfStepsLabel = UILabel() private lazy var textStackView = UIStackView(arrangedSubviews: [titleLabel, numberOfStepsLabel]) private lazy var numberOfStepsStackView = UIStackView(arrangedSubviews: [textStackView, numberOfStepsStepper]) - var didValueChange: ((UIStepper) -> Void)? + var didValueChange: (@MainActor @Sendable(UIStepper) -> Void)? var minValue: Double { get { numberOfStepsStepper.minimumValue } diff --git a/MisticaCatalog/Source/Common/Views/UISwitchTableViewCell.swift b/MisticaCatalog/Source/Common/Views/UISwitchTableViewCell.swift index 68ea6d372..90728633b 100644 --- a/MisticaCatalog/Source/Common/Views/UISwitchTableViewCell.swift +++ b/MisticaCatalog/Source/Common/Views/UISwitchTableViewCell.swift @@ -16,7 +16,7 @@ public class UISwitchTableViewCell: UITableViewCell { set { `switch`.isOn = newValue } } - public var didValueChange: ((UISwitch) -> Void)? + public var didValueChange: (@MainActor @Sendable(UISwitch) -> Void)? public init(reuseIdentifier: String?) { super.init(style: .default, reuseIdentifier: reuseIdentifier) diff --git a/MisticaCatalog/Source/MisticaCatalogApp.swift b/MisticaCatalog/Source/MisticaCatalogApp.swift index 747d9087b..a53aaf097 100644 --- a/MisticaCatalog/Source/MisticaCatalogApp.swift +++ b/MisticaCatalog/Source/MisticaCatalogApp.swift @@ -106,19 +106,19 @@ struct MisticaCatalogApp: App { func configureFontStyle(for brandStyle: BrandStyle) { if let mapping = brandStyle.fontMapping { - FontStyle.fontNameForWeight = { weight in + FontManager.shared.fontNameForWeight = { weight in mapping.fontName(for: weight) } - FontStyle.uiFontNameForWeight = { weight in + FontManager.shared.uiFontNameForWeight = { weight in mapping.UIfontName(for: weight) } } else { - FontStyle.fontNameForWeight = nil - FontStyle.uiFontNameForWeight = nil + FontManager.shared.fontNameForWeight = nil + FontManager.shared.uiFontNameForWeight = nil } } } extension UIColor { - static var misticaCatalogTint = UIColor(hex: "#0066FF")! + static let misticaCatalogTint = UIColor(hex: "#0066FF")! } diff --git a/Package.swift b/Package.swift index 56c3f56db..9c0ce522c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 import PackageDescription @@ -15,7 +15,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/airbnb/lottie-spm.git", exact: "4.5.0"), - .package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", exact: "1.8.2"), + .package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", exact: "1.17.6"), .package(url: "https://github.com/SDWebImage/SDWebImage.git", exact: "5.19.1"), .package(url: "https://github.com/SDWebImage/SDWebImageSVGCoder.git", exact: "1.7.0") ], diff --git a/Sources/Mistica/Components/Badge/NovumBarButtonItem.swift b/Sources/Mistica/Components/Badge/NovumBarButtonItem.swift index c458e3f42..fce7f1b6e 100644 --- a/Sources/Mistica/Components/Badge/NovumBarButtonItem.swift +++ b/Sources/Mistica/Components/Badge/NovumBarButtonItem.swift @@ -11,6 +11,7 @@ import UIKit /// Creates a UITabBarItem with the Badge style of Novum /// - Parameters: /// - badgeValue: The number to show in the badge or 0 for do not display it. +@MainActor public func createNovumTabBarItem(badgeValue: UInt = 0) -> UITabBarItem { let item = UITabBarItem() item.badgeColor = .badge diff --git a/Sources/Mistica/Components/Button/ButtonStyle+Toolkit.swift b/Sources/Mistica/Components/Button/ButtonStyle+Toolkit.swift index a7e546ba3..b47f224a0 100644 --- a/Sources/Mistica/Components/Button/ButtonStyle+Toolkit.swift +++ b/Sources/Mistica/Components/Button/ButtonStyle+Toolkit.swift @@ -17,9 +17,9 @@ public extension Button.Style { private static var smallFont: UIFont { .textPreset2(weight: .button) } private static var linkFont: UIFont { .textPreset2(weight: .button) } - private static var regularMinimumWidth: CGFloat = 156 - private static var smallMinimumWidth: CGFloat = 104 - private static var linkMinimumWidth: CGFloat = 0 + private static let regularMinimumWidth: CGFloat = 156 + private static let smallMinimumWidth: CGFloat = 104 + private static let linkMinimumWidth: CGFloat = 0 private enum ImageHeight { static let regular: CGFloat = 24 diff --git a/Sources/Mistica/Components/Callout/Callout.swift b/Sources/Mistica/Components/Callout/Callout.swift index 11c956958..9a6956116 100644 --- a/Sources/Mistica/Components/Callout/Callout.swift +++ b/Sources/Mistica/Components/Callout/Callout.swift @@ -31,7 +31,7 @@ public class Callout: UIView { if let contentConfiguration = contentConfiguration { configure(withConfiguration: contentConfiguration) } else { - configure(withConfiguration: .emptyConfiguration) + configure(withConfiguration: .emptyConfiguration()) } } } diff --git a/Sources/Mistica/Components/Callout/CalloutConfiguration.swift b/Sources/Mistica/Components/Callout/CalloutConfiguration.swift index a31fb4b9e..549da86eb 100644 --- a/Sources/Mistica/Components/Callout/CalloutConfiguration.swift +++ b/Sources/Mistica/Components/Callout/CalloutConfiguration.swift @@ -10,7 +10,9 @@ import Foundation import UIKit public struct CalloutConfiguration { - static let emptyConfiguration = CalloutConfiguration(asset: .none, title: nil, description: "Empty configuration", actions: nil, canClose: true) + public static func emptyConfiguration() -> CalloutConfiguration { + CalloutConfiguration(asset: .none, title: nil, description: "Empty configuration", actions: nil, canClose: true) + } public enum CalloutActions { case primary(CalloutButton) diff --git a/Sources/Mistica/Components/Callout/Internals/CalloutContentBase.swift b/Sources/Mistica/Components/Callout/Internals/CalloutContentBase.swift index 15ceb452e..691709718 100644 --- a/Sources/Mistica/Components/Callout/Internals/CalloutContentBase.swift +++ b/Sources/Mistica/Components/Callout/Internals/CalloutContentBase.swift @@ -51,10 +51,10 @@ extension CalloutContentBase { var descriptionTitle: String { get { - calloutBaseView.messagesView.description + calloutBaseView.messagesView.calloutDescription } set { - calloutBaseView.messagesView.description = newValue + calloutBaseView.messagesView.calloutDescription = newValue } } @@ -107,7 +107,7 @@ extension CalloutContentBase { } calloutBaseView.title = configuration.title - calloutBaseView.description = configuration.description + calloutBaseView.calloutTitleDescription = configuration.description switch configuration.actions { case let .primary(primaryButton): diff --git a/Sources/Mistica/Components/Callout/Internals/CalloutMessagesContent.swift b/Sources/Mistica/Components/Callout/Internals/CalloutMessagesContent.swift index 244680238..70b464bc1 100644 --- a/Sources/Mistica/Components/Callout/Internals/CalloutMessagesContent.swift +++ b/Sources/Mistica/Components/Callout/Internals/CalloutMessagesContent.swift @@ -42,7 +42,7 @@ extension CalloutMessagesContent { } } - override var description: String { + var calloutDescription: String { get { descriptionLabel.text! } diff --git a/Sources/Mistica/Components/Callout/Internals/CalloutTitleActions.swift b/Sources/Mistica/Components/Callout/Internals/CalloutTitleActions.swift index 239e35cd3..1e19a7328 100644 --- a/Sources/Mistica/Components/Callout/Internals/CalloutTitleActions.swift +++ b/Sources/Mistica/Components/Callout/Internals/CalloutTitleActions.swift @@ -38,12 +38,12 @@ extension CalloutTitleActions { } } - override var description: String { + var calloutTitleDescription: String { get { - messagesView.description + messagesView.calloutDescription } set { - messagesView.description = newValue + messagesView.calloutDescription = newValue } } diff --git a/Sources/Mistica/Components/Callout/Model/CalloutButton.swift b/Sources/Mistica/Components/Callout/Model/CalloutButton.swift index 7f23a25b1..0467dd7b8 100644 --- a/Sources/Mistica/Components/Callout/Model/CalloutButton.swift +++ b/Sources/Mistica/Components/Callout/Model/CalloutButton.swift @@ -8,16 +8,16 @@ import Foundation -public struct CalloutButton { +public struct CalloutButton: Sendable { public let title: String public let loadingTitle: String? public let accessibilityIdentifier: String? - public let tapHandler: (() -> Void)? + public let tapHandler: (@Sendable() -> Void)? public init(title: String, loadingTitle: String?, accessibilityIdentifier: String? = nil, - tapHandler: (() -> Void)?) { + tapHandler: (@Sendable() -> Void)?) { self.title = title self.loadingTitle = loadingTitle self.accessibilityIdentifier = accessibilityIdentifier @@ -25,14 +25,14 @@ public struct CalloutButton { } } -public struct CalloutLinkButton { +public struct CalloutLinkButton: Sendable { public let title: String public let accessibilityIdentifier: String? - public let tapHandler: (() -> Void)? + public let tapHandler: (@Sendable() -> Void)? public init(title: String, accessibilityIdentifier: String? = nil, - tapHandler: (() -> Void)?) { + tapHandler: (@Sendable() -> Void)?) { self.title = title self.accessibilityIdentifier = accessibilityIdentifier self.tapHandler = tapHandler diff --git a/Sources/Mistica/Components/Cards/DataCard.swift b/Sources/Mistica/Components/Cards/DataCard.swift index f3dfbcec0..77ac50c60 100644 --- a/Sources/Mistica/Components/Cards/DataCard.swift +++ b/Sources/Mistica/Components/Cards/DataCard.swift @@ -79,7 +79,7 @@ public class DataCard: UIView { if let contentConfiguration = contentConfiguration { configure(with: contentConfiguration) } else { - configure(with: .emptyConfiguration) + configure(with: .emptyConfiguration()) } } } @@ -294,11 +294,13 @@ private extension DataCard { } private extension DataCardConfiguration { - static let emptyConfiguration = DataCardConfiguration( - title: "", - descriptionTitle: "", - buttons: .link( - CardLinkButton(title: "", accessibilityIdentifier: nil, tapHandler: nil) + static func emptyConfiguration() -> DataCardConfiguration { + DataCardConfiguration( + title: "", + descriptionTitle: "", + buttons: .link( + CardLinkButton(title: "", accessibilityIdentifier: nil, tapHandler: nil) + ) ) - ) + } } diff --git a/Sources/Mistica/Components/Cards/MediaCard.swift b/Sources/Mistica/Components/Cards/MediaCard.swift index ec22d6c27..df18abb44 100644 --- a/Sources/Mistica/Components/Cards/MediaCard.swift +++ b/Sources/Mistica/Components/Cards/MediaCard.swift @@ -75,7 +75,7 @@ public class MediaCard: UIView { if let contentConfiguration = contentConfiguration { configure(with: contentConfiguration) } else { - configure(with: .emptyConfiguration) + configure(with: .emptyConfiguration()) } } } @@ -201,5 +201,8 @@ private extension MediaCard { } private extension MediaCardConfiguration { - static let emptyConfiguration = MediaCardConfiguration(richMedia: UIView(), descriptionTitle: "") + @MainActor + static func emptyConfiguration() -> MediaCardConfiguration { + MediaCardConfiguration(richMedia: UIView(), descriptionTitle: "") + } } diff --git a/Sources/Mistica/Components/Cards/Model/CardButton.swift b/Sources/Mistica/Components/Cards/Model/CardButton.swift index ec15e550f..d538ddf98 100644 --- a/Sources/Mistica/Components/Cards/Model/CardButton.swift +++ b/Sources/Mistica/Components/Cards/Model/CardButton.swift @@ -8,16 +8,16 @@ import Foundation -public struct CardButton { +public struct CardButton: Sendable { public let title: String public let accessibilityIdentifier: String? public let loadingTitle: String? - public let tapHandler: (() -> Void)? + public let tapHandler: (@Sendable() -> Void)? public init(title: String, loadingTitle: String?, accessibilityIdentifier: String? = nil, - tapHandler: (() -> Void)?) { + tapHandler: (@Sendable() -> Void)?) { self.title = title self.loadingTitle = loadingTitle self.accessibilityIdentifier = accessibilityIdentifier @@ -25,14 +25,14 @@ public struct CardButton { } } -public struct CardLinkButton { +public struct CardLinkButton: Sendable { public let title: String public let accessibilityIdentifier: String? - public let tapHandler: (() -> Void)? + public let tapHandler: (@Sendable() -> Void)? public init(title: String, accessibilityIdentifier: String? = nil, - tapHandler: (() -> Void)?) { + tapHandler: (@Sendable() -> Void)?) { self.title = title self.accessibilityIdentifier = accessibilityIdentifier self.tapHandler = tapHandler diff --git a/Sources/Mistica/Components/Checkbox/Checkbox.swift b/Sources/Mistica/Components/Checkbox/Checkbox.swift index 4273e7af7..4c3b5c685 100644 --- a/Sources/Mistica/Components/Checkbox/Checkbox.swift +++ b/Sources/Mistica/Components/Checkbox/Checkbox.swift @@ -14,7 +14,7 @@ public class Checkbox: UIControl { private enum Constants { static let viewWidth = CGFloat(18) static let animationDuration = Double(0.4) - static let timingFunction = CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1) + nonisolated(unsafe) static let timingFunction = CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1) } private let imageView = UIImageView(image: .checkmarkIcon) diff --git a/Sources/Mistica/Components/Crouton/Presentation/CroutonController.swift b/Sources/Mistica/Components/Crouton/Presentation/CroutonController.swift index 333f878d8..660a884c8 100644 --- a/Sources/Mistica/Components/Crouton/Presentation/CroutonController.swift +++ b/Sources/Mistica/Components/Crouton/Presentation/CroutonController.swift @@ -8,10 +8,12 @@ import UIKit +@MainActor public class CroutonController: NSObject { public enum RootViewController { public typealias Closure = () -> UIViewController? - public static let `default`: Closure = { UIApplication.shared.windows.filter(\.isKeyWindow).first?.rootViewController } + @MainActor public static let `default`: Closure = { UIApplication.shared.windows.filter(\.isKeyWindow).first?.rootViewController + } } public typealias Token = UUID diff --git a/Sources/Mistica/Components/Crouton/Presentation/CustomCroutonContainer.swift b/Sources/Mistica/Components/Crouton/Presentation/CustomCroutonContainer.swift index 5dc3126e1..80ad7ab00 100644 --- a/Sources/Mistica/Components/Crouton/Presentation/CustomCroutonContainer.swift +++ b/Sources/Mistica/Components/Crouton/Presentation/CustomCroutonContainer.swift @@ -10,5 +10,5 @@ import UIKit // Protocol implemented by view controllers that want to show croutons in a very specific view @objc public protocol CustomCroutonContainer { - var customCroutonContainerView: UIView { get } + @MainActor var customCroutonContainerView: UIView { get } } diff --git a/Sources/Mistica/Components/Crouton/Presentation/OngoingCrouton.swift b/Sources/Mistica/Components/Crouton/Presentation/OngoingCrouton.swift index e630c64ad..bd1cb778c 100644 --- a/Sources/Mistica/Components/Crouton/Presentation/OngoingCrouton.swift +++ b/Sources/Mistica/Components/Crouton/Presentation/OngoingCrouton.swift @@ -9,6 +9,7 @@ import UIKit extension CroutonController { + @MainActor struct OngoingCrouton { let token: Token let croutonView: CroutonView diff --git a/Sources/Mistica/Components/EmptyState/EmptyState.swift b/Sources/Mistica/Components/EmptyState/EmptyState.swift index 2aef57d21..2674905ff 100644 --- a/Sources/Mistica/Components/EmptyState/EmptyState.swift +++ b/Sources/Mistica/Components/EmptyState/EmptyState.swift @@ -23,7 +23,7 @@ public class EmptyState: UIView { if let contentConfiguration = contentConfiguration { configure(withConfiguration: contentConfiguration) } else { - configure(withConfiguration: .empty) + configure(withConfiguration: .emptyConfiguration()) } } } diff --git a/Sources/Mistica/Components/EmptyState/EmptyStateConfiguration.swift b/Sources/Mistica/Components/EmptyState/EmptyStateConfiguration.swift index 6645f2f85..8d87411ca 100644 --- a/Sources/Mistica/Components/EmptyState/EmptyStateConfiguration.swift +++ b/Sources/Mistica/Components/EmptyState/EmptyStateConfiguration.swift @@ -10,7 +10,14 @@ import Foundation import UIKit public struct EmptyStateConfiguration { - static let empty = EmptyStateConfiguration(type: .default(.icon(UIImage())), title: "Basic configuration", description: "This is a basic configuration for the empty state", actions: nil) + static func emptyConfiguration() -> EmptyStateConfiguration { + EmptyStateConfiguration( + type: .default(.icon(UIImage())), + title: "Basic configuration", + description: "This is a basic configuration for the empty state", + actions: nil + ) + } public enum EmptyStateActions { case primary(EmptyStateButton) diff --git a/Sources/Mistica/Components/EmptyState/Model/EmptyStateButton.swift b/Sources/Mistica/Components/EmptyState/Model/EmptyStateButton.swift index 520ba5ee5..f5875b150 100644 --- a/Sources/Mistica/Components/EmptyState/Model/EmptyStateButton.swift +++ b/Sources/Mistica/Components/EmptyState/Model/EmptyStateButton.swift @@ -8,26 +8,26 @@ import Foundation -public struct EmptyStateButton { +public struct EmptyStateButton: Sendable { public let title: String public let loadingTitle: String? - public let tapHandler: (() -> Void)? + public let tapHandler: (@Sendable() -> Void)? public init(title: String, loadingTitle: String?, - tapHandler: (() -> Void)?) { + tapHandler: (@Sendable() -> Void)?) { self.title = title self.loadingTitle = loadingTitle self.tapHandler = tapHandler } } -public struct EmptyStateLinkButton { +public struct EmptyStateLinkButton: Sendable { public let title: String - public let tapHandler: (() -> Void)? + public let tapHandler: (@Sendable() -> Void)? public init(title: String, - tapHandler: (() -> Void)?) { + tapHandler: (@Sendable() -> Void)?) { self.title = title self.tapHandler = tapHandler } diff --git a/Sources/Mistica/Components/Feedback/FeedbackView.swift b/Sources/Mistica/Components/Feedback/FeedbackView.swift index f523ad7b7..52926a1f7 100644 --- a/Sources/Mistica/Components/Feedback/FeedbackView.swift +++ b/Sources/Mistica/Components/Feedback/FeedbackView.swift @@ -208,8 +208,13 @@ public class FeedbackView: UIView { private lazy var buttonsView: UIView = { let buttonsView = UIStackView(arrangedSubviews: []) - [primaryButton, secondaryButton].compactMap { $0 } - .forEach(buttonsView.addArrangedSubview(_:)) + if let primaryButton = primaryButton { + buttonsView.addArrangedSubview(primaryButton) + } + + if let secondaryButton = secondaryButton { + buttonsView.addArrangedSubview(secondaryButton) + } buttonsView.alignment = .fill buttonsView.axis = .vertical @@ -365,7 +370,7 @@ private extension FeedbackView { } // Prepare - views.forEach(prepare(view:)) + try? views.forEach(prepare(view:)) // Generate animators animators = views.map(animation).map { animation in let animator = animator @@ -410,8 +415,10 @@ private extension FeedbackView { let hapticFeedbackDelay = style.hapticFeedbackDelay else { return } Timer.scheduledTimer(withTimeInterval: hapticFeedbackDelay, repeats: false) { [weak self] _ in guard let self = self else { return } - self.feedbackGenerator?.notificationOccurred(hapticFeedbackStyle) - self.feedbackGenerator = nil + Task { @MainActor in + self.feedbackGenerator?.notificationOccurred(hapticFeedbackStyle) + self.feedbackGenerator = nil + } } } @@ -423,11 +430,13 @@ private extension FeedbackView { primaryButton?.isLoading = true prepareHapticFeedback() completion { [weak self] in - self?.prepareAnimation() - self?.startAnimation() + Task { @MainActor in + self?.prepareAnimation() + self?.startAnimation() - self?.primaryButton?.title = title - self?.primaryButton?.isLoading = false + self?.primaryButton?.title = title + self?.primaryButton?.isLoading = false + } } case .none: break diff --git a/Sources/Mistica/Components/Feedback/Model/FeedbackConfiguration.swift b/Sources/Mistica/Components/Feedback/Model/FeedbackConfiguration.swift index b295b6136..323bdb594 100644 --- a/Sources/Mistica/Components/Feedback/Model/FeedbackConfiguration.swift +++ b/Sources/Mistica/Components/Feedback/Model/FeedbackConfiguration.swift @@ -9,8 +9,8 @@ import Foundation import UIKit -public typealias FeedbackCompletion = () -> Void -public typealias FeedbackRetryCompletion = (@escaping () -> Void) -> Void +public typealias FeedbackCompletion = @Sendable() -> Void +public typealias FeedbackRetryCompletion = (@escaping @Sendable() -> Void) -> Void @frozen public enum FeedbackPrimaryAction { diff --git a/Sources/Mistica/Components/Filter/Filter.swift b/Sources/Mistica/Components/Filter/Filter.swift index f2fc1bb5b..c8812fc89 100644 --- a/Sources/Mistica/Components/Filter/Filter.swift +++ b/Sources/Mistica/Components/Filter/Filter.swift @@ -18,6 +18,7 @@ private enum SegmentsContentMode { /// The FilterDelegate protocol defines methods that allow you to manage the selection and deselection of /// segments in a `Filter`. The methods of this protocol are all optional. +@MainActor public protocol FilterDelegate: AnyObject { func filter(_ filter: Filter, didProgramaticallySelectSegment segment: Segment) func filter(_ filter: Filter, didManuallySelectSegment segment: Segment) diff --git a/Sources/Mistica/Components/Filter/Segment.swift b/Sources/Mistica/Components/Filter/Segment.swift index de0217c18..e35f396b9 100644 --- a/Sources/Mistica/Components/Filter/Segment.swift +++ b/Sources/Mistica/Components/Filter/Segment.swift @@ -8,7 +8,7 @@ import Foundation -public struct Segment: Equatable { +public struct Segment: Equatable, Sendable { public let id: String public let title: String public let accessibilityIdentifier: String? diff --git a/Sources/Mistica/Components/Form/FormView.swift b/Sources/Mistica/Components/Form/FormView.swift index 91ce2cf10..3df2e50e2 100644 --- a/Sources/Mistica/Components/Form/FormView.swift +++ b/Sources/Mistica/Components/Form/FormView.swift @@ -10,6 +10,7 @@ import Foundation import UIKit +@MainActor @objc public protocol FormViewDelegate: AnyObject { func formViewButtonShouldBeEnabled(_ formView: FormView) -> Bool func formViewDidTapButton(_ formView: FormView, isValid: Bool) @@ -95,21 +96,21 @@ public extension FormView { } func addInputFields(_ inputFields: [InputField]) { - inputFields.forEach(addInputField) + try? inputFields.forEach(addInputField) arrangeViews() } func removeInputFields(_ inputFields: [InputField]) { - inputFields.forEach(removeInputField) + try? inputFields.forEach(removeInputField) } func addValidatableViews(_ views: [ValidatableView]) { - views.forEach(addValidatableView) + try? views.forEach(addValidatableView) arrangeViews() } func removeValidatableViews(_ views: [ValidatableView]) { - views.forEach(removeValidatableView) + try? views.forEach(removeValidatableView) } func addHeaderView(_ headerView: UIView) { diff --git a/Sources/Mistica/Components/Form/ValidatableView.swift b/Sources/Mistica/Components/Form/ValidatableView.swift index adb99b48d..01738139d 100644 --- a/Sources/Mistica/Components/Form/ValidatableView.swift +++ b/Sources/Mistica/Components/Form/ValidatableView.swift @@ -8,6 +8,7 @@ import UIKit +@MainActor public protocol Validatable: AnyObject { func isValid() -> Bool func validate() diff --git a/Sources/Mistica/Components/InputField/InputField.swift b/Sources/Mistica/Components/InputField/InputField.swift index ce21c964e..a101ab456 100644 --- a/Sources/Mistica/Components/InputField/InputField.swift +++ b/Sources/Mistica/Components/InputField/InputField.swift @@ -8,6 +8,7 @@ import Foundation +import Combine import UIKit public class InputField: UIView { @@ -377,16 +378,24 @@ public class InputField: UIView { updateAssistiveLabelAlpha() updateStyle() - subscribeToPlaceholdeLabelBoundsChanges() + subscribeToPlaceholderChanges() } deinit { - unsubscribeToPlaceholdeLabelBoundsChanges() + DispatchQueue.main.async { [weak self] in + self?.cancellables.forEach { $0.cancel() } + } } - override public func observeValue(forKeyPath _: String?, of _: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) { - updatePlaceholderLayerPosition() - updatePlaceholderLayerSize() + private var cancellables = Set() + + private func subscribeToPlaceholderChanges() { + backingPlaceholderLabel.publisher(for: \.bounds) + .sink { _ in + self.updatePlaceholderLayerPosition() + self.updatePlaceholderLayerSize() + } + .store(in: &cancellables) } override public var intrinsicContentSize: CGSize { @@ -706,14 +715,6 @@ private extension InputField { return validationStrategy?.validate(text: text) ?? .success } } - - func subscribeToPlaceholdeLabelBoundsChanges() { - backingPlaceholderLabel.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: nil) - } - - func unsubscribeToPlaceholdeLabelBoundsChanges() { - backingPlaceholderLabel.removeObserver(self, forKeyPath: #keyPath(UIView.bounds)) - } } // MARK: Animations diff --git a/Sources/Mistica/Components/InputField/InputFieldDelegate.swift b/Sources/Mistica/Components/InputField/InputFieldDelegate.swift index 57fded6a1..ff5b33257 100644 --- a/Sources/Mistica/Components/InputField/InputFieldDelegate.swift +++ b/Sources/Mistica/Components/InputField/InputFieldDelegate.swift @@ -9,6 +9,7 @@ import Foundation import UIKit +@MainActor public protocol InputFieldDelegate: AnyObject { func inputFieldTextDidChange(_ field: InputField) func inputFieldShouldBeginEditing(_ field: InputField) -> Bool @@ -24,6 +25,7 @@ public protocol InputFieldDelegate: AnyObject { func inputFieldShouldLayout(_ field: InputField) } +@MainActor @objc public protocol InputFieldDataSource: AnyObject { func inputFieldPickerElements(_ inputField: InputField) -> [String] func inputField(_ inputField: InputField, didSelectPickerElementAt index: Int) diff --git a/Sources/Mistica/Components/Lists/Internals/CellCenterSectionView.swift b/Sources/Mistica/Components/Lists/Internals/CellCenterSectionView.swift index c3c56c51d..4419fb8e3 100644 --- a/Sources/Mistica/Components/Lists/Internals/CellCenterSectionView.swift +++ b/Sources/Mistica/Components/Lists/Internals/CellCenterSectionView.swift @@ -8,6 +8,7 @@ import UIKit +@MainActor protocol ListCellContentViewDelegate: AnyObject { func accessibilityChanged() } diff --git a/Sources/Mistica/Components/Lists/ListCellContentView.swift b/Sources/Mistica/Components/Lists/ListCellContentView.swift index 41c94a6f6..bcad7d64e 100644 --- a/Sources/Mistica/Components/Lists/ListCellContentView.swift +++ b/Sources/Mistica/Components/Lists/ListCellContentView.swift @@ -8,6 +8,7 @@ import UIKit +@MainActor protocol ListCellContentTableViewDelegate { func cellStyleChanged() func accessibilityChanged() diff --git a/Sources/Mistica/Components/RadioButton/RadioButton.swift b/Sources/Mistica/Components/RadioButton/RadioButton.swift index 12e2acc59..53b250a72 100644 --- a/Sources/Mistica/Components/RadioButton/RadioButton.swift +++ b/Sources/Mistica/Components/RadioButton/RadioButton.swift @@ -14,7 +14,7 @@ public class RadioButton: UIControl { private enum Constants { static let viewWidth = CGFloat(20) static let animationDuration = Double(0.4) - static let timingFunction = CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1) + nonisolated(unsafe) static let timingFunction = CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1) } private var _isActivated = false diff --git a/Sources/Mistica/Components/Sheet/View/BottomSheetPresentation/BottomSheetInteractiveDismissalTransition.swift b/Sources/Mistica/Components/Sheet/View/BottomSheetPresentation/BottomSheetInteractiveDismissalTransition.swift index 6df18a282..d7527b9f0 100644 --- a/Sources/Mistica/Components/Sheet/View/BottomSheetPresentation/BottomSheetInteractiveDismissalTransition.swift +++ b/Sources/Mistica/Components/Sheet/View/BottomSheetPresentation/BottomSheetInteractiveDismissalTransition.swift @@ -11,7 +11,7 @@ import UIKit final class BottomSheetInteractiveDismissalTransition: NSObject { private enum Constants { static let maxBouncingHeight: CGFloat = 250 - static let animationDuration: CGFloat = UIView.defaultAnimationDuration + @MainActor static let animationDuration: CGFloat = UIView.defaultAnimationDuration static let animationCurve: UIView.AnimationCurve = .easeOut } @@ -29,6 +29,7 @@ final class BottomSheetInteractiveDismissalTransition: NSObject { // MARK: Public methods extension BottomSheetInteractiveDismissalTransition { + @MainActor func start(moving presentedView: UIView, interactiveDismissal: Bool) { self.interactiveDismissal = interactiveDismissal @@ -42,6 +43,7 @@ extension BottomSheetInteractiveDismissalTransition { ) } + @MainActor func move(_ presentedView: UIView, using translation: CGFloat) { let progress = translation / presentedView.frame.height @@ -51,6 +53,7 @@ extension BottomSheetInteractiveDismissalTransition { transitionContext?.updateInteractiveTransition(progress) } + @MainActor func stop( moving presentedView: UIView, at translation: CGFloat, @@ -171,6 +174,7 @@ extension BottomSheetInteractiveDismissalTransition: UIViewControllerInteractive // MARK: Private private extension BottomSheetInteractiveDismissalTransition { + @MainActor func createHeightAnimator( animating view: UIView, from height: CGFloat @@ -198,6 +202,7 @@ private extension BottomSheetInteractiveDismissalTransition { return propertyAnimator } + @MainActor func createOffsetAnimator( animating view: UIView, to offset: CGFloat, diff --git a/Sources/Mistica/Components/Sheet/View/Fragments/List/Touchable.swift b/Sources/Mistica/Components/Sheet/View/Fragments/List/Touchable.swift index 3be6841af..1a1a385ca 100644 --- a/Sources/Mistica/Components/Sheet/View/Fragments/List/Touchable.swift +++ b/Sources/Mistica/Components/Sheet/View/Fragments/List/Touchable.swift @@ -8,6 +8,7 @@ import Foundation +@MainActor protocol Touchable { func touchBegan() func touchEnded() diff --git a/Sources/Mistica/Components/Stepper/DeterminateStepperView.swift b/Sources/Mistica/Components/Stepper/DeterminateStepperView.swift index 07c14c2cd..d02228b00 100644 --- a/Sources/Mistica/Components/Stepper/DeterminateStepperView.swift +++ b/Sources/Mistica/Components/Stepper/DeterminateStepperView.swift @@ -164,7 +164,7 @@ private extension DeterminateStepperView { arrangedSubviews.append(createStep(step: step)) } - arrangedSubviews.forEach(stackView.addArrangedSubview) + try? arrangedSubviews.forEach(stackView.addArrangedSubview) activateSegmentsWidthConstraints() diff --git a/Sources/Mistica/Components/Tabs/Model/TabItem.swift b/Sources/Mistica/Components/Tabs/Model/TabItem.swift index cf2ba04aa..598034fe6 100644 --- a/Sources/Mistica/Components/Tabs/Model/TabItem.swift +++ b/Sources/Mistica/Components/Tabs/Model/TabItem.swift @@ -8,7 +8,7 @@ import UIKit -public struct TabItem: Equatable { +public struct TabItem: Equatable, Sendable { public let title: String public let icon: UIImage? public let accessibilityIdentifier: String? diff --git a/Sources/Mistica/Components/Tabs/TabsView.swift b/Sources/Mistica/Components/Tabs/TabsView.swift index 5fc512f97..89ff000ab 100644 --- a/Sources/Mistica/Components/Tabs/TabsView.swift +++ b/Sources/Mistica/Components/Tabs/TabsView.swift @@ -10,6 +10,7 @@ import UIKit /// The TabsViewDelegate protocol defines methods that allow you to manage the selection and deselection of /// segments in a `TabsView`. The methods of this protocol are all optional. +@MainActor public protocol TabsViewDelegate: AnyObject { func tabsView(_ tabsView: TabsView, didSelectTab tab: TabItem) } diff --git a/Sources/Mistica/Components/ViewStates/LoadErrorViewControllerDelegate.swift b/Sources/Mistica/Components/ViewStates/LoadErrorViewControllerDelegate.swift index 31cdba808..b8e8595ba 100644 --- a/Sources/Mistica/Components/ViewStates/LoadErrorViewControllerDelegate.swift +++ b/Sources/Mistica/Components/ViewStates/LoadErrorViewControllerDelegate.swift @@ -8,6 +8,7 @@ import Foundation +@MainActor public protocol LoadErrorViewControllerDelegate: AnyObject { func loadErrorViewControllerDidTapRetry(_ viewController: LoadErrorViewController) } diff --git a/Sources/Mistica/Utils/Views/Keyboard/KeyboardNotificationCenter.swift b/Sources/Mistica/Utils/Views/Keyboard/KeyboardNotificationCenter.swift index 3e59d3fd1..9bea39573 100644 --- a/Sources/Mistica/Utils/Views/Keyboard/KeyboardNotificationCenter.swift +++ b/Sources/Mistica/Utils/Views/Keyboard/KeyboardNotificationCenter.swift @@ -10,6 +10,7 @@ import Foundation import UIKit public extension KeyboardInfo { + @MainActor func animateAlongsideKeyboard(animations: @escaping () -> Void) { UIView.animate( withDuration: duration, @@ -21,7 +22,7 @@ public extension KeyboardInfo { } } -public struct KeyboardEvent: Hashable { +public struct KeyboardEvent: Hashable, Sendable { let notificationName: Notification.Name public static let willShow = KeyboardEvent(notificationName: UIResponder.keyboardWillShowNotification) diff --git a/Sources/MisticaCommon/Assets/MisticaBrandAssets.swift b/Sources/MisticaCommon/Assets/MisticaBrandAssets.swift index 4805090db..11ef7d9e5 100644 --- a/Sources/MisticaCommon/Assets/MisticaBrandAssets.swift +++ b/Sources/MisticaCommon/Assets/MisticaBrandAssets.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI -public protocol MisticaBrandAssets { +public protocol MisticaBrandAssets: Sendable { var iconNotificationInfo: UIImage? { get } var successAnimation: NSDataAsset? { get } var checkAnimation: NSDataAsset? { get } diff --git a/Sources/MisticaCommon/Colors/BlauColorPalette.swift b/Sources/MisticaCommon/Colors/BlauColorPalette.swift index 9380c21ae..bcd44525f 100644 --- a/Sources/MisticaCommon/Colors/BlauColorPalette.swift +++ b/Sources/MisticaCommon/Colors/BlauColorPalette.swift @@ -285,7 +285,7 @@ struct BlauColors: MisticaColors { )) } -public struct BlauColorPalette { +public struct BlauColorPalette: Sendable { public init() {} public let blauBluePrimary = UIColor(hex: "#00B6F1")! public let blauBluePrimary10 = UIColor(hex: "#F7FDFF")! diff --git a/Sources/MisticaCommon/Colors/MisticaColor.swift b/Sources/MisticaCommon/Colors/MisticaColor.swift index a528231c3..512b192cf 100644 --- a/Sources/MisticaCommon/Colors/MisticaColor.swift +++ b/Sources/MisticaCommon/Colors/MisticaColor.swift @@ -8,7 +8,7 @@ import UIKit -public enum MisticaColor { +public enum MisticaColor: Sendable { case solid(UIColor) case gradient(MisticaGradient) } diff --git a/Sources/MisticaCommon/Colors/MisticaColors.swift b/Sources/MisticaCommon/Colors/MisticaColors.swift index 0e155f906..62eb9d75d 100644 --- a/Sources/MisticaCommon/Colors/MisticaColors.swift +++ b/Sources/MisticaCommon/Colors/MisticaColors.swift @@ -8,7 +8,7 @@ import UIKit -public protocol MisticaColors { +public protocol MisticaColors: Sendable { var backgroundBrand: MisticaColor { get } var backgroundBrandSecondary: UIColor { get } var appBarBackground: UIColor { get } diff --git a/Sources/MisticaCommon/Colors/MisticaGradient.swift b/Sources/MisticaCommon/Colors/MisticaGradient.swift index 9b80f4730..b5046d607 100644 --- a/Sources/MisticaCommon/Colors/MisticaGradient.swift +++ b/Sources/MisticaCommon/Colors/MisticaGradient.swift @@ -8,7 +8,7 @@ import UIKit -public struct MisticaGradient { +public struct MisticaGradient: Sendable { public let colors: [UIColor] public let stops: [CGFloat] public let angle: CGFloat diff --git a/Sources/MisticaCommon/Colors/MovistarColorPalette.swift b/Sources/MisticaCommon/Colors/MovistarColorPalette.swift index dae916aef..76430c4c8 100644 --- a/Sources/MisticaCommon/Colors/MovistarColorPalette.swift +++ b/Sources/MisticaCommon/Colors/MovistarColorPalette.swift @@ -285,7 +285,7 @@ struct MovistarColors: MisticaColors { )) } -public struct MovistarColorPalette { +public struct MovistarColorPalette: Sendable { public init() {} public let movistarBlue = UIColor(hex: "#0B9CEA")! public let movistarBlue10 = UIColor(hex: "#E6F5FD")! diff --git a/Sources/MisticaCommon/Colors/O2ColorPalette.swift b/Sources/MisticaCommon/Colors/O2ColorPalette.swift index 8090fc78f..6a1a21796 100644 --- a/Sources/MisticaCommon/Colors/O2ColorPalette.swift +++ b/Sources/MisticaCommon/Colors/O2ColorPalette.swift @@ -285,7 +285,7 @@ struct O2Colors: MisticaColors { )) } -public struct O2ColorPalette { +public struct O2ColorPalette: Sendable { public init() {} public let o2BluePrimary = UIColor(hex: "#0019A5")! public let o2BluePrimary70 = UIColor(hex: "#000066")! diff --git a/Sources/MisticaCommon/Colors/O2NewColorPalette.swift b/Sources/MisticaCommon/Colors/O2NewColorPalette.swift index db23fe056..85f427895 100644 --- a/Sources/MisticaCommon/Colors/O2NewColorPalette.swift +++ b/Sources/MisticaCommon/Colors/O2NewColorPalette.swift @@ -303,7 +303,7 @@ struct O2NewColors: MisticaColors { )) } -public struct O2NewColorPalette { +public struct O2NewColorPalette: Sendable { public init() {} public let beyondBlue = UIColor(hex: "#0050FF")! public let beyondBlue10 = UIColor(hex: "#E5EDFF")! diff --git a/Sources/MisticaCommon/Colors/TelefonicaColorPalette.swift b/Sources/MisticaCommon/Colors/TelefonicaColorPalette.swift index ac4a0d68b..6e011f2ee 100644 --- a/Sources/MisticaCommon/Colors/TelefonicaColorPalette.swift +++ b/Sources/MisticaCommon/Colors/TelefonicaColorPalette.swift @@ -285,7 +285,7 @@ struct TelefonicaColors: MisticaColors { )) } -public struct TelefonicaColorPalette { +public struct TelefonicaColorPalette: Sendable { public init() {} public let telefonicaBlue = UIColor(hex: "#0066FF")! public let telefonicaBlue10 = UIColor(hex: "#E5F0FF")! diff --git a/Sources/MisticaCommon/Colors/TuColorPalette.swift b/Sources/MisticaCommon/Colors/TuColorPalette.swift index b364e47e7..5f3da0fac 100644 --- a/Sources/MisticaCommon/Colors/TuColorPalette.swift +++ b/Sources/MisticaCommon/Colors/TuColorPalette.swift @@ -285,7 +285,7 @@ struct TuColors: MisticaColors { )) } -public struct TuColorPalette { +public struct TuColorPalette: Sendable { public init() {} public let primary = UIColor(hex: "#2B3447")! public let primary10 = UIColor(hex: "#EAEBED")! diff --git a/Sources/MisticaCommon/Colors/VivoColorPalette.swift b/Sources/MisticaCommon/Colors/VivoColorPalette.swift index 1aa56c5d2..dc057fdf2 100644 --- a/Sources/MisticaCommon/Colors/VivoColorPalette.swift +++ b/Sources/MisticaCommon/Colors/VivoColorPalette.swift @@ -285,7 +285,7 @@ struct VivoColors: MisticaColors { )) } -public struct VivoColorPalette { +public struct VivoColorPalette: Sendable { public init() {} public let vivoPurple = UIColor(hex: "#660099")! public let vivoPurpleDark = UIColor(hex: "#461E5F")! diff --git a/Sources/MisticaCommon/Colors/VivoNewColorPalette.swift b/Sources/MisticaCommon/Colors/VivoNewColorPalette.swift index fc448baa2..998c06038 100644 --- a/Sources/MisticaCommon/Colors/VivoNewColorPalette.swift +++ b/Sources/MisticaCommon/Colors/VivoNewColorPalette.swift @@ -285,7 +285,7 @@ struct VivoNewColors: MisticaColors { )) } -public struct VivoNewColorPalette { +public struct VivoNewColorPalette: Sendable { public init() {} public let vivoPurple = UIColor(hex: "#660099")! public let vivoPurpleDark = UIColor(hex: "#461E5F")! diff --git a/Sources/MisticaCommon/Controls/MisticaAppearance.swift b/Sources/MisticaCommon/Controls/MisticaAppearance.swift index f35acdd74..197e913c4 100644 --- a/Sources/MisticaCommon/Controls/MisticaAppearance.swift +++ b/Sources/MisticaCommon/Controls/MisticaAppearance.swift @@ -16,6 +16,7 @@ public enum MisticaControlStyle: CaseIterable { case pageControl } +@MainActor enum MisticaAppearance { static func setUp(controls: [MisticaControlStyle]) { for control in controls { diff --git a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/CalloutAccessibilityIdentifiers.swift b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/CalloutAccessibilityIdentifiers.swift index 44cabfbec..0da3759bb 100644 --- a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/CalloutAccessibilityIdentifiers.swift +++ b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/CalloutAccessibilityIdentifiers.swift @@ -13,10 +13,10 @@ private extension DefaultAccessibilityIdentifier.Feature { } public enum CalloutAccessibilityIdentifiers { - public static var title = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .title) - public static var description = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .description) - public static var primaryButton = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .primaryButton) - public static var secondaryButton = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .secondaryButton) - public static var linkButton = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .linkButton) - public static var closeButton = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .closeButton) + public static let title = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .title) + public static let description = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .description) + public static let primaryButton = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .primaryButton) + public static let secondaryButton = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .secondaryButton) + public static let linkButton = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .linkButton) + public static let closeButton = DefaultAccessibilityIdentifier(feature: .callout, section: nil, elementType: .closeButton) } diff --git a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/DataCardAccessibilityIdentifiers.swift b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/DataCardAccessibilityIdentifiers.swift index 14503ca0d..df0c8da19 100644 --- a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/DataCardAccessibilityIdentifiers.swift +++ b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/DataCardAccessibilityIdentifiers.swift @@ -13,10 +13,10 @@ private extension DefaultAccessibilityIdentifier.Feature { } public enum DataCardAccessibilityIdentifiers { - public static var headline = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .tag) - public static var title = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .title) - public static var subtitle = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .subtitle) - public static var description = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .description) - public static var primaryButton = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .primaryButton) - public static var linkButton = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .linkButton) + public static let headline = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .tag) + public static let title = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .title) + public static let subtitle = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .subtitle) + public static let description = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .description) + public static let primaryButton = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .primaryButton) + public static let linkButton = DefaultAccessibilityIdentifier(feature: .dataCard, section: .item, elementType: .linkButton) } diff --git a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/DefaultAccessibilityIdentifiers.swift b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/DefaultAccessibilityIdentifiers.swift index 76c3128ec..d3bc674ba 100644 --- a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/DefaultAccessibilityIdentifiers.swift +++ b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/DefaultAccessibilityIdentifiers.swift @@ -8,22 +8,22 @@ import Foundation -public struct DefaultAccessibilityIdentifier { - public struct Feature { +public struct DefaultAccessibilityIdentifier: Sendable { + public struct Feature: Sendable { let description: String public init(_ description: String) { self.description = description } } - public struct Section { + public struct Section: Sendable { let description: String public init(_ description: String) { self.description = description } } - public struct ElementType { + public struct ElementType: Sendable { let description: String public init(_ description: String) { self.description = description diff --git a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/FeedbackAccessibilityIdentifiers.swift b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/FeedbackAccessibilityIdentifiers.swift index d688b92f4..8c8291695 100644 --- a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/FeedbackAccessibilityIdentifiers.swift +++ b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/FeedbackAccessibilityIdentifiers.swift @@ -11,11 +11,11 @@ private extension DefaultAccessibilityIdentifier.Feature { } public enum FeedbackAccessibilityIdentifiers { - public static var icon = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .icon) - public static var title = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .title) - public static var description = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .description) - public static var primaryButton = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .primaryButton) - public static var secondaryButton = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .secondaryButton) - public static var linkButton = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .linkButton) - public static var slot = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .slot) + public static let icon = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .icon) + public static let title = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .title) + public static let description = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .description) + public static let primaryButton = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .primaryButton) + public static let secondaryButton = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .secondaryButton) + public static let linkButton = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .linkButton) + public static let slot = DefaultAccessibilityIdentifier(feature: .feedback, section: .item, elementType: .slot) } diff --git a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/ListAccessibilityIdentifiers.swift b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/ListAccessibilityIdentifiers.swift index aa0a6db53..0fd4f22d9 100644 --- a/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/ListAccessibilityIdentifiers.swift +++ b/Sources/MisticaCommon/DefaultAccessibilityIdentifiers/ListAccessibilityIdentifiers.swift @@ -13,12 +13,12 @@ private extension DefaultAccessibilityIdentifier.Feature { } public enum ListAccessibilityIdentifiers { - public static var icon = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .icon) - public static var tag = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .tag) - public static var title = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .title) - public static var subtitle = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .subtitle) - public static var description = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .description) - public static var slot = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .slot) - public static var action = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .action) - public static var chevron = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .chevron) + public static let icon = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .icon) + public static let tag = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .tag) + public static let title = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .title) + public static let subtitle = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .subtitle) + public static let description = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .description) + public static let slot = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .slot) + public static let action = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .action) + public static let chevron = DefaultAccessibilityIdentifier(feature: .list, section: .item, elementType: .chevron) } diff --git a/Sources/MisticaCommon/Extensions/Bundle+Mistica.swift b/Sources/MisticaCommon/Extensions/Bundle+Mistica.swift index 83d9e172d..34c1c0d73 100644 --- a/Sources/MisticaCommon/Extensions/Bundle+Mistica.swift +++ b/Sources/MisticaCommon/Extensions/Bundle+Mistica.swift @@ -23,7 +23,7 @@ extension Bundle { // To fix the problem, we manually search for the bundle assets. https://developer.apple.com/forums/thread/664295 private class CurrentBundleFinder {} private extension Bundle { - static var misticaCommon: Bundle = { + static let misticaCommon: Bundle = { let bundleName = "Mistica_MisticaCommon" let candidates = [ diff --git a/Sources/MisticaCommon/Extensions/Lottie+Utils.swift b/Sources/MisticaCommon/Extensions/Lottie+Utils.swift index 1ac4c459a..ed80a5dfb 100644 --- a/Sources/MisticaCommon/Extensions/Lottie+Utils.swift +++ b/Sources/MisticaCommon/Extensions/Lottie+Utils.swift @@ -15,7 +15,7 @@ import Lottie **/ public extension Lottie.LottieConfiguration { - static var current: LottieConfiguration = { + nonisolated(unsafe) static var current: LottieConfiguration = { var configuration: LottieConfiguration = .init(renderingEngine: .automatic) #if DEBUG if isRunningUnitTests { diff --git a/Sources/MisticaCommon/Fonts/FontStyle.swift b/Sources/MisticaCommon/Fonts/FontStyle.swift index 510dc39ab..91c9a9583 100644 --- a/Sources/MisticaCommon/Fonts/FontStyle.swift +++ b/Sources/MisticaCommon/Fonts/FontStyle.swift @@ -9,6 +9,39 @@ import SwiftUI import UIKit +public class FontManager: @unchecked Sendable { + public static let shared = FontManager() + + private let queue = DispatchQueue(label: "com.telefonica.fontManager", attributes: .concurrent) + + private var _fontNameForWeight: (@Sendable(Font.Weight) -> String)? = nil + private var _uiFontNameForWeight: (@Sendable(UIFont.Weight) -> String)? = nil + + public var fontNameForWeight: (@Sendable(Font.Weight) -> String)? { + get { + queue.sync { _fontNameForWeight } + } + set { + queue.async { + self._fontNameForWeight = newValue + } + } + } + + public var uiFontNameForWeight: (@Sendable(UIFont.Weight) -> String)? { + get { + queue.sync { _uiFontNameForWeight } + } + set { + queue.async { + self._uiFontNameForWeight = newValue + } + } + } + + private init() {} +} + @frozen @objc public enum FontStyle: Int, CaseIterable, CustomStringConvertible { case textPreset1 @@ -30,22 +63,20 @@ import UIKit ) -> Font { let pointSize = calculateFontSize(constrainedToPreferredSize: constrainedPreferredSize) - if let fontName = Self.fontNameForWeight?(weight) { + if let fontName = FontManager.shared.fontNameForWeight?(weight) { return Font.custom(fontName, size: pointSize) } else { return Font.system(size: pointSize, weight: weight, design: .default) } } - public static var fontNameForWeight: ((Font.Weight) -> String)? = nil - func preferredFont( weight: UIFont.Weight, constrainedToPreferredSize constrainedPreferredSize: UIContentSizeCategory? = nil ) -> UIFont { let pointSize = calculateFontSize(constrainedToPreferredSize: constrainedPreferredSize) - if let fontName = Self.uiFontNameForWeight?(weight), + if let fontName = FontManager.shared.uiFontNameForWeight?(weight), let customFont = UIFont(name: fontName, size: pointSize) { return customFont } else { @@ -53,8 +84,6 @@ import UIKit } } - public static var uiFontNameForWeight: ((UIFont.Weight) -> String)? = nil - public var description: String { switch self { case .textPreset1: @@ -90,14 +119,14 @@ private extension FontStyle { let fontMetrics = UIFontMetrics(forTextStyle: uiFontPressetsCorrelations) var scaledBaseSize = round(fontMetrics.scaledValue(for: baseSize)) - if let constrainedPreferredSize = maximumFonSize(constrainedPreferredSize: constrainedPreferredSize) { + if let constrainedPreferredSize = maximumFontSize(constrainedPreferredSize: constrainedPreferredSize) { scaledBaseSize = min(scaledBaseSize, constrainedPreferredSize) } return scaledBaseSize } - func maximumFonSize(constrainedPreferredSize: UIContentSizeCategory?) -> CGFloat? { + func maximumFontSize(constrainedPreferredSize: UIContentSizeCategory?) -> CGFloat? { guard let constrainedPreferredSize else { return nil } let traitCollection = UITraitCollection(preferredContentSizeCategory: constrainedPreferredSize) diff --git a/Sources/MisticaCommon/Fonts/FontToolkit+UIFont.swift b/Sources/MisticaCommon/Fonts/FontToolkit+UIFont.swift index 183421e46..aa5366c5f 100644 --- a/Sources/MisticaCommon/Fonts/FontToolkit+UIFont.swift +++ b/Sources/MisticaCommon/Fonts/FontToolkit+UIFont.swift @@ -8,7 +8,7 @@ import UIKit -var _isDynamicTypeEnabled = true +nonisolated(unsafe) var _isDynamicTypeEnabled = true public extension UIFont { static func textPreset1(weight: FontStyle.TextPreset1Weight, constrainedToPreferredSize: UIContentSizeCategory? = nil) -> UIFont { diff --git a/Sources/MisticaCommon/Fonts/MisticaFontSizes.swift b/Sources/MisticaCommon/Fonts/MisticaFontSizes.swift index 997e4dfc0..573cf0a2a 100644 --- a/Sources/MisticaCommon/Fonts/MisticaFontSizes.swift +++ b/Sources/MisticaCommon/Fonts/MisticaFontSizes.swift @@ -8,7 +8,7 @@ import Foundation -public protocol MisticaFontSizes { +public protocol MisticaFontSizes: Sendable { var tabsLabel: CGFloat { get } var title3: CGFloat { get } var text1: CGFloat { get } diff --git a/Sources/MisticaCommon/Fonts/MisticaFontWeightType.swift b/Sources/MisticaCommon/Fonts/MisticaFontWeightType.swift index e1019114c..5d4ecfae2 100644 --- a/Sources/MisticaCommon/Fonts/MisticaFontWeightType.swift +++ b/Sources/MisticaCommon/Fonts/MisticaFontWeightType.swift @@ -9,7 +9,7 @@ import SwiftUI /// Available font weights in Mistica -public enum MisticaFontWeightType { +public enum MisticaFontWeightType: Sendable { case light case regular case medium diff --git a/Sources/MisticaCommon/Fonts/MisticaFontWeights.swift b/Sources/MisticaCommon/Fonts/MisticaFontWeights.swift index 7665d284d..6d9ad1c2a 100644 --- a/Sources/MisticaCommon/Fonts/MisticaFontWeights.swift +++ b/Sources/MisticaCommon/Fonts/MisticaFontWeights.swift @@ -8,7 +8,7 @@ import UIKit -public protocol MisticaFontWeights { +public protocol MisticaFontWeights: Sendable { var cardTitle: MisticaFontWeightType { get } var button: MisticaFontWeightType { get } var tabsLabel: MisticaFontWeightType { get } diff --git a/Sources/MisticaCommon/MisticaConfig.swift b/Sources/MisticaCommon/MisticaConfig.swift index de90da0f0..829cf5a5d 100644 --- a/Sources/MisticaCommon/MisticaConfig.swift +++ b/Sources/MisticaCommon/MisticaConfig.swift @@ -8,24 +8,82 @@ import Foundation -public enum MisticaConfig { - public private(set) static var currentColors: MisticaColors = MovistarColors() - public private(set) static var currentBrandAssets: MisticaBrandAssets = DefaultMisticaBrandAssets() - public private(set) static var currentStyledControls = [MisticaControlStyle]() - public private(set) static var currentFontWeights: MisticaFontWeights = MovistarFontWeights() - public private(set) static var currentCornerRadius: MisticaCornerRadius = MovistarCornerRadius() - public private(set) static var currentFontSizes: MisticaFontSizes = MovistarFontSizes() +public enum MisticaConfig: @unchecked Sendable { + private static let concurrentQueue = DispatchQueue(label: "com.misticaConfig.queue") + + nonisolated(unsafe) private static var _currentColors: MisticaColors = MovistarColors() + nonisolated(unsafe) private static var _currentBrandAssets: MisticaBrandAssets = DefaultMisticaBrandAssets() + nonisolated(unsafe) private static var _currentStyledControls = [MisticaControlStyle]() + nonisolated(unsafe) private static var _currentFontWeights: MisticaFontWeights = MovistarFontWeights() + nonisolated(unsafe) private static var _currentCornerRadius: MisticaCornerRadius = MovistarCornerRadius() + nonisolated(unsafe) private static var _currentFontSizes: MisticaFontSizes = MovistarFontSizes() + + public static var currentColors: MisticaColors { + get { + concurrentQueue.sync { _currentColors } + } + set { + concurrentQueue.async { _currentColors = newValue } + } + } + + public static var currentBrandAssets: MisticaBrandAssets { + get { + concurrentQueue.sync { _currentBrandAssets } + } + set { + concurrentQueue.async { _currentBrandAssets = newValue } + } + } + + public static var currentStyledControls: [MisticaControlStyle] { + get { + concurrentQueue.sync { _currentStyledControls } + } + set { + concurrentQueue.async { _currentStyledControls = newValue } + } + } + + public static var currentFontWeights: MisticaFontWeights { + get { + concurrentQueue.sync { _currentFontWeights } + } + set { + concurrentQueue.async { _currentFontWeights = newValue } + } + } + + public static var currentCornerRadius: MisticaCornerRadius { + get { + concurrentQueue.sync { _currentCornerRadius } + } + set { + concurrentQueue.async { _currentCornerRadius = newValue } + } + } + + public static var currentFontSizes: MisticaFontSizes { + get { + concurrentQueue.sync { _currentFontSizes } + } + set { + concurrentQueue.async { _currentFontSizes = newValue } + } + } // MARK: Public Setup - public static var brandStyle: BrandStyle = .movistar { + nonisolated(unsafe) public static var brandStyle: BrandStyle = .movistar { didSet { configure(for: brandStyle) - MisticaAppearance.setUp(controls: currentStyledControls) + Task { @MainActor in + MisticaAppearance.setUp(controls: currentStyledControls) + } } } - public static func styleControls(_ controls: [MisticaControlStyle]) { + @MainActor public static func styleControls(_ controls: [MisticaControlStyle]) { currentStyledControls = controls MisticaAppearance.setUp(controls: controls) } diff --git a/Sources/MisticaCommon/Radius/MisticaCornerRadius.swift b/Sources/MisticaCommon/Radius/MisticaCornerRadius.swift index 26125833f..bfe4ce5a2 100644 --- a/Sources/MisticaCommon/Radius/MisticaCornerRadius.swift +++ b/Sources/MisticaCommon/Radius/MisticaCornerRadius.swift @@ -12,7 +12,7 @@ public enum MisticaRadiusConstants { static let roundedRadius: CGFloat = 999.0 } -public protocol MisticaCornerRadius { +public protocol MisticaCornerRadius: Sendable { var avatar: CGFloat { get } var bar: CGFloat { get } var button: CGFloat { get } diff --git a/Sources/MisticaCommon/Utils/Accessibility/AccessibilityHelper.swift b/Sources/MisticaCommon/Utils/Accessibility/AccessibilityHelper.swift index 7f3b8ab4b..81311162b 100644 --- a/Sources/MisticaCommon/Utils/Accessibility/AccessibilityHelper.swift +++ b/Sources/MisticaCommon/Utils/Accessibility/AccessibilityHelper.swift @@ -6,9 +6,10 @@ // Copyright © Telefonica. All rights reserved. // -import UIKit +@preconcurrency import UIKit public enum AccessibilityHelper { + @MainActor public static func post(_ announcement: String) { guard UIAccessibility.isVoiceOverRunning else { return } diff --git a/Sources/MisticaCommon/Utils/Accessibility/ListCells/AccessibilityListCellInteractiveData.swift b/Sources/MisticaCommon/Utils/Accessibility/ListCells/AccessibilityListCellInteractiveData.swift index 511cdcc31..73f46b7df 100644 --- a/Sources/MisticaCommon/Utils/Accessibility/ListCells/AccessibilityListCellInteractiveData.swift +++ b/Sources/MisticaCommon/Utils/Accessibility/ListCells/AccessibilityListCellInteractiveData.swift @@ -8,14 +8,14 @@ import Foundation -public struct AccessibilityListCellInteractiveData { +public struct AccessibilityListCellInteractiveData: Sendable { public let label: String? - public let action: (() -> Void)? + public let action: (@MainActor @Sendable() -> Void)? - public init(label: String? = nil, action: (() -> Void)? = nil) { + public init(label: String? = nil, action: (@MainActor @Sendable() -> Void)? = nil) { self.label = label self.action = action } - public static var `default`: AccessibilityListCellInteractiveData = .init() + public static let `default`: AccessibilityListCellInteractiveData = .init() } diff --git a/Sources/MisticaCommon/Utils/Accessibility/ListCells/AccessibilityListCellType.swift b/Sources/MisticaCommon/Utils/Accessibility/ListCells/AccessibilityListCellType.swift index 10162570f..0a80bf007 100644 --- a/Sources/MisticaCommon/Utils/Accessibility/ListCells/AccessibilityListCellType.swift +++ b/Sources/MisticaCommon/Utils/Accessibility/ListCells/AccessibilityListCellType.swift @@ -8,11 +8,11 @@ import Foundation -public enum AccessibilityListCellType { +public enum AccessibilityListCellType: Sendable { case interactive(AccessibilityListCellInteractiveData) case doubleInteraction(AccessibilityListCellInteractiveData) case informative case customInformative(String) - public static var `default`: AccessibilityListCellType = .interactive(.default) + public static let `default`: AccessibilityListCellType = .interactive(.default) } diff --git a/Sources/MisticaSwiftUI/Components/Button/MisticaButton.swift b/Sources/MisticaSwiftUI/Components/Button/MisticaButton.swift index aeecf925d..76cbf11b2 100644 --- a/Sources/MisticaSwiftUI/Components/Button/MisticaButton.swift +++ b/Sources/MisticaSwiftUI/Components/Button/MisticaButton.swift @@ -216,7 +216,7 @@ private extension MisticaButton { // MARK: EnvironmentValues -public struct MisticaButtonLoadingInfo { +public struct MisticaButtonLoadingInfo: Sendable { public let isLoading: Bool public let loadingTitle: String } diff --git a/Sources/MisticaSwiftUI/Components/Button/MisticaButtonStyle.swift b/Sources/MisticaSwiftUI/Components/Button/MisticaButtonStyle.swift index 81a735ec7..40b92f68c 100644 --- a/Sources/MisticaSwiftUI/Components/Button/MisticaButtonStyle.swift +++ b/Sources/MisticaSwiftUI/Components/Button/MisticaButtonStyle.swift @@ -22,8 +22,8 @@ public struct MisticaButtonStyle: ButtonStyle { // MARK: Styles -private var _opacity = 0.5 -private var _opacityWithBackground = 0.1 +nonisolated(unsafe) private var _opacity = 0.5 +nonisolated(unsafe) private var _opacityWithBackground = 0.1 public extension ButtonStyle where Self == MisticaButtonStyle { static var opacity: CGFloat { diff --git a/Sources/MisticaSwiftUI/Components/Carousel/Carousels/TrackableScrollView.swift b/Sources/MisticaSwiftUI/Components/Carousel/Carousels/TrackableScrollView.swift index 526b80d6f..4e6133b74 100644 --- a/Sources/MisticaSwiftUI/Components/Carousel/Carousels/TrackableScrollView.swift +++ b/Sources/MisticaSwiftUI/Components/Carousel/Carousels/TrackableScrollView.swift @@ -66,7 +66,7 @@ private extension TrackableScrollView { struct ScrollOffsetPreferenceKey: PreferenceKey { typealias Value = [CGPoint] - static var defaultValue: [CGPoint] = [.zero] + static let defaultValue: [CGPoint] = [.zero] static func reduce(value: inout [CGPoint], nextValue: () -> [CGPoint]) { value.append(contentsOf: nextValue()) diff --git a/Sources/MisticaSwiftUI/Components/Feedback/Feedback.swift b/Sources/MisticaSwiftUI/Components/Feedback/Feedback.swift index 8ed23a509..da14d6bd1 100644 --- a/Sources/MisticaSwiftUI/Components/Feedback/Feedback.swift +++ b/Sources/MisticaSwiftUI/Components/Feedback/Feedback.swift @@ -133,7 +133,9 @@ public struct Feedback< feedbackGenerator.prepare() Timer.scheduledTimer(withTimeInterval: hapticFeedbackDelay, repeats: false) { _ in - feedbackGenerator.notificationOccurred(hapticFeedbackStyle) + Task { @MainActor in + feedbackGenerator.notificationOccurred(hapticFeedbackStyle) + } } } case .image(let image): diff --git a/Sources/MisticaSwiftUI/Components/Feedback/FeedbackStyle.swift b/Sources/MisticaSwiftUI/Components/Feedback/FeedbackStyle.swift index 5398262a6..a150ba1e6 100644 --- a/Sources/MisticaSwiftUI/Components/Feedback/FeedbackStyle.swift +++ b/Sources/MisticaSwiftUI/Components/Feedback/FeedbackStyle.swift @@ -31,19 +31,19 @@ public enum FeedbackStyle { } } - var primaryButtonStyle: MisticaButtonStyle { + @MainActor var primaryButtonStyle: MisticaButtonStyle { shouldUseInverseFeedbacks ? .misticaPrimaryInverse() : .misticaPrimary() } - var secondaryButtonStyle: MisticaButtonStyle { + @MainActor var secondaryButtonStyle: MisticaButtonStyle { shouldUseInverseFeedbacks ? .misticaSecondaryInverse() : .misticaSecondary() } - var linkButtonStyle: MisticaButtonStyle { + @MainActor var linkButtonStyle: MisticaButtonStyle { shouldUseInverseFeedbacks ? .misticaLinkInverse() : .misticaLink() } - var iconStyle: FeedbackIconStyle { + @MainActor var iconStyle: FeedbackIconStyle { switch self { case .success, .error: guard let dataAsset = dataAsset else { return .none } diff --git a/Sources/MisticaSwiftUI/Components/Inputfield/InputField.swift b/Sources/MisticaSwiftUI/Components/Inputfield/InputField.swift index df16dac3d..9d90b0c22 100644 --- a/Sources/MisticaSwiftUI/Components/Inputfield/InputField.swift +++ b/Sources/MisticaSwiftUI/Components/Inputfield/InputField.swift @@ -17,7 +17,7 @@ private enum Constants { } public struct InputField: View { - public enum ValidationState: Int, Identifiable, Equatable { + public enum ValidationState: Int, Identifiable, Equatable, Sendable { case normal case invalid diff --git a/Sources/MisticaSwiftUI/Components/Inputfield/LegacyTextField.swift b/Sources/MisticaSwiftUI/Components/Inputfield/LegacyTextField.swift index 4181be8d9..2c7b29d25 100644 --- a/Sources/MisticaSwiftUI/Components/Inputfield/LegacyTextField.swift +++ b/Sources/MisticaSwiftUI/Components/Inputfield/LegacyTextField.swift @@ -160,10 +160,12 @@ extension LegacyTextFieldCoordinator: UIPickerViewDelegate, UIPickerViewDataSour // MARK: Date picker extension LegacyTextFieldCoordinator { + @MainActor @objc func doneButtonTapped(sender: UIBarButtonItem) { textField?.endEditing(true) } + @MainActor @objc func datePickerValueChanged(picker: UIDatePicker) { guard case .date(let format, _) = inputStyle else { return } formatter.dateFormat = format diff --git a/Sources/MisticaSwiftUI/Components/Skeletons/Skeleton.swift b/Sources/MisticaSwiftUI/Components/Skeletons/Skeleton.swift index 633113240..a33166246 100644 --- a/Sources/MisticaSwiftUI/Components/Skeletons/Skeleton.swift +++ b/Sources/MisticaSwiftUI/Components/Skeletons/Skeleton.swift @@ -18,10 +18,10 @@ public enum SkeletonType { public struct Skeleton: View { enum Constants { - static var lineHeight = 8.0 - static var radius = MisticaConfig.currentCornerRadius.container - static var spacing = 16.0 - static var circleSize = 40.0 + static let lineHeight = 8.0 + static let radius = MisticaConfig.currentCornerRadius.container + static let spacing = 16.0 + static let circleSize = 40.0 } let type: SkeletonType diff --git a/Sources/MisticaSwiftUI/Components/Snackbar/Snackbar.swift b/Sources/MisticaSwiftUI/Components/Snackbar/Snackbar.swift index 19418b624..685da9efb 100644 --- a/Sources/MisticaSwiftUI/Components/Snackbar/Snackbar.swift +++ b/Sources/MisticaSwiftUI/Components/Snackbar/Snackbar.swift @@ -10,7 +10,7 @@ import Foundation import SwiftUI -public enum SnackbarStyle { +public enum SnackbarStyle: Sendable { case normal case error } @@ -105,7 +105,9 @@ public struct Snackbar: View { } timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false, block: { _ in - executeDismissHandlerBlock(with: .timeout) + Task { @MainActor in + executeDismissHandlerBlock(with: .timeout) + } }) }) } diff --git a/Sources/MisticaSwiftUI/Components/Tabs/TabItem.swift b/Sources/MisticaSwiftUI/Components/Tabs/TabItem.swift index ccbf98cdc..7050b73de 100644 --- a/Sources/MisticaSwiftUI/Components/Tabs/TabItem.swift +++ b/Sources/MisticaSwiftUI/Components/Tabs/TabItem.swift @@ -24,8 +24,8 @@ public struct TabItem: Equatable { } public struct TabItemView: Equatable, View { - private var tabItem: TabItem - private var indexRow: Int + nonisolated(unsafe) private var tabItem: TabItem + nonisolated private var indexRow: Int @Binding private var selectedIndexRow: Int private var textAccessibilityLabel: String? @@ -43,7 +43,7 @@ public struct TabItemView: Equatable, View { _selectedIndexRow = selectedIndexRow } - public static func == (lhs: TabItemView, rhs: TabItemView) -> Bool { + nonisolated public static func == (lhs: TabItemView, rhs: TabItemView) -> Bool { lhs.tabItem == rhs.tabItem && lhs.indexRow == rhs.indexRow } diff --git a/Sources/MisticaSwiftUI/Components/Tabs/Tabs.swift b/Sources/MisticaSwiftUI/Components/Tabs/Tabs.swift index ba7891284..3bc072b6b 100644 --- a/Sources/MisticaSwiftUI/Components/Tabs/Tabs.swift +++ b/Sources/MisticaSwiftUI/Components/Tabs/Tabs.swift @@ -130,7 +130,7 @@ public struct Tabs: View { struct CGFloatPreferenceKey: PreferenceKey { typealias Value = CGFloat - static var defaultValue: Value = 0 + static let defaultValue: Value = 0 static func reduce(value _: inout Value, nextValue: () -> Value) { _ = nextValue() diff --git a/Sources/MisticaSwiftUI/Utils/Extensions/EnvironmentValues.swift b/Sources/MisticaSwiftUI/Utils/Extensions/EnvironmentValues.swift index 58deb8297..cf184d02a 100644 --- a/Sources/MisticaSwiftUI/Utils/Extensions/EnvironmentValues.swift +++ b/Sources/MisticaSwiftUI/Utils/Extensions/EnvironmentValues.swift @@ -19,8 +19,8 @@ public extension EnvironmentValues { } @available(iOSApplicationExtension, unavailable) -private struct SafeAreaInsetsKey: EnvironmentKey { - static var defaultValue: EdgeInsets { +private struct SafeAreaInsetsKey: @preconcurrency EnvironmentKey { + @MainActor static var defaultValue: EdgeInsets { UIApplication.shared.keyWindow?.safeAreaInsets.swiftUiInsets ?? EdgeInsets() } } diff --git a/Sources/MisticaSwiftUI/Utils/Extensions/View+Utils.swift b/Sources/MisticaSwiftUI/Utils/Extensions/View+Utils.swift index 796b3e3e1..b7109d1ff 100644 --- a/Sources/MisticaSwiftUI/Utils/Extensions/View+Utils.swift +++ b/Sources/MisticaSwiftUI/Utils/Extensions/View+Utils.swift @@ -71,7 +71,7 @@ public extension View { frame(maxHeight: .infinity, alignment: alignment) } - func eraseToAnyView() -> AnyView { + nonisolated func eraseToAnyView() -> AnyView { AnyView(self) } diff --git a/Sources/MisticaSwiftUI/Utils/Modifiers/SizeViewModifier.swift b/Sources/MisticaSwiftUI/Utils/Modifiers/SizeViewModifier.swift index c54367873..f30274468 100644 --- a/Sources/MisticaSwiftUI/Utils/Modifiers/SizeViewModifier.swift +++ b/Sources/MisticaSwiftUI/Utils/Modifiers/SizeViewModifier.swift @@ -10,7 +10,7 @@ import Foundation import SwiftUI struct SizePreferenceKey: PreferenceKey { - static var defaultValue: CGSize = .zero + static let defaultValue: CGSize = .zero static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = nextValue() diff --git a/Tests/MisticaCommonTests/Fonts/FontStyleTests.swift b/Tests/MisticaCommonTests/Fonts/FontStyleTests.swift index 0c2a36cf4..aea1664cc 100644 --- a/Tests/MisticaCommonTests/Fonts/FontStyleTests.swift +++ b/Tests/MisticaCommonTests/Fonts/FontStyleTests.swift @@ -13,7 +13,7 @@ import XCTest final class FontStyleTests: XCTestCase { override class func setUp() { - FontStyle.uiFontNameForWeight = { weight in + FontManager.shared.uiFontNameForWeight = { weight in switch weight { case .light, .ultraLight, .thin: return "Telefonica-Light" @@ -30,7 +30,7 @@ final class FontStyleTests: XCTestCase { } override class func tearDown() { - FontStyle.uiFontNameForWeight = nil + FontManager.shared.uiFontNameForWeight = nil } func testMovistarCustomFonts() { diff --git a/Tests/MisticaCommonTests/Fonts/FontToolkitTests.swift b/Tests/MisticaCommonTests/Fonts/FontToolkitTests.swift index a77e1a2a0..61a990456 100644 --- a/Tests/MisticaCommonTests/Fonts/FontToolkitTests.swift +++ b/Tests/MisticaCommonTests/Fonts/FontToolkitTests.swift @@ -11,11 +11,12 @@ import SnapshotTesting import UIKit import XCTest +@MainActor final class FontToolkitTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testFonts() { diff --git a/Tests/MisticaCommonTests/TestHelpers.swift b/Tests/MisticaCommonTests/TestHelpers.swift index 37a5840d6..6ab73e008 100644 --- a/Tests/MisticaCommonTests/TestHelpers.swift +++ b/Tests/MisticaCommonTests/TestHelpers.swift @@ -22,9 +22,10 @@ extension UIView { // MARK: - Helpers +@MainActor func assertSnapshotForAllBrandsAndStyles( as snapshotting: Snapshotting, - file: StaticString = #file, + file: StaticString = #filePath, testName: String = #function, line: UInt = #line, viewBuilder: @autoclosure () -> View @@ -33,7 +34,7 @@ func assertSnapshotForAllBrandsAndStyles( MisticaConfig.brandStyle = brand assertSnapshot( - matching: viewBuilder(), + of: viewBuilder(), as: snapshotting, named: "with-\(brand)-style", file: file, @@ -45,7 +46,7 @@ func assertSnapshotForAllBrandsAndStyles( darkView.overrideUserInterfaceStyle = .dark assertSnapshot( - matching: darkView, + of: darkView, as: snapshotting, named: "with-\(brand)-dark-style", file: file, @@ -55,6 +56,7 @@ func assertSnapshotForAllBrandsAndStyles( } } +@MainActor protocol UserInterfaceStyling { var overrideUserInterfaceStyle: UIUserInterfaceStyle { get set } } diff --git a/Tests/MisticaSwiftUITests/TestHelpers.swift b/Tests/MisticaSwiftUITests/TestHelpers.swift index 37a5840d6..eada67166 100644 --- a/Tests/MisticaSwiftUITests/TestHelpers.swift +++ b/Tests/MisticaSwiftUITests/TestHelpers.swift @@ -22,9 +22,29 @@ extension UIView { // MARK: - Helpers +@MainActor +public func assertSnapshotWithoutAnimations( + of value: @autoclosure () throws -> Value, + as snapshotting: Snapshotting, + file: StaticString = #filePath, + testName: String = #function, + line: UInt = #line +) { + UIView.setAnimationsEnabled(false) + + assertSnapshot( + of: try value(), + as: snapshotting, + file: file, + testName: testName, + line: line + ) +} + +@MainActor func assertSnapshotForAllBrandsAndStyles( as snapshotting: Snapshotting, - file: StaticString = #file, + file: StaticString = #filePath, testName: String = #function, line: UInt = #line, viewBuilder: @autoclosure () -> View @@ -33,7 +53,7 @@ func assertSnapshotForAllBrandsAndStyles( MisticaConfig.brandStyle = brand assertSnapshot( - matching: viewBuilder(), + of: viewBuilder(), as: snapshotting, named: "with-\(brand)-style", file: file, @@ -45,7 +65,7 @@ func assertSnapshotForAllBrandsAndStyles( darkView.overrideUserInterfaceStyle = .dark assertSnapshot( - matching: darkView, + of: darkView, as: snapshotting, named: "with-\(brand)-dark-style", file: file, @@ -55,6 +75,7 @@ func assertSnapshotForAllBrandsAndStyles( } } +@MainActor protocol UserInterfaceStyling { var overrideUserInterfaceStyle: UIUserInterfaceStyle { get set } } diff --git a/Tests/MisticaSwiftUITests/UI/BadgeTests.swift b/Tests/MisticaSwiftUITests/UI/BadgeTests.swift index dd68062f5..fabbf1916 100644 --- a/Tests/MisticaSwiftUITests/UI/BadgeTests.swift +++ b/Tests/MisticaSwiftUITests/UI/BadgeTests.swift @@ -11,21 +11,24 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class BadgeTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testNumericBadgeContent() { assertSnapshot( - matching: makeTemplateWithNumericBadge(), + of: makeTemplateWithNumericBadge(), as: .image ) } func testFlagBadgeContent() { assertSnapshot( - matching: makeTemplateWithFlagBadge(), + of: makeTemplateWithFlagBadge(), as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/ButtonTests.swift b/Tests/MisticaSwiftUITests/UI/ButtonTests.swift index 6bb49d36b..36dd6910d 100644 --- a/Tests/MisticaSwiftUITests/UI/ButtonTests.swift +++ b/Tests/MisticaSwiftUITests/UI/ButtonTests.swift @@ -11,72 +11,75 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class ButtonTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: Regular Buttons func testRegularSizeWithPrimaryStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaPrimary(small: false)), + of: makeTemplateWithAllButtonStates(style: .misticaPrimary(small: false)), as: .image ) } func testRegularSizeWithPrimaryInverseStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaPrimaryInverse(small: false), inverse: true), + of: makeTemplateWithAllButtonStates(style: .misticaPrimaryInverse(small: false), inverse: true), as: .image ) } func testRegularSizeWithSecondaryStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaSecondary(small: false)), + of: makeTemplateWithAllButtonStates(style: .misticaSecondary(small: false)), as: .image ) } func testRegularSizeWithSecondaryInverseStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaSecondaryInverse(small: false), inverse: true), + of: makeTemplateWithAllButtonStates(style: .misticaSecondaryInverse(small: false), inverse: true), as: .image ) } func testRegularSizeWithDangerStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaDanger(small: false)), + of: makeTemplateWithAllButtonStates(style: .misticaDanger(small: false)), as: .image ) } func testRegularSizeWithLinkStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaLink(small: false)), + of: makeTemplateWithAllButtonStates(style: .misticaLink(small: false)), as: .image ) } func testRegularSizeWithLinkInverseStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaLinkInverse(small: false), inverse: true), + of: makeTemplateWithAllButtonStates(style: .misticaLinkInverse(small: false), inverse: true), as: .image ) } func testRegularSizeWithLinkWithChevron() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaLink(withChevron: true)), + of: makeTemplateWithAllButtonStates(style: .misticaLink(withChevron: true)), as: .image ) } func testRegularSizeWithLinkInverseWithChevron() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaLinkInverse(withChevron: true), inverse: true), + of: makeTemplateWithAllButtonStates(style: .misticaLinkInverse(withChevron: true), inverse: true), as: .image ) } @@ -85,63 +88,63 @@ final class ButtonTests: XCTestCase { func testSmallSizeWithPrimaryStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaPrimary(small: true)), + of: makeTemplateWithAllButtonStates(style: .misticaPrimary(small: true)), as: .image ) } func testSmallSizeWithPrimaryInverseStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaPrimaryInverse(small: true), inverse: true), + of: makeTemplateWithAllButtonStates(style: .misticaPrimaryInverse(small: true), inverse: true), as: .image ) } func testSmallSizeWithSecondaryStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaSecondary(small: true)), + of: makeTemplateWithAllButtonStates(style: .misticaSecondary(small: true)), as: .image ) } func testSmallSizeWithSecondaryInverseStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaSecondaryInverse(small: true), inverse: true), + of: makeTemplateWithAllButtonStates(style: .misticaSecondaryInverse(small: true), inverse: true), as: .image ) } func testSmallSizeWithDangerStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaDanger(small: true)), + of: makeTemplateWithAllButtonStates(style: .misticaDanger(small: true)), as: .image ) } func testSmallSizeWithLinkStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaLink(small: true)), + of: makeTemplateWithAllButtonStates(style: .misticaLink(small: true)), as: .image ) } func testSmallSizeWithLinkInverseStyle() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaLinkInverse(small: true), inverse: true), + of: makeTemplateWithAllButtonStates(style: .misticaLinkInverse(small: true), inverse: true), as: .image ) } func testSmallSizeWithLinkWithChevron() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaLink(small: true, withChevron: true)), + of: makeTemplateWithAllButtonStates(style: .misticaLink(small: true, withChevron: true)), as: .image ) } func testSmallSizeWithLinkInverseWithChevron() { assertSnapshot( - matching: makeTemplateWithAllButtonStates(style: .misticaLinkInverse(small: true, withChevron: true), inverse: true), + of: makeTemplateWithAllButtonStates(style: .misticaLinkInverse(small: true, withChevron: true), inverse: true), as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/CalloutTests.swift b/Tests/MisticaSwiftUITests/UI/CalloutTests.swift index 592a458e9..8d5450071 100644 --- a/Tests/MisticaSwiftUITests/UI/CalloutTests.swift +++ b/Tests/MisticaSwiftUITests/UI/CalloutTests.swift @@ -11,11 +11,12 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class CalloutTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testPrimary() { @@ -27,7 +28,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -42,7 +43,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -57,7 +58,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -71,7 +72,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -86,7 +87,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -100,7 +101,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -113,7 +114,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -125,7 +126,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -141,7 +142,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -156,7 +157,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } @@ -172,7 +173,7 @@ final class CalloutTests: XCTestCase { .frame(width: 350) assertSnapshot( - matching: callout, + of: callout, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/CarouselTests.swift b/Tests/MisticaSwiftUITests/UI/CarouselTests.swift index 702ae6f72..189b1002c 100644 --- a/Tests/MisticaSwiftUITests/UI/CarouselTests.swift +++ b/Tests/MisticaSwiftUITests/UI/CarouselTests.swift @@ -11,57 +11,59 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class CarouselTests: XCTestCase { - override class func setUp() { - super.setUp() - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testFree() { assertSnapshot( - matching: givenCarousel(scrollStyle: .free), + of: givenCarousel(scrollStyle: .free), as: .image ) } func testPaginated() { assertSnapshot( - matching: givenCarousel(scrollStyle: .paginated), + of: givenCarousel(scrollStyle: .paginated), as: .image ) } func testIndex() { assertSnapshot( - matching: givenCarousel(index: .constant(1)), + of: givenCarousel(index: .constant(1)), as: .image ) } func testNoBullets() { assertSnapshot( - matching: givenCarousel(controlStyle: .disabled), + of: givenCarousel(controlStyle: .disabled), as: .image ) } func testLeadingBullets() { assertSnapshot( - matching: givenCarousel(controlStyle: .bullets, controlAlignment: .leading), + of: givenCarousel(controlStyle: .bullets, controlAlignment: .leading), as: .image ) } func testTrailingBullets() { assertSnapshot( - matching: givenCarousel(controlStyle: .bullets, controlAlignment: .trailing), + of: givenCarousel(controlStyle: .bullets, controlAlignment: .trailing), as: .image ) } func testFullWith() { assertSnapshot( - matching: givenCarousel(carouselStyle: .fullWidth), + of: givenCarousel(carouselStyle: .fullWidth), as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/CheckboxTests.swift b/Tests/MisticaSwiftUITests/UI/CheckboxTests.swift index f51ffd48a..2b5fb647b 100644 --- a/Tests/MisticaSwiftUITests/UI/CheckboxTests.swift +++ b/Tests/MisticaSwiftUITests/UI/CheckboxTests.swift @@ -11,16 +11,19 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class CheckboxTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testUnselectedCheckbox() { let checkbox = Checkbox(isSelected: .constant(false)) assertSnapshot( - matching: checkbox, + of: checkbox, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/ChipTests.swift b/Tests/MisticaSwiftUITests/UI/ChipTests.swift index 66aebbee1..ea6d27b06 100644 --- a/Tests/MisticaSwiftUITests/UI/ChipTests.swift +++ b/Tests/MisticaSwiftUITests/UI/ChipTests.swift @@ -11,16 +11,19 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class ChipTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testTooShortChip() { let chip = Chip(style: .normal, text: "", isSelected: .constant(false)) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -29,7 +32,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .normal, text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", icon: .search, onDismiss: {}) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -38,7 +41,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .normal, text: "Lorem ipsum", icon: .search, onDismiss: {}) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -47,7 +50,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .normal, text: "Lorem ipsum", onDismiss: {}) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -56,7 +59,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .normal, text: "Lorem ipsum", icon: .search, isSelected: .constant(false)) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -65,7 +68,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .normal, text: "Lorem ipsum", icon: .search, isSelected: .constant(true)) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -74,7 +77,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .inverse, text: "", isSelected: .constant(false)) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -83,7 +86,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .inverse, text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", icon: .search, isSelected: .constant(false)) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -92,7 +95,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .inverse, text: "Lorem ipsum", icon: .search, onDismiss: {}) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -101,7 +104,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .inverse, text: "Lorem ipsum", icon: nil, onDismiss: {}) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -110,7 +113,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .inverse, text: "Lorem ipsum", icon: .search, isSelected: .constant(false)) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } @@ -119,7 +122,7 @@ final class ChipTests: XCTestCase { let chip = Chip(style: .inverse, text: "Lorem ipsum", icon: .search, isSelected: .constant(true)) assertSnapshot( - matching: fixedContainer { chip }, + of: fixedContainer { chip }, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/CroutonTests.swift b/Tests/MisticaSwiftUITests/UI/CroutonTests.swift index 3c1beb463..78ccf0984 100644 --- a/Tests/MisticaSwiftUITests/UI/CroutonTests.swift +++ b/Tests/MisticaSwiftUITests/UI/CroutonTests.swift @@ -11,9 +11,12 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class CroutonTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testTitle() { @@ -25,7 +28,7 @@ final class CroutonTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -39,7 +42,7 @@ final class CroutonTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -53,7 +56,7 @@ final class CroutonTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -68,7 +71,7 @@ final class CroutonTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -83,7 +86,7 @@ final class CroutonTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -97,7 +100,7 @@ final class CroutonTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -110,7 +113,7 @@ final class CroutonTests: XCTestCase { config: SnackbarConfig(title: "Title", dismissInterval: .tenSeconds(SnackbarAction(title: "Action", handler: {}))) ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -123,7 +126,7 @@ final class CroutonTests: XCTestCase { config: SnackbarConfig(title: "Title", dismissInterval: .infinite(SnackbarAction(title: "Action", handler: {})), forceDismiss: true) ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -137,7 +140,7 @@ final class CroutonTests: XCTestCase { config: SnackbarConfig(title: "Title", dismissInterval: .infinite(SnackbarAction(title: "Large Action", handler: {})), forceDismiss: true) ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } diff --git a/Tests/MisticaSwiftUITests/UI/DataCardTests.swift b/Tests/MisticaSwiftUITests/UI/DataCardTests.swift index 0339b78ab..ddc8ea796 100644 --- a/Tests/MisticaSwiftUITests/UI/DataCardTests.swift +++ b/Tests/MisticaSwiftUITests/UI/DataCardTests.swift @@ -11,6 +11,7 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class DataCardTests: XCTestCase { private enum Constants { static let headline = "Headline" @@ -20,10 +21,10 @@ final class DataCardTests: XCTestCase { static let multiLineMessage = "Nam non ipsum id metus cursus dictum. Praesent efficitur erat libero, vitae tempus orci iaculis id. Proin ipsum ante, auctor mattis rutrum sit amet, elementum vitae quam. Praesent velit lectus, lacinia ut accumsan sit amet, convallis non leo. Ut quis facilisis sapien. " } - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testAlternativeColors() { @@ -49,7 +50,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -66,7 +67,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -83,7 +84,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -98,7 +99,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: UIHostingController(rootView: dataCard), + of: UIHostingController(rootView: dataCard), as: .image(on: .iPhone8) ) } @@ -114,7 +115,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -130,7 +131,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -146,7 +147,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -164,7 +165,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -180,7 +181,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -199,7 +200,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -219,7 +220,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -239,7 +240,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -258,7 +259,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -274,7 +275,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } @@ -290,7 +291,7 @@ final class DataCardTests: XCTestCase { .padding(16) assertSnapshot( - matching: dataCard, + of: dataCard, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/EmptyStateTests.swift b/Tests/MisticaSwiftUITests/UI/EmptyStateTests.swift index 231285995..155832c2a 100644 --- a/Tests/MisticaSwiftUITests/UI/EmptyStateTests.swift +++ b/Tests/MisticaSwiftUITests/UI/EmptyStateTests.swift @@ -11,9 +11,12 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class EmptyStateTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testIconAsset() { @@ -26,7 +29,7 @@ final class EmptyStateTests: XCTestCase { ) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } @@ -42,7 +45,7 @@ final class EmptyStateTests: XCTestCase { .frame(width: 300, height: 300) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } @@ -57,7 +60,7 @@ final class EmptyStateTests: XCTestCase { ) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } @@ -73,7 +76,7 @@ final class EmptyStateTests: XCTestCase { .frame(height: 300) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } @@ -87,7 +90,7 @@ final class EmptyStateTests: XCTestCase { ) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } @@ -101,7 +104,7 @@ final class EmptyStateTests: XCTestCase { ) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } @@ -115,7 +118,7 @@ final class EmptyStateTests: XCTestCase { .frame(width: 150) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } @@ -129,7 +132,7 @@ final class EmptyStateTests: XCTestCase { ) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } @@ -146,7 +149,7 @@ final class EmptyStateTests: XCTestCase { .padding(24) assertSnapshot( - matching: emptyState, + of: emptyState, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/FeedbackTests.swift b/Tests/MisticaSwiftUITests/UI/FeedbackTests.swift index 68ffb3698..d4ee30035 100644 --- a/Tests/MisticaSwiftUITests/UI/FeedbackTests.swift +++ b/Tests/MisticaSwiftUITests/UI/FeedbackTests.swift @@ -11,6 +11,7 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class FeedbackTests: XCTestCase { private enum Constants { static let singleLineTitle = "Title" @@ -19,18 +20,17 @@ final class FeedbackTests: XCTestCase { static let multiLineSubtitle = "Nam non ipsum id metus cursus dictum. Praesent efficitur erat libero, vitae tempus orci iaculis id. Proin ipsum ante, auctor mattis rutrum sit amet, elementum vitae quam. Praesent velit lectus, lacinia ut accumsan sit amet, convallis non leo. Ut quis facilisis sapien. " } - override class func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .failed) { + super.invokeTest() + } } func testInformative() { let feedback = makeTemplate(style: .informative) - assertSnapshot( - matching: UIHostingController(rootView: feedback), + assertSnapshotWithoutAnimations( + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -38,8 +38,8 @@ final class FeedbackTests: XCTestCase { func testError() { let feedback = makeTemplate(style: .error(reference: nil)) - assertSnapshot( - matching: UIHostingController(rootView: feedback), + assertSnapshotWithoutAnimations( + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -47,8 +47,8 @@ final class FeedbackTests: XCTestCase { func testErrorReference() { let feedback = makeTemplate(style: .error(reference: "Error reference: #1992")) - assertSnapshot( - matching: UIHostingController(rootView: feedback), + assertSnapshotWithoutAnimations( + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -56,8 +56,8 @@ final class FeedbackTests: XCTestCase { func testSuccess() { let feedback = makeTemplate(style: .success) - assertSnapshot( - matching: UIHostingController(rootView: feedback), + assertSnapshotWithoutAnimations( + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -65,8 +65,8 @@ final class FeedbackTests: XCTestCase { func testFeedback() { let feedback = makeTemplate(style: .feedback(Image(systemName: "swift"))) - assertSnapshot( - matching: UIHostingController(rootView: feedback), + assertSnapshotWithoutAnimations( + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -75,7 +75,7 @@ final class FeedbackTests: XCTestCase { let feedback = makeTemplate(style: .informative, title: Constants.multiLineTitle, message: Constants.multiLineSubtitle) assertSnapshot( - matching: UIHostingController(rootView: feedback), + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -94,7 +94,7 @@ final class FeedbackTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: feedback), + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -113,7 +113,7 @@ final class FeedbackTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: feedback), + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -135,7 +135,7 @@ final class FeedbackTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: feedback), + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -154,7 +154,7 @@ final class FeedbackTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: feedback), + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -172,8 +172,8 @@ final class FeedbackTests: XCTestCase { Button("Link", action: {}) } - assertSnapshot( - matching: UIHostingController(rootView: feedback), + assertSnapshotWithoutAnimations( + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } @@ -191,8 +191,8 @@ final class FeedbackTests: XCTestCase { Button("Link", action: {}) } - assertSnapshot( - matching: UIHostingController(rootView: feedback), + assertSnapshotWithoutAnimations( + of: UIHostingController(rootView: feedback), as: .image(on: .iPhone8) ) } diff --git a/Tests/MisticaSwiftUITests/UI/GradientTests.swift b/Tests/MisticaSwiftUITests/UI/GradientTests.swift index 5aaf33a74..525a9a9ef 100644 --- a/Tests/MisticaSwiftUITests/UI/GradientTests.swift +++ b/Tests/MisticaSwiftUITests/UI/GradientTests.swift @@ -11,9 +11,12 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class GradientTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testGradientInView() { @@ -21,7 +24,7 @@ final class GradientTests: XCTestCase { let misticaColor: MisticaColor = .gradient(misticaGradient) assertSnapshot( - matching: makeTemplate(misticaColor: misticaColor), + of: makeTemplate(misticaColor: misticaColor), as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/HeaderTests.swift b/Tests/MisticaSwiftUITests/UI/HeaderTests.swift index 9a3afcc8c..33b163b40 100644 --- a/Tests/MisticaSwiftUITests/UI/HeaderTests.swift +++ b/Tests/MisticaSwiftUITests/UI/HeaderTests.swift @@ -11,12 +11,12 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class HeaderTests: XCTestCase { - override class func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } } @@ -30,7 +30,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -42,7 +42,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -54,7 +54,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -66,7 +66,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -78,7 +78,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -90,7 +90,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -106,7 +106,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -120,7 +120,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -134,7 +134,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -148,7 +148,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -166,7 +166,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -180,7 +180,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } @@ -194,7 +194,7 @@ extension HeaderTests { ) assertSnapshot( - matching: UIHostingController(rootView: header), + of: UIHostingController(rootView: header), as: .image(on: .iPhone8) ) } diff --git a/Tests/MisticaSwiftUITests/UI/InputFieldTests.swift b/Tests/MisticaSwiftUITests/UI/InputFieldTests.swift index 78508d340..963ce9f3e 100644 --- a/Tests/MisticaSwiftUITests/UI/InputFieldTests.swift +++ b/Tests/MisticaSwiftUITests/UI/InputFieldTests.swift @@ -11,19 +11,19 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class InputFieldTests: XCTestCase { - override class func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testText() { let input = makeTemplate(style: .text) assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -32,7 +32,7 @@ final class InputFieldTests: XCTestCase { let input = makeTemplate(style: .secure) assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -41,7 +41,7 @@ final class InputFieldTests: XCTestCase { let input = makeTemplate(style: .phone(code: "+34")) assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -50,7 +50,7 @@ final class InputFieldTests: XCTestCase { let input = makeTemplate(style: .search) assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -59,7 +59,7 @@ final class InputFieldTests: XCTestCase { let input = makeTemplate(style: .date()) assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -68,7 +68,7 @@ final class InputFieldTests: XCTestCase { let input = makeTemplate(style: .dropdown(options: ["1", "2"])) assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -77,7 +77,7 @@ final class InputFieldTests: XCTestCase { let input = makeTemplate(style: .text, text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ullamcorper at justo eget porta. Pellentesque sit amet felis vel eros commodo euismod vel quis nisl.") assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -86,7 +86,7 @@ final class InputFieldTests: XCTestCase { let input = makeTemplate(style: .text, placeholder: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ullamcorper at justo eget porta. Pellentesque sit amet felis vel eros commodo euismod vel quis nisl.") assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -95,7 +95,7 @@ final class InputFieldTests: XCTestCase { let input = makeTemplate(style: .text, assistiveText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ullamcorper at justo eget porta. Pellentesque sit amet felis vel eros commodo euismod vel quis nisl.") assertSnapshot( - matching: input, + of: input, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/ListTests.swift b/Tests/MisticaSwiftUITests/UI/ListTests.swift index ff4bfe631..af0e1ae8e 100644 --- a/Tests/MisticaSwiftUITests/UI/ListTests.swift +++ b/Tests/MisticaSwiftUITests/UI/ListTests.swift @@ -11,21 +11,24 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class ListTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testFullwidthRowContent() { assertSnapshot( - matching: makeTemplateWithStyle(style: .fullwidth), + of: makeTemplateWithStyle(style: .fullwidth), as: .image ) } func testBoxedRowContent() { assertSnapshot( - matching: makeTemplateWithStyle(style: .boxed), + of: makeTemplateWithStyle(style: .boxed), as: .image ) } @@ -39,7 +42,7 @@ final class ListTests: XCTestCase { ).frame(width: 400, height: 200) assertSnapshot( - matching: row, + of: row, as: .image ) } @@ -53,7 +56,7 @@ final class ListTests: XCTestCase { ).frame(width: 400, height: 200) assertSnapshot( - matching: row, + of: row, as: .image ) } @@ -90,7 +93,7 @@ final class ListTests: XCTestCase { .frame(width: 250, height: 400) assertSnapshot( - matching: rows, + of: rows, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/RadioButtonTests.swift b/Tests/MisticaSwiftUITests/UI/RadioButtonTests.swift index 34776a5a3..46e480385 100644 --- a/Tests/MisticaSwiftUITests/UI/RadioButtonTests.swift +++ b/Tests/MisticaSwiftUITests/UI/RadioButtonTests.swift @@ -11,16 +11,19 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class RadioButtonTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testSelectedRadioButton() { let radioButton = RadioButton(isSelected: .constant(true)) assertSnapshot( - matching: radioButton, + of: radioButton, as: .image ) } @@ -29,7 +32,7 @@ final class RadioButtonTests: XCTestCase { let radioButton = RadioButton(isSelected: .constant(false)) assertSnapshot( - matching: radioButton, + of: radioButton, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/SkeletonTests.swift b/Tests/MisticaSwiftUITests/UI/SkeletonTests.swift index 0bc89da8c..18a3e1f68 100644 --- a/Tests/MisticaSwiftUITests/UI/SkeletonTests.swift +++ b/Tests/MisticaSwiftUITests/UI/SkeletonTests.swift @@ -11,16 +11,19 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class SkeletonTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testLineSkeleton() { let skeleton = Skeleton(type: .line(width: 300)) assertSnapshot( - matching: skeleton, + of: skeleton, as: .image ) } @@ -30,7 +33,7 @@ final class SkeletonTests: XCTestCase { .frame(width: 300, height: 60) assertSnapshot( - matching: skeleton, + of: skeleton, as: .image ) } @@ -40,7 +43,7 @@ final class SkeletonTests: XCTestCase { .frame(width: 300, height: 110) assertSnapshot( - matching: skeleton, + of: skeleton, as: .image ) } @@ -49,7 +52,7 @@ final class SkeletonTests: XCTestCase { let skeleton = Skeleton(type: .circle(size: CGSize(width: 40, height: 40))) assertSnapshot( - matching: skeleton, + of: skeleton, as: .image ) } @@ -59,7 +62,7 @@ final class SkeletonTests: XCTestCase { .frame(width: 300) assertSnapshot( - matching: skeleton, + of: skeleton, as: .image ) } @@ -68,7 +71,7 @@ final class SkeletonTests: XCTestCase { let skeleton = Skeleton(type: .rectangle(width: 360, height: 180, isRounded: true)) assertSnapshot( - matching: skeleton, + of: skeleton, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/SnackbarTests.swift b/Tests/MisticaSwiftUITests/UI/SnackbarTests.swift index 982b1c48c..53fdecda5 100644 --- a/Tests/MisticaSwiftUITests/UI/SnackbarTests.swift +++ b/Tests/MisticaSwiftUITests/UI/SnackbarTests.swift @@ -11,9 +11,12 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class SnackbarTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testTitle() { @@ -25,7 +28,7 @@ final class SnackbarTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -39,7 +42,7 @@ final class SnackbarTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -53,7 +56,7 @@ final class SnackbarTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -68,7 +71,7 @@ final class SnackbarTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -83,7 +86,7 @@ final class SnackbarTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -97,7 +100,7 @@ final class SnackbarTests: XCTestCase { ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -110,7 +113,7 @@ final class SnackbarTests: XCTestCase { config: SnackbarConfig(title: "Title", dismissInterval: .tenSeconds(SnackbarAction(title: "Action", handler: {}))) ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -123,7 +126,7 @@ final class SnackbarTests: XCTestCase { config: SnackbarConfig(title: "Title", dismissInterval: .infinite(SnackbarAction(title: "Action", handler: {})), forceDismiss: true) ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } @@ -137,7 +140,7 @@ final class SnackbarTests: XCTestCase { config: SnackbarConfig(title: "Title", dismissInterval: .infinite(SnackbarAction(title: "Large Action", handler: {})), forceDismiss: true) ) assertSnapshot( - matching: UIHostingController(rootView: view), + of: UIHostingController(rootView: view), as: .image(on: .iPhone8) ) } diff --git a/Tests/MisticaSwiftUITests/UI/StepperTests.swift b/Tests/MisticaSwiftUITests/UI/StepperTests.swift index 9e7e977b4..ddea35b2e 100644 --- a/Tests/MisticaSwiftUITests/UI/StepperTests.swift +++ b/Tests/MisticaSwiftUITests/UI/StepperTests.swift @@ -11,19 +11,19 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class StepperTests: XCTestCase { - override class func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testStepZeroOfFourSteps() { let input = makeTemplate(step: 0, steps: 4) assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -32,7 +32,7 @@ final class StepperTests: XCTestCase { let input = makeTemplate(step: 1, steps: 4) assertSnapshot( - matching: input, + of: input, as: .image ) } @@ -41,7 +41,7 @@ final class StepperTests: XCTestCase { let input = makeTemplate(step: 4, steps: 4) assertSnapshot( - matching: input, + of: input, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/TabsTests.swift b/Tests/MisticaSwiftUITests/UI/TabsTests.swift index b59e804cb..468a6477c 100644 --- a/Tests/MisticaSwiftUITests/UI/TabsTests.swift +++ b/Tests/MisticaSwiftUITests/UI/TabsTests.swift @@ -11,9 +11,12 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class TabsTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testTwoLongTabs() { @@ -21,7 +24,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -31,7 +34,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -41,7 +44,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -51,7 +54,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -61,7 +64,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -71,7 +74,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -81,7 +84,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -91,7 +94,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -101,7 +104,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } @@ -111,7 +114,7 @@ final class TabsTests: XCTestCase { .frame(width: 400) assertSnapshot( - matching: tabs, + of: tabs, as: .image ) } diff --git a/Tests/MisticaSwiftUITests/UI/TagTests.swift b/Tests/MisticaSwiftUITests/UI/TagTests.swift index fc27410bc..b3b128f19 100644 --- a/Tests/MisticaSwiftUITests/UI/TagTests.swift +++ b/Tests/MisticaSwiftUITests/UI/TagTests.swift @@ -11,28 +11,31 @@ import SnapshotTesting import SwiftUI import XCTest +@MainActor final class TagTests: XCTestCase { - override class func setUp() { - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testLargeTagContent() { assertSnapshot( - matching: makeTemplateWithAllTags(content: "Large tag content"), + of: makeTemplateWithAllTags(content: "Large tag content"), as: .image ) } func testSmallTagContent() { assertSnapshot( - matching: makeTemplateWithAllTags(content: "Tag"), + of: makeTemplateWithAllTags(content: "Tag"), as: .image ) } func testSmallTagContentWitchIcon() { assertSnapshot( - matching: makeTemplateWithAllTags(content: "Tag", icon: true), + of: makeTemplateWithAllTags(content: "Tag", icon: true), as: .image ) } diff --git a/Tests/MisticaTests/UI/BadgeTests.swift b/Tests/MisticaTests/UI/BadgeTests.swift index 5b275ddff..d71f22821 100644 --- a/Tests/MisticaTests/UI/BadgeTests.swift +++ b/Tests/MisticaTests/UI/BadgeTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class BadgeTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testFlagBadge() { @@ -35,7 +36,7 @@ final class BadgeTests: XCTestCase { view.secondNumericBadge.style = .numeric view.secondNumericBadge.value = 1_000 - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } } diff --git a/Tests/MisticaTests/UI/ButtonTests.swift b/Tests/MisticaTests/UI/ButtonTests.swift index 7aeeb7fc9..70bb3746d 100644 --- a/Tests/MisticaTests/UI/ButtonTests.swift +++ b/Tests/MisticaTests/UI/ButtonTests.swift @@ -10,6 +10,7 @@ import SnapshotTesting import XCTest +@MainActor final class ButtonTests: XCTestCase { enum Constants { static let leftImage = UIImage(systemName: "plus")! @@ -18,9 +19,16 @@ final class ButtonTests: XCTestCase { override class func setUp() { super.setUp() - UIView.setAnimationsEnabled(false) - isRecording = false + Task { @MainActor in + UIView.setAnimationsEnabled(false) + } + } + + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -220,7 +228,7 @@ final class ButtonTests: XCTestCase { buttonNormalState.title = "A very very very long long long long teeeext" assertSnapshot( - matching: buttonNormalState, + of: buttonNormalState, as: .image(size: CGSize(width: 156, height: 48)) ) } @@ -233,7 +241,7 @@ final class ButtonTests: XCTestCase { buttonNormalState.isLoading = true assertSnapshot( - matching: buttonNormalState, + of: buttonNormalState, as: .image(size: CGSize(width: 156, height: 48)) ) } @@ -247,7 +255,7 @@ final class ButtonTests: XCTestCase { button.rightImage = .chevron assertSnapshot( - matching: button, + of: button, as: .image(size: CGSize(width: 500, height: 48)) ) } @@ -256,7 +264,7 @@ final class ButtonTests: XCTestCase { MisticaConfig.brandStyle = .vivo assertSnapshot( - matching: makeTemplateWithRegularAndSmallButtonsAndLinkButton(), + of: makeTemplateWithRegularAndSmallButtonsAndLinkButton(), as: .image ) } @@ -265,7 +273,7 @@ final class ButtonTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: makeTemplateAlignment(contentMode: .left), + of: makeTemplateAlignment(contentMode: .left), as: .image ) } @@ -274,7 +282,7 @@ final class ButtonTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: makeTemplateAlignment(contentMode: .right), + of: makeTemplateAlignment(contentMode: .right), as: .image ) } @@ -289,7 +297,7 @@ final class ButtonTests: XCTestCase { buttonNormalState.loadingTitle = "Loading" assertSnapshot( - matching: buttonNormalState, + of: buttonNormalState, as: .image(size: buttonNormalState.intrinsicContentSize), named: "assertInitialState" ) @@ -297,7 +305,7 @@ final class ButtonTests: XCTestCase { buttonNormalState.isLoading = true assertSnapshot( - matching: buttonNormalState, + of: buttonNormalState, as: .image(size: buttonNormalState.intrinsicContentSize), named: "finalState" ) @@ -311,7 +319,7 @@ final class ButtonTests: XCTestCase { button.loadingTitle = "Loading" assertSnapshot( - matching: button, + of: button, as: .image(size: button.intrinsicContentSize), named: "assertInitialState" ) @@ -320,7 +328,7 @@ final class ButtonTests: XCTestCase { button.isLoading = false assertSnapshot( - matching: button, + of: button, as: .image(size: button.intrinsicContentSize), named: "finalState" ) @@ -333,7 +341,7 @@ final class ButtonTests: XCTestCase { button.title = "Regular" assertSnapshot( - matching: button, + of: button, as: .image(size: button.intrinsicContentSize), named: "assertInitialState" ) @@ -342,7 +350,7 @@ final class ButtonTests: XCTestCase { button.isEnabled = true assertSnapshot( - matching: button, + of: button, as: .image(size: button.intrinsicContentSize), named: "finalState" ) @@ -355,7 +363,7 @@ final class ButtonTests: XCTestCase { button.title = "Regular" assertSnapshot( - matching: button, + of: button, as: .image(size: button.intrinsicContentSize), named: "assertInitialState" ) @@ -364,7 +372,7 @@ final class ButtonTests: XCTestCase { button.isSelected = false assertSnapshot( - matching: button, + of: button, as: .image(size: button.intrinsicContentSize), named: "finalState" ) @@ -386,7 +394,7 @@ final class ButtonTests: XCTestCase { view.buttonCentered.isLoading = true assertSnapshot( - matching: view.asRootOfViewController(), + of: view.asRootOfViewController(), as: .image(on: .iPhoneX) // We need a device with Safe Area ) } @@ -410,145 +418,147 @@ final class ButtonTests: XCTestCase { // MARK: - Helpers -private func makeTemplateWithAllButtonStates(style: Button.Style, isSmall: Bool, leftImage: Bool = false, rightImage: Button.RightImage? = nil) -> UIView { - let leftImage = leftImage ? Button.LeftImage.custom(image: ButtonTests.Constants.leftImage) : nil - - let buttonNormalState = Button() - buttonNormalState.title = "Normal" - buttonNormalState.style = style - buttonNormalState.isSmall = isSmall - buttonNormalState.leftImage = leftImage - buttonNormalState.rightImage = rightImage - - let buttonDisabledState = Button() - buttonDisabledState.title = "Disabled" - buttonDisabledState.style = style - buttonDisabledState.isEnabled = false - buttonDisabledState.isSmall = isSmall - buttonDisabledState.leftImage = leftImage - buttonDisabledState.rightImage = rightImage - - let buttonSelectedState = Button() - buttonSelectedState.title = "Selected" - buttonSelectedState.style = style - buttonSelectedState.isSelected = true - buttonSelectedState.isSmall = isSmall - buttonSelectedState.leftImage = leftImage - buttonSelectedState.rightImage = rightImage - - let buttonLoadingState = Button() - buttonLoadingState.loadingTitle = "Loading" - buttonLoadingState.style = style - buttonLoadingState.isLoading = true - buttonLoadingState.isSmall = isSmall - buttonLoadingState.leftImage = leftImage - buttonLoadingState.rightImage = rightImage - - let vStack = UIStackView(arrangedSubviews: [ - buttonNormalState, - buttonSelectedState, - buttonDisabledState, - buttonLoadingState - ]) - - vStack.axis = .vertical - vStack.alignment = .center - vStack.spacing = 0 - vStack.frame = CGRect( - x: 0, - y: 0, - width: buttonLoadingState.intrinsicContentSize.width, - height: buttonLoadingState.intrinsicContentSize.height * 4 - ) - - return vStack -} +private extension ButtonTests { + func makeTemplateWithAllButtonStates(style: Button.Style, isSmall: Bool, leftImage: Bool = false, rightImage: Button.RightImage? = nil) -> UIView { + let leftImage = leftImage ? Button.LeftImage.custom(image: ButtonTests.Constants.leftImage) : nil -private func makeTemplateWithRegularAndSmallButtonsAndLinkButton() -> UIStackView { - let smallButton = Button() - smallButton.title = "O" - smallButton.isSmall = true - - let regularButton = Button() - regularButton.title = "O" - - let linkButton = Button() - linkButton.title = "O" - linkButton.isSelected = true - linkButton.style = .link - - let vStack = UIStackView(arrangedSubviews: [ - smallButton, - regularButton, - linkButton - ]) - - let expectedWidth = vStack.arrangedSubviews - .map(\.intrinsicContentSize) - .map(\.width) - .reduce(CGFloat(0), CGFloat.maximum) - let expectedHeight = vStack.arrangedSubviews - .map(\.intrinsicContentSize) - .map(\.height) - .reduce(CGFloat(0), +) - - vStack.axis = .vertical - vStack.alignment = .center - vStack.spacing = 0 - vStack.frame = CGRect( - x: 0, - y: 0, - width: expectedWidth, - height: expectedHeight - ) - - return vStack -} - -private func makeTemplateAlignment(contentMode: UIView.ContentMode) -> UIView { - let containerView = UIView() - containerView.backgroundColor = .white - - let title = UILabel() - title.text = "Lorem ipsum dolor sit amet" - title.translatesAutoresizingMaskIntoConstraints = false - - let linkButton = Button() - linkButton.title = "Link" - linkButton.style = .link - linkButton.contentMode = contentMode - linkButton.translatesAutoresizingMaskIntoConstraints = false - - containerView.addSubview(title) - containerView.addSubview(linkButton) - - NSLayoutConstraint.activate([ - title.topAnchor.constraint(equalTo: containerView.topAnchor), - title.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -5), - title.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 5), - linkButton.topAnchor.constraint(equalTo: title.bottomAnchor) - ]) - - switch contentMode { - case .left: - linkButton.leadingAnchor.constraint(equalTo: title.leadingAnchor).isActive = true - case .right: - linkButton.trailingAnchor.constraint(equalTo: title.trailingAnchor).isActive = true - default: - fatalError("Sorry but at the moment of implementing this, I only took into account left and right contentMode") - } - - let expectedHeight = containerView.subviews - .map(\.intrinsicContentSize) - .map(\.height) - .reduce(CGFloat(0), +) - - containerView.frame = CGRect( - x: 0, - y: 0, - width: title.intrinsicContentSize.width + 10, // title width plus some margin - height: expectedHeight - ) - - return containerView + let buttonNormalState = Button() + buttonNormalState.title = "Normal" + buttonNormalState.style = style + buttonNormalState.isSmall = isSmall + buttonNormalState.leftImage = leftImage + buttonNormalState.rightImage = rightImage + + let buttonDisabledState = Button() + buttonDisabledState.title = "Disabled" + buttonDisabledState.style = style + buttonDisabledState.isEnabled = false + buttonDisabledState.isSmall = isSmall + buttonDisabledState.leftImage = leftImage + buttonDisabledState.rightImage = rightImage + + let buttonSelectedState = Button() + buttonSelectedState.title = "Selected" + buttonSelectedState.style = style + buttonSelectedState.isSelected = true + buttonSelectedState.isSmall = isSmall + buttonSelectedState.leftImage = leftImage + buttonSelectedState.rightImage = rightImage + + let buttonLoadingState = Button() + buttonLoadingState.loadingTitle = "Loading" + buttonLoadingState.style = style + buttonLoadingState.isLoading = true + buttonLoadingState.isSmall = isSmall + buttonLoadingState.leftImage = leftImage + buttonLoadingState.rightImage = rightImage + + let vStack = UIStackView(arrangedSubviews: [ + buttonNormalState, + buttonSelectedState, + buttonDisabledState, + buttonLoadingState + ]) + + vStack.axis = .vertical + vStack.alignment = .center + vStack.spacing = 0 + vStack.frame = CGRect( + x: 0, + y: 0, + width: buttonLoadingState.intrinsicContentSize.width, + height: buttonLoadingState.intrinsicContentSize.height * 4 + ) + + return vStack + } + + func makeTemplateWithRegularAndSmallButtonsAndLinkButton() -> UIStackView { + let smallButton = Button() + smallButton.title = "O" + smallButton.isSmall = true + + let regularButton = Button() + regularButton.title = "O" + + let linkButton = Button() + linkButton.title = "O" + linkButton.isSelected = true + linkButton.style = .link + + let vStack = UIStackView(arrangedSubviews: [ + smallButton, + regularButton, + linkButton + ]) + + let expectedWidth = vStack.arrangedSubviews + .map(\.intrinsicContentSize) + .map(\.width) + .reduce(CGFloat(0), CGFloat.maximum) + let expectedHeight = vStack.arrangedSubviews + .map(\.intrinsicContentSize) + .map(\.height) + .reduce(CGFloat(0), +) + + vStack.axis = .vertical + vStack.alignment = .center + vStack.spacing = 0 + vStack.frame = CGRect( + x: 0, + y: 0, + width: expectedWidth, + height: expectedHeight + ) + + return vStack + } + + func makeTemplateAlignment(contentMode: UIView.ContentMode) -> UIView { + let containerView = UIView() + containerView.backgroundColor = .white + + let title = UILabel() + title.text = "Lorem ipsum dolor sit amet" + title.translatesAutoresizingMaskIntoConstraints = false + + let linkButton = Button() + linkButton.title = "Link" + linkButton.style = .link + linkButton.contentMode = contentMode + linkButton.translatesAutoresizingMaskIntoConstraints = false + + containerView.addSubview(title) + containerView.addSubview(linkButton) + + NSLayoutConstraint.activate([ + title.topAnchor.constraint(equalTo: containerView.topAnchor), + title.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -5), + title.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 5), + linkButton.topAnchor.constraint(equalTo: title.bottomAnchor) + ]) + + switch contentMode { + case .left: + linkButton.leadingAnchor.constraint(equalTo: title.leadingAnchor).isActive = true + case .right: + linkButton.trailingAnchor.constraint(equalTo: title.trailingAnchor).isActive = true + default: + fatalError("Sorry but at the moment of implementing this, I only took into account left and right contentMode") + } + + let expectedHeight = containerView.subviews + .map(\.intrinsicContentSize) + .map(\.height) + .reduce(CGFloat(0), +) + + containerView.frame = CGRect( + x: 0, + y: 0, + width: title.intrinsicContentSize.width + 10, // title width plus some margin + height: expectedHeight + ) + + return containerView + } } diff --git a/Tests/MisticaTests/UI/CalloutTests.swift b/Tests/MisticaTests/UI/CalloutTests.swift index ce4ce4505..e15ab1fcb 100644 --- a/Tests/MisticaTests/UI/CalloutTests.swift +++ b/Tests/MisticaTests/UI/CalloutTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class CalloutTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -36,7 +37,7 @@ final class CalloutTests: XCTestCase { title: "This is a title" ) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testMinimumContent() { @@ -44,7 +45,7 @@ final class CalloutTests: XCTestCase { let view = makeBasicCallout() - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContent() { @@ -52,7 +53,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons() - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutIcon() { @@ -60,7 +61,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(asset: .none) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutTitle() { @@ -68,7 +69,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(title: nil) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryButtonOnly() { @@ -76,7 +77,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(actions: CalloutConfiguration.CalloutActions.primary(AnyValues.primary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryButtonOnlyWithoutAsset() { @@ -84,7 +85,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(asset: .none, actions: CalloutConfiguration.CalloutActions.primary(AnyValues.primary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryAndLinkButtonsOnly() { @@ -92,7 +93,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(actions: CalloutConfiguration.CalloutActions.primaryAndLink(primary: AnyValues.primary, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryAndLinkButtonsOnlyWithoutAsset() { @@ -100,7 +101,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(asset: .none, actions: CalloutConfiguration.CalloutActions.primaryAndLink(primary: AnyValues.primary, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryAndSecondaryButtonsOnly() { @@ -108,7 +109,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(actions: CalloutConfiguration.CalloutActions.primaryAndSecondary(primary: AnyValues.primary, secondary: AnyValues.secondary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryAndSecondaryButtonsOnlyWithoutAsset() { @@ -116,7 +117,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(asset: .none, actions: CalloutConfiguration.CalloutActions.primaryAndSecondary(primary: AnyValues.primary, secondary: AnyValues.secondary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testSecondaryButtonOnly() { @@ -124,7 +125,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(actions: CalloutConfiguration.CalloutActions.secondary(AnyValues.secondary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testSecondaryButtonOnlyWithoutAsset() { @@ -132,7 +133,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(asset: .none, actions: CalloutConfiguration.CalloutActions.secondary(AnyValues.secondary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testSecondaryAndLinkButtonsOnly() { @@ -140,7 +141,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(actions: CalloutConfiguration.CalloutActions.secondaryAndLink(secondary: AnyValues.secondary, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testSecondaryAndLinkButtonsOnlyWithoutAsset() { @@ -148,7 +149,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(asset: .none, actions: CalloutConfiguration.CalloutActions.secondaryAndLink(secondary: AnyValues.secondary, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testLinkButtonOnly() { @@ -156,7 +157,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(actions: CalloutConfiguration.CalloutActions.link(AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testLinkButtonOnlyWithoutAsset() { @@ -164,7 +165,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(asset: .none, actions: CalloutConfiguration.CalloutActions.link(AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } // MARK: Behaviour @@ -175,7 +176,7 @@ final class CalloutTests: XCTestCase { let view = makeCalloutWithContentAndButtons(actions: CalloutConfiguration.CalloutActions.primaryAndLink(primary: AnyValues.primary, link: AnyValues.link)) view.primaryButton.isLoading = true - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } // MARK: XIB integration @@ -193,7 +194,7 @@ final class CalloutTests: XCTestCase { view.callout.contentConfiguration = configurationWithActions assertSnapshot( - matching: view.asRootOfViewController(), + of: view.asRootOfViewController(), as: .image(on: .iPhoneX) ) } diff --git a/Tests/MisticaTests/UI/CarouselTests.swift b/Tests/MisticaTests/UI/CarouselTests.swift index 3b8275e2e..d32289d34 100644 --- a/Tests/MisticaTests/UI/CarouselTests.swift +++ b/Tests/MisticaTests/UI/CarouselTests.swift @@ -10,14 +10,19 @@ import SnapshotTesting import XCTest +@MainActor final class CarouselTests: XCTestCase { override func setUp() { super.setUp() - - isRecording = false MisticaConfig.brandStyle = .movistar } + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } + } + // MARK: - Layout // MARK: - Default config @@ -28,7 +33,7 @@ final class CarouselTests: XCTestCase { ) assertSnapshot( - matching: carouselTestsViewController, + of: carouselTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -40,7 +45,7 @@ final class CarouselTests: XCTestCase { ) assertSnapshot( - matching: carouselTestsViewController, + of: carouselTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -52,7 +57,7 @@ final class CarouselTests: XCTestCase { ) assertSnapshot( - matching: carouselTestsViewController, + of: carouselTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -65,7 +70,7 @@ final class CarouselTests: XCTestCase { ) assertSnapshot( - matching: carouselTestsViewController, + of: carouselTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -79,7 +84,7 @@ final class CarouselTests: XCTestCase { ) assertSnapshot( - matching: carouselTestsViewController, + of: carouselTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -94,7 +99,7 @@ final class CarouselTests: XCTestCase { ) assertSnapshot( - matching: carouselTestsViewController, + of: carouselTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -216,6 +221,6 @@ extension CarouselTests { enum AnyValues { static let title = "Any title" static let subtitle = "Any subtitle" - static var image = UIImage(color: .green) + static let image = UIImage(color: .green) } } diff --git a/Tests/MisticaTests/UI/CheckboxTests.swift b/Tests/MisticaTests/UI/CheckboxTests.swift index a09caa1d9..281a97092 100644 --- a/Tests/MisticaTests/UI/CheckboxTests.swift +++ b/Tests/MisticaTests/UI/CheckboxTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class CheckboxTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -42,7 +43,7 @@ final class CheckboxTests: XCTestCase { checkbox.isChecked = false assertSnapshot( - matching: checkbox, + of: checkbox, as: .image(size: checkbox.intrinsicContentSize), named: "assertInitialState" ) @@ -50,7 +51,7 @@ final class CheckboxTests: XCTestCase { checkbox.isChecked = true assertSnapshot( - matching: checkbox, + of: checkbox, as: .image(size: checkbox.intrinsicContentSize), named: "finalState" ) @@ -64,7 +65,7 @@ final class CheckboxTests: XCTestCase { let view = CheckboxXIBIntegration.viewFromNib() assertSnapshot( - matching: view, + of: view, as: .image ) } @@ -72,13 +73,15 @@ final class CheckboxTests: XCTestCase { // MARK: - Helpers -private func makeTemplateWithCheckboxState(isChecked: Bool) -> UIView { - let checkbox = Checkbox(frame: CGRect(origin: .zero, size: CGSize(width: 18, height: 18))) - checkbox.isChecked = isChecked +private extension CheckboxTests { + func makeTemplateWithCheckboxState(isChecked: Bool) -> UIView { + let checkbox = Checkbox(frame: CGRect(origin: .zero, size: CGSize(width: 18, height: 18))) + checkbox.isChecked = isChecked - let containerView = UIView(frame: CGRect(origin: .zero, size: checkbox.intrinsicContentSize)) - containerView.backgroundColor = .white - containerView.addSubview(checkbox) + let containerView = UIView(frame: CGRect(origin: .zero, size: checkbox.intrinsicContentSize)) + containerView.backgroundColor = .white + containerView.addSubview(checkbox) - return containerView + return containerView + } } diff --git a/Tests/MisticaTests/UI/ControlsTests.swift b/Tests/MisticaTests/UI/ControlsTests.swift index bb8b2afe4..8bc904fa6 100644 --- a/Tests/MisticaTests/UI/ControlsTests.swift +++ b/Tests/MisticaTests/UI/ControlsTests.swift @@ -10,11 +10,12 @@ import Mistica import SnapshotTesting import XCTest +@MainActor final class ControlsTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Switch Style @@ -47,14 +48,14 @@ final class ControlsTests: XCTestCase { func testTabBarControl() { let tabView = makeTabBarTemplate() assertSnapshot( - matching: tabView, + of: tabView, as: .image(size: CGSize(width: 420, height: 60)) ) tabView.overrideUserInterfaceStyle = .dark assertSnapshot( - matching: tabView, + of: tabView, as: .image(size: CGSize(width: 420, height: 60)), named: "with-dark-style" ) @@ -79,7 +80,7 @@ final class ControlsTests: XCTestCase { segmentedControl.selectedSegmentIndex = 1 assertSnapshot( - matching: segmentedControl, + of: segmentedControl, as: .image(size: segmentedControl.intrinsicContentSize), named: "assertInitialState" ) @@ -87,7 +88,7 @@ final class ControlsTests: XCTestCase { segmentedControl.selectedSegmentIndex = 3 assertSnapshot( - matching: segmentedControl, + of: segmentedControl, as: .image(size: segmentedControl.intrinsicContentSize), named: "finalState" ) @@ -103,7 +104,7 @@ final class ControlsTests: XCTestCase { tabBarController.selectedIndex = 1 assertSnapshot( - matching: tabBarController, + of: tabBarController, as: .image(size: CGSize(width: 420, height: 60)), named: "assertInitialState" ) @@ -111,7 +112,7 @@ final class ControlsTests: XCTestCase { tabBarController.selectedIndex = 2 assertSnapshot( - matching: tabBarController, + of: tabBarController, as: .image(size: CGSize(width: 420, height: 60)), named: "finalState" ) @@ -128,7 +129,7 @@ final class ControlsTests: XCTestCase { pageControl.currentPage = 3 assertSnapshot( - matching: pageControl, + of: pageControl, as: .image(size: pageControl.intrinsicContentSize), named: "assertInitialState" ) @@ -136,7 +137,7 @@ final class ControlsTests: XCTestCase { pageControl.currentPage = 1 assertSnapshot( - matching: pageControl, + of: pageControl, as: .image(size: pageControl.intrinsicContentSize), named: "finalState" ) diff --git a/Tests/MisticaTests/UI/CroutonTests.swift b/Tests/MisticaTests/UI/CroutonTests.swift index 15d1eef34..ea4130ba0 100644 --- a/Tests/MisticaTests/UI/CroutonTests.swift +++ b/Tests/MisticaTests/UI/CroutonTests.swift @@ -10,12 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class CroutonTests: XCTestCase { - override func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testInfoCrouton() { diff --git a/Tests/MisticaTests/UI/DataCardTests.swift b/Tests/MisticaTests/UI/DataCardTests.swift index aeadfd3e0..742fc19c7 100644 --- a/Tests/MisticaTests/UI/DataCardTests.swift +++ b/Tests/MisticaTests/UI/DataCardTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class DataCardTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -32,7 +33,7 @@ final class DataCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(asset: .image(.init(color: .cyan))) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testShowAssetOfTypeIcon() { @@ -40,7 +41,7 @@ final class DataCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(asset: .icon(.init(color: .cyan), backgroundColor: .black)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testMinimumContent() { @@ -48,7 +49,7 @@ final class DataCardTests: XCTestCase { let view = makeBasicCard() - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContent() { @@ -56,7 +57,7 @@ final class DataCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons() - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutIcon() { @@ -64,7 +65,7 @@ final class DataCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(asset: .none) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutHeadline() { @@ -72,7 +73,7 @@ final class DataCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(headline: nil) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutSubtitle() { @@ -80,7 +81,7 @@ final class DataCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(subtitle: nil) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutFragment() { @@ -88,7 +89,7 @@ final class DataCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(hasFragment: false) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testTextsWithMultiLine() { @@ -100,7 +101,7 @@ final class DataCardTests: XCTestCase { descriptionTitle: "Mauris vel nisi efficitur, fringilla urna at, gravida nunc. Sed eu dui sit amet est fringilla eleifend. Ut aliquam, tortor ac varius sodales" ) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryButtonsOnly() { @@ -108,7 +109,7 @@ final class DataCardTests: XCTestCase { let view = makeBasicCard(buttons: .primary(AnyValues.button)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryAndLinkButtons() { @@ -116,7 +117,7 @@ final class DataCardTests: XCTestCase { let view = makeBasicCard(buttons: .primaryAndLink(primary: AnyValues.button, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } // MARK: Behaviour @@ -127,7 +128,7 @@ final class DataCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(buttons: .primaryAndLink(primary: AnyValues.button, link: AnyValues.link)) view.primaryButton.isLoading = true - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } // MARK: XIB integration @@ -145,7 +146,7 @@ final class DataCardTests: XCTestCase { view.card.contentConfiguration = configurationWithActions assertSnapshot( - matching: view.asRootOfViewController(), + of: view.asRootOfViewController(), as: .image(on: .iPhoneX) ) } diff --git a/Tests/MisticaTests/UI/DeterminateStepperTests.swift b/Tests/MisticaTests/UI/DeterminateStepperTests.swift index 5fbb59343..f5b495979 100644 --- a/Tests/MisticaTests/UI/DeterminateStepperTests.swift +++ b/Tests/MisticaTests/UI/DeterminateStepperTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class DeterminateStepperTests: XCTestCase { - override class func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -34,7 +35,7 @@ final class DeterminateStepperTests: XCTestCase { let stepper = makeTemplateWithStepperState(currentStep: 0) assertSnapshot( - matching: stepper, + of: stepper, as: .image, named: "assertInitialState" ) @@ -42,7 +43,7 @@ final class DeterminateStepperTests: XCTestCase { stepper.currentStep = 1 assertSnapshot( - matching: stepper, + of: stepper, as: .image, named: "finalState" ) @@ -54,7 +55,7 @@ final class DeterminateStepperTests: XCTestCase { let stepper = makeTemplateWithStepperState(numberOfSteps: 3) assertSnapshot( - matching: stepper, + of: stepper, as: .image, named: "assertInitialState" ) @@ -62,7 +63,7 @@ final class DeterminateStepperTests: XCTestCase { stepper.numberOfSteps = 4 assertSnapshot( - matching: stepper, + of: stepper, as: .image, named: "finalState" ) @@ -76,7 +77,7 @@ final class DeterminateStepperTests: XCTestCase { let view = DeterminateStepperXIBIntegration.viewFromNib() assertSnapshot( - matching: view, + of: view, as: .image ) } @@ -84,9 +85,11 @@ final class DeterminateStepperTests: XCTestCase { // MARK: - Helpers -private func makeTemplateWithStepperState(currentStep: Int = 0, numberOfSteps: Int = 3) -> DeterminateStepperView { - let stepperView = DeterminateStepperView(frame: CGRect(origin: .zero, size: CGSize(width: 600, height: 24))) - stepperView.numberOfSteps = numberOfSteps - stepperView.currentStep = currentStep - return stepperView +private extension DeterminateStepperTests { + func makeTemplateWithStepperState(currentStep: Int = 0, numberOfSteps: Int = 3) -> DeterminateStepperView { + let stepperView = DeterminateStepperView(frame: CGRect(origin: .zero, size: CGSize(width: 600, height: 24))) + stepperView.numberOfSteps = numberOfSteps + stepperView.currentStep = currentStep + return stepperView + } } diff --git a/Tests/MisticaTests/UI/EmptyStatesTests.swift b/Tests/MisticaTests/UI/EmptyStatesTests.swift index e2b7b37ac..aa41f5c93 100644 --- a/Tests/MisticaTests/UI/EmptyStatesTests.swift +++ b/Tests/MisticaTests/UI/EmptyStatesTests.swift @@ -10,11 +10,16 @@ import SnapshotTesting import XCTest +@MainActor final class EmptyStatesTests: XCTestCase { override class func setUp() { super.setUp() + } - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -32,7 +37,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeBasicEmptyState() - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testShowActions() { @@ -45,7 +50,7 @@ final class EmptyStatesTests: XCTestCase { actions: .primary(AnyValues.primary) ) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentAsACard() { @@ -58,7 +63,7 @@ final class EmptyStatesTests: XCTestCase { actions: .primaryAndLink(primary: AnyValues.primary, link: AnyValues.link) ) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutDescription() { @@ -66,7 +71,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(description: nil) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryButtonOnly() { @@ -74,7 +79,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(actions: .primary(AnyValues.primary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryAndLinkButtonsOnly() { @@ -82,7 +87,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(actions: .primaryAndLink(primary: AnyValues.primary, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryAndLinkButtonsOnlyAsACard() { @@ -90,7 +95,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(actions: .primaryAndLink(primary: AnyValues.primary, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testSecondaryAndLinkButtonsOnly() { @@ -98,7 +103,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(actions: .secondaryAndLink(secondary: AnyValues.secondary, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testSecondaryButtonOnly() { @@ -106,7 +111,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(actions: .secondary(AnyValues.secondary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testSecondaryButtonOnlyAsACard() { @@ -114,7 +119,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(type: .card(.icon(AnyValues.iconImage)), actions: .secondary(AnyValues.secondary)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testSecondaryAndLinkButtonsOnlyAsACard() { @@ -122,7 +127,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(type: .card(.icon(AnyValues.iconImage)), actions: .secondaryAndLink(secondary: AnyValues.secondary, link: AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testEmptyButtonOnly() { @@ -130,7 +135,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(actions: .empty) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testEmptyButtonOnlyAsACard() { @@ -138,7 +143,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(type: .card(.icon(AnyValues.iconImage)), actions: .empty) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testLinkButtonOnlyAsACard() { @@ -146,7 +151,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(type: .card(.icon(AnyValues.iconImage)), actions: .link(AnyValues.link)) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } // MARK: Behaviour @@ -157,7 +162,7 @@ final class EmptyStatesTests: XCTestCase { let view = makeEmptyStateWithContentAndButtons(actions: .primaryAndLink(primary: AnyValues.primary, link: AnyValues.link)) view.primaryButton.isLoading = true - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } // MARK: XIB integration @@ -176,7 +181,7 @@ final class EmptyStatesTests: XCTestCase { view.emptyState.contentConfiguration = configurationWithActions assertSnapshot( - matching: view.asRootOfViewController(), + of: view.asRootOfViewController(), as: .image(on: .iPhoneX) ) } diff --git a/Tests/MisticaTests/UI/FeedbackTests.swift b/Tests/MisticaTests/UI/FeedbackTests.swift index c237ad2da..c29ab7716 100644 --- a/Tests/MisticaTests/UI/FeedbackTests.swift +++ b/Tests/MisticaTests/UI/FeedbackTests.swift @@ -10,6 +10,7 @@ import Mistica import SnapshotTesting import XCTest +@MainActor final class FeedbackTests: XCTestCase { private enum Constants { static let singleLineTitle = "Title" @@ -23,11 +24,10 @@ final class FeedbackTests: XCTestCase { static let retryLoadingTitle = "Loading Title" } - override class func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: Simple views diff --git a/Tests/MisticaTests/UI/FilterTests.swift b/Tests/MisticaTests/UI/FilterTests.swift index 46e447f5e..963ea21e8 100644 --- a/Tests/MisticaTests/UI/FilterTests.swift +++ b/Tests/MisticaTests/UI/FilterTests.swift @@ -10,12 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class FilterTests: XCTestCase { - override func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testSegmentsInFilter() { diff --git a/Tests/MisticaTests/UI/FormTests.swift b/Tests/MisticaTests/UI/FormTests.swift index 449cc60be..84b586f73 100644 --- a/Tests/MisticaTests/UI/FormTests.swift +++ b/Tests/MisticaTests/UI/FormTests.swift @@ -10,6 +10,7 @@ import SnapshotTesting import XCTest +@MainActor final class FormsTests: XCTestCase { private enum Constants { static let headerTitle = "Header view" @@ -17,10 +18,10 @@ final class FormsTests: XCTestCase { static let footerTitle = "Footer view" } - override func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Simple view @@ -78,7 +79,7 @@ final class FormsTests: XCTestCase { let formTestsViewController = FormTestsViewController(formView: formView) assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -97,7 +98,7 @@ final class FormsTests: XCTestCase { let formTestsViewController = FormTestsViewController(formView: formView) assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "assertInitialState" ) @@ -105,7 +106,7 @@ final class FormsTests: XCTestCase { formView.addInputFields([makeInputFieldWithEmailStyle()]) assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "finalState" ) @@ -126,7 +127,7 @@ final class FormsTests: XCTestCase { let formTestsViewController = FormTestsViewController(formView: formView) assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "assertInitialState" ) @@ -134,7 +135,7 @@ final class FormsTests: XCTestCase { formView.removeInputFields([firstInputField]) assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "finalState" ) @@ -159,7 +160,7 @@ final class FormsTests: XCTestCase { let formTestsViewController = FormTestsViewController(formView: formView) assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "assertInitialState" ) @@ -167,7 +168,7 @@ final class FormsTests: XCTestCase { formView.validate() assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "finalState" ) @@ -191,7 +192,7 @@ final class FormsTests: XCTestCase { let formTestsViewController = FormTestsViewController(formView: formView) assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "assertInitialState" ) @@ -199,7 +200,7 @@ final class FormsTests: XCTestCase { formView.validate() assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "finalState" ) @@ -222,7 +223,7 @@ final class FormsTests: XCTestCase { let formTestsViewController = FormTestsViewController(formView: formView) assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "assertInitialState" ) @@ -230,7 +231,7 @@ final class FormsTests: XCTestCase { formView.validate() assertSnapshot( - matching: formTestsViewController, + of: formTestsViewController, as: .image(on: .iPhoneSe), named: "finalState" ) @@ -256,7 +257,7 @@ final class FormsTests: XCTestCase { formView.button.title = Constants.buttonTitle formView.addFooterView(makeLabel(withText: Constants.footerTitle)) - assertSnapshot(matching: view.asRootOfViewController(), as: .image(on: .iPhoneSe)) + assertSnapshot(of: view.asRootOfViewController(), as: .image(on: .iPhoneSe)) } } diff --git a/Tests/MisticaTests/UI/GradientTests.swift b/Tests/MisticaTests/UI/GradientTests.swift index 1692dfb5b..ef090bd78 100644 --- a/Tests/MisticaTests/UI/GradientTests.swift +++ b/Tests/MisticaTests/UI/GradientTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class GradientTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testGradientInView() { @@ -26,7 +27,7 @@ final class GradientTests: XCTestCase { gradientView.setMisticaColorBackground(misticaColor) assertSnapshot( - matching: gradientView, + of: gradientView, as: .image ) } diff --git a/Tests/MisticaTests/UI/HeaderTests.swift b/Tests/MisticaTests/UI/HeaderTests.swift index e57a63ab7..9a76ff280 100644 --- a/Tests/MisticaTests/UI/HeaderTests.swift +++ b/Tests/MisticaTests/UI/HeaderTests.swift @@ -10,14 +10,18 @@ import SnapshotTesting import XCTest +@MainActor final class HeaderTests: XCTestCase { override func setUp() { super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false MisticaConfig.brandStyle = .movistar } + + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } + } } // MARK: - Minimal header @@ -25,14 +29,14 @@ final class HeaderTests: XCTestCase { extension HeaderTests { func testMinimalPretitleHeader() { assertSnapshot( - matching: makeHeader(pretitle: HeaderText(text: "Only a pretitle")), + of: makeHeader(pretitle: HeaderText(text: "Only a pretitle")), as: .image(on: .iPhoneSe) ) } func testMinimalPretitleHeaderInNavigationBar() { assertSnapshot( - matching: makeHeader( + of: makeHeader( style: .inverse, pretitle: HeaderText(text: "Only a pretitle") ), @@ -42,14 +46,14 @@ extension HeaderTests { func testMinimalTitleHeader() { assertSnapshot( - matching: makeHeader(title: HeaderText(text: "Only a title")), + of: makeHeader(title: HeaderText(text: "Only a title")), as: .image(on: .iPhoneSe) ) } func testMinimalTitleHeaderInNavigationBar() { assertSnapshot( - matching: makeHeader( + of: makeHeader( style: .inverse, title: HeaderText(text: "Only a title") ), @@ -59,14 +63,14 @@ extension HeaderTests { func testMinimalDescriptionHeader() { assertSnapshot( - matching: makeHeader(descriptionValue: HeaderText(text: "Only a description")), + of: makeHeader(descriptionValue: HeaderText(text: "Only a description")), as: .image(on: .iPhoneSe) ) } func testMinimalDescriptionHeaderInNavigationBar() { assertSnapshot( - matching: makeHeader( + of: makeHeader( style: .inverse, descriptionValue: HeaderText(text: "Only a description") ), @@ -78,7 +82,7 @@ extension HeaderTests { extension HeaderTests { func testFullHeader() { assertSnapshot( - matching: makeHeader( + of: makeHeader( pretitle: HeaderText(text: "Pretitle"), title: HeaderText(text: "Title"), descriptionValue: HeaderText(text: "Description") @@ -89,7 +93,7 @@ extension HeaderTests { func testFullHeaderWithLongTexts() { assertSnapshot( - matching: makeHeader( + of: makeHeader( pretitle: HeaderText(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit"), title: HeaderText(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit"), descriptionValue: HeaderText(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit") @@ -100,7 +104,7 @@ extension HeaderTests { func testFullHeaderWithLongTextsAndLineLimitToTwo() { assertSnapshot( - matching: makeHeader( + of: makeHeader( pretitle: HeaderText(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit", lineLimit: 2), title: HeaderText(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit", lineLimit: 2), descriptionValue: HeaderText(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit", lineLimit: 2) @@ -111,7 +115,7 @@ extension HeaderTests { func testFullHeaderWithAlternateColorsInverse() { assertSnapshot( - matching: makeHeader( + of: makeHeader( style: .inverse, pretitle: HeaderText(text: "Pretitle"), title: HeaderText(text: "Title"), diff --git a/Tests/MisticaTests/UI/HighlightedCardTests.swift b/Tests/MisticaTests/UI/HighlightedCardTests.swift index 4aa3cb4dc..d079ca6dd 100644 --- a/Tests/MisticaTests/UI/HighlightedCardTests.swift +++ b/Tests/MisticaTests/UI/HighlightedCardTests.swift @@ -11,11 +11,12 @@ import SnapshotTesting import XCTest +@MainActor final class HighlightedCardTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -83,7 +84,7 @@ final class HighlightedCardTests: XCTestCase { ) assertSnapshot( - matching: card, + of: card, as: .image ) } @@ -101,7 +102,7 @@ final class HighlightedCardTests: XCTestCase { card.rightImageStyle = .fill assertSnapshot( - matching: card, + of: card, as: .image ) } @@ -119,7 +120,7 @@ final class HighlightedCardTests: XCTestCase { card.rightImageStyle = .fit assertSnapshot( - matching: card, + of: card, as: .image ) } @@ -136,7 +137,7 @@ final class HighlightedCardTests: XCTestCase { card.backgroundImage = UIImage.circle(diameter: 30, color: .yellow) assertSnapshot( - matching: card, + of: card, as: .image ) } @@ -153,7 +154,7 @@ final class HighlightedCardTests: XCTestCase { card.showActionButton = false assertSnapshot( - matching: card, + of: card, as: .image ) } @@ -170,7 +171,7 @@ final class HighlightedCardTests: XCTestCase { card.showCloseButton = true assertSnapshot( - matching: card, + of: card, as: .image ) } @@ -188,7 +189,7 @@ final class HighlightedCardTests: XCTestCase { card.showCloseButton = true assertSnapshot( - matching: card, + of: card, as: .image ) } @@ -206,7 +207,7 @@ final class HighlightedCardTests: XCTestCase { card.showCloseButton = true assertSnapshot( - matching: card, + of: card, as: .image ) } @@ -222,7 +223,7 @@ final class HighlightedCardTests: XCTestCase { view.card.actionButtonTitle = "Empezar pruebas" assertSnapshot( - matching: view.asRootOfViewController(), + of: view.asRootOfViewController(), as: .image(on: .iPhoneSe) ) } @@ -230,30 +231,32 @@ final class HighlightedCardTests: XCTestCase { // MARK: - Helpers -private func makeCard( - style: HighlightedCardStyle = .normal, - title: String = "Resolver problema técnico", - subtitle: String = "Usa nuestra herramienta para resolver tus problemas técnicos", - rightImage: UIImage? = nil, - actionButtonTitle: String? = "Empezar pruebas", - actionButtonStyle: HighlightedCard.ButtonStyle = .primary -) -> HighlightedCard { - let view = HighlightedCard( - title: title, - subtitle: subtitle, - rightImage: rightImage, - actionButtonStyle: actionButtonStyle - ) - - view.style = style - view.actionButtonTitle = actionButtonTitle - - let cardSize = view.systemLayoutSizeFitting( - CGSize(width: 300, height: 0), - withHorizontalFittingPriority: .required, - verticalFittingPriority: .defaultLow - ) - view.frame = CGRect(x: 0, y: 0, width: cardSize.width, height: cardSize.height) - - return view +private extension HighlightedCardTests { + func makeCard( + style: HighlightedCardStyle = .normal, + title: String = "Resolver problema técnico", + subtitle: String = "Usa nuestra herramienta para resolver tus problemas técnicos", + rightImage: UIImage? = nil, + actionButtonTitle: String? = "Empezar pruebas", + actionButtonStyle: HighlightedCard.ButtonStyle = .primary + ) -> HighlightedCard { + let view = HighlightedCard( + title: title, + subtitle: subtitle, + rightImage: rightImage, + actionButtonStyle: actionButtonStyle + ) + + view.style = style + view.actionButtonTitle = actionButtonTitle + + let cardSize = view.systemLayoutSizeFitting( + CGSize(width: 300, height: 0), + withHorizontalFittingPriority: .required, + verticalFittingPriority: .defaultLow + ) + view.frame = CGRect(x: 0, y: 0, width: cardSize.width, height: cardSize.height) + + return view + } } diff --git a/Tests/MisticaTests/UI/IndeterminateStepperTests.swift b/Tests/MisticaTests/UI/IndeterminateStepperTests.swift index 3536571b0..e432caa0b 100644 --- a/Tests/MisticaTests/UI/IndeterminateStepperTests.swift +++ b/Tests/MisticaTests/UI/IndeterminateStepperTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class IndeterminateStepperTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -34,7 +35,7 @@ final class IndeterminateStepperTests: XCTestCase { let stepper = makeTemplateWithStepperState(value: 0) assertSnapshot( - matching: stepper, + of: stepper, as: .image, named: "assertInitialState" ) @@ -42,7 +43,7 @@ final class IndeterminateStepperTests: XCTestCase { stepper.value = 50 assertSnapshot( - matching: stepper, + of: stepper, as: .image, named: "finalState" ) @@ -56,7 +57,7 @@ final class IndeterminateStepperTests: XCTestCase { let view = IndeterminateStepperXIBIntegration.viewFromNib() assertSnapshot( - matching: view, + of: view, as: .image ) } @@ -64,8 +65,10 @@ final class IndeterminateStepperTests: XCTestCase { // MARK: - Helpers -private func makeTemplateWithStepperState(value: Int = 0) -> IndeterminateStepperView { - let stepperView = IndeterminateStepperView(frame: CGRect(origin: .zero, size: CGSize(width: 600, height: 24))) - stepperView.value = value - return stepperView +private extension IndeterminateStepperTests { + func makeTemplateWithStepperState(value: Int = 0) -> IndeterminateStepperView { + let stepperView = IndeterminateStepperView(frame: CGRect(origin: .zero, size: CGSize(width: 600, height: 24))) + stepperView.value = value + return stepperView + } } diff --git a/Tests/MisticaTests/UI/InputFieldTests.swift b/Tests/MisticaTests/UI/InputFieldTests.swift index 806f1fff0..110d3a9c2 100644 --- a/Tests/MisticaTests/UI/InputFieldTests.swift +++ b/Tests/MisticaTests/UI/InputFieldTests.swift @@ -10,16 +10,16 @@ import Mistica import SnapshotTesting import XCTest +@MainActor final class InputFieldTests: XCTestCase { private enum Constants { static let defaultTextValue = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." } - override func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles diff --git a/Tests/MisticaTests/UI/ListsTests.swift b/Tests/MisticaTests/UI/ListsTests.swift index bcda0843f..3326d54ed 100644 --- a/Tests/MisticaTests/UI/ListsTests.swift +++ b/Tests/MisticaTests/UI/ListsTests.swift @@ -10,14 +10,20 @@ import SnapshotTesting import XCTest +@MainActor final class ListsTests: XCTestCase { override func setUp() { super.setUp() - isRecording = false MisticaConfig.brandStyle = .movistar } + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } + } + // MARK: - Layout // MARK: - Default config @@ -26,7 +32,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController(assetType: .custom(.image(AnyValues.image))) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -35,7 +41,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController(assetType: .custom(.image(AnyValues.image), size: CGSize(width: 100, height: 40))) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -44,7 +50,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController(assetType: .smallIcon(AnyValues.image)) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -53,7 +59,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController(assetType: .largeIcon(AnyValues.image, backgroundColor: .blue)) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -65,7 +71,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -76,7 +82,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController(title: AnyValues.title) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -88,7 +94,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -100,7 +106,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -112,7 +118,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -124,7 +130,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -137,7 +143,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -151,7 +157,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -164,7 +170,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -177,7 +183,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -190,7 +196,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -203,7 +209,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -217,7 +223,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -231,7 +237,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -244,7 +250,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -257,7 +263,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -270,7 +276,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -283,7 +289,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -297,7 +303,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -312,7 +318,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -326,7 +332,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -340,7 +346,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -354,7 +360,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -368,7 +374,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -383,7 +389,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -397,7 +403,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -411,7 +417,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -426,7 +432,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -441,7 +447,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -457,7 +463,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -473,7 +479,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -487,7 +493,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -501,7 +507,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -515,7 +521,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -529,7 +535,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -543,7 +549,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -557,7 +563,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -571,7 +577,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -585,7 +591,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -600,7 +606,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -615,7 +621,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -631,7 +637,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -648,7 +654,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -662,7 +668,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -677,7 +683,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -693,7 +699,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -711,7 +717,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -725,7 +731,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -740,7 +746,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -756,7 +762,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -774,7 +780,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -796,7 +802,7 @@ final class ListsTests: XCTestCase { ) assertSnapshot( - matching: listTestsViewController, + of: listTestsViewController, as: .image(on: .iPhoneSe) ) } @@ -838,7 +844,7 @@ extension ListsTests { Detail text line 1 Detail text line 2 """ - static var image = UIImage(color: .green) + static let image = UIImage(color: .green) } private func makeListTestsViewController( diff --git a/Tests/MisticaTests/UI/LoadErrorViewControllerTests.swift b/Tests/MisticaTests/UI/LoadErrorViewControllerTests.swift index 12838e506..9e130487c 100644 --- a/Tests/MisticaTests/UI/LoadErrorViewControllerTests.swift +++ b/Tests/MisticaTests/UI/LoadErrorViewControllerTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class LoadErrorViewControllerTests: XCTestCase { - override func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } } @@ -28,21 +29,21 @@ extension LoadErrorViewControllerTests { func testNoTitle() { assertSnapshot( - matching: makeLoadErrorViewController(title: nil), + of: makeLoadErrorViewController(title: nil), as: .image ) } func testNoAction() { assertSnapshot( - matching: makeLoadErrorViewController(showActionButton: false), + of: makeLoadErrorViewController(showActionButton: false), as: .image ) } func testEmptyDescriptionAction() { assertSnapshot( - matching: makeLoadErrorViewController(descriptionText: ""), + of: makeLoadErrorViewController(descriptionText: ""), as: .image ) } diff --git a/Tests/MisticaTests/UI/MediaCardTests.swift b/Tests/MisticaTests/UI/MediaCardTests.swift index 32a7f9d18..3c5343538 100644 --- a/Tests/MisticaTests/UI/MediaCardTests.swift +++ b/Tests/MisticaTests/UI/MediaCardTests.swift @@ -10,11 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class MediaCardTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -32,7 +33,7 @@ final class MediaCardTests: XCTestCase { let view = makeBasicCard() - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContent() { @@ -40,7 +41,7 @@ final class MediaCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons() - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutFragment() { @@ -48,7 +49,7 @@ final class MediaCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(hasFragment: false) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutHeadline() { @@ -56,7 +57,7 @@ final class MediaCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(headline: nil) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutTitle() { @@ -64,7 +65,7 @@ final class MediaCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(title: nil) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testFullContentWithoutPretitle() { @@ -72,7 +73,7 @@ final class MediaCardTests: XCTestCase { let view = makeCardWithFullContentAndButtons(pretitle: nil) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testTextsWithMultiLine() { @@ -84,7 +85,7 @@ final class MediaCardTests: XCTestCase { descriptionTitle: "Mauris vel nisi efficitur, fringilla urna at, gravida nunc. Sed eu dui sit amet est fringilla eleifend. Ut aliquam, tortor ac varius sodales" ) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryButtonsOnly() { @@ -92,7 +93,7 @@ final class MediaCardTests: XCTestCase { let view = makeBasicCard() - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } func testPrimaryAndLinkButtons() { @@ -102,7 +103,7 @@ final class MediaCardTests: XCTestCase { primaryButton: AnyValues.button, linkButton: AnyValues.link ) - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } // MARK: Behaviour @@ -115,7 +116,7 @@ final class MediaCardTests: XCTestCase { ) view.primaryButton.isLoading = true - assertSnapshot(matching: view, as: .image) + assertSnapshot(of: view, as: .image) } // MARK: XIB integration @@ -137,7 +138,7 @@ final class MediaCardTests: XCTestCase { view.card.contentConfiguration = configurationWithActions assertSnapshot( - matching: view.asRootOfViewController(), + of: view.asRootOfViewController(), as: .image(on: .iPhoneX) ) } @@ -149,6 +150,7 @@ extension MediaCardTests { enum AnyValues { static let button = CardButton(title: "Button", loadingTitle: "Loading", tapHandler: nil) static let link = CardLinkButton(title: "Button Link", tapHandler: nil) + @MainActor static var richMedia: UIImageView { let image = UIImageView(image: UIImage(color: .green)) diff --git a/Tests/MisticaTests/UI/PopoverViewTests.swift b/Tests/MisticaTests/UI/PopoverViewTests.swift index ce8fb0fec..7aeadb1b8 100644 --- a/Tests/MisticaTests/UI/PopoverViewTests.swift +++ b/Tests/MisticaTests/UI/PopoverViewTests.swift @@ -10,11 +10,12 @@ import Mistica import SnapshotTesting import XCTest +@MainActor final class PopoverViewTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Styles @@ -32,14 +33,14 @@ final class PopoverViewTests: XCTestCase { let downPopover = makePopover(tipDirection: .down) assertSnapshot( - matching: downPopover, + of: downPopover, as: .image(size: .init(width: 200, height: 100)) ) let upPopover = makePopover(tipDirection: .up) assertSnapshot( - matching: upPopover, + of: upPopover, as: .image(size: .init(width: 200, height: 100)) ) } @@ -50,7 +51,7 @@ final class PopoverViewTests: XCTestCase { let popover = makePopover(tipDirection: .down, title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") assertSnapshot( - matching: popover, + of: popover, as: .image(size: .init(width: 400, height: 150)) ) } @@ -59,7 +60,7 @@ final class PopoverViewTests: XCTestCase { let popover = makePopover(tipDirection: .down, title: nil) assertSnapshot( - matching: popover, + of: popover, as: .image(size: .init(width: 200, height: 100)) ) } @@ -70,7 +71,7 @@ final class PopoverViewTests: XCTestCase { let popover = makePopover(tipDirection: .down, subtitle: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") assertSnapshot( - matching: popover, + of: popover, as: .image(size: .init(width: 400, height: 150)) ) } @@ -79,7 +80,7 @@ final class PopoverViewTests: XCTestCase { let popover = makePopover(tipDirection: .down, subtitle: nil) assertSnapshot( - matching: popover, + of: popover, as: .image(size: .init(width: 500, height: 100)) ) } @@ -91,7 +92,7 @@ final class PopoverViewTests: XCTestCase { let popover = makePopover(tipDirection: .down, title: largeText, subtitle: largeText) assertSnapshot( - matching: popover, + of: popover, as: .image(size: .init(width: 400, height: 200)) ) } @@ -102,7 +103,7 @@ final class PopoverViewTests: XCTestCase { let popover = makePopover(tipDirection: .down, image: nil) assertSnapshot( - matching: popover, + of: popover, as: .image(size: .init(width: 200, height: 100)) ) } @@ -113,7 +114,7 @@ final class PopoverViewTests: XCTestCase { let popover = makePopover(tipDirection: .down, canClose: false) assertSnapshot( - matching: popover, + of: popover, as: .image(size: .init(width: 200, height: 100)) ) } diff --git a/Tests/MisticaTests/UI/RadioButtonTests.swift b/Tests/MisticaTests/UI/RadioButtonTests.swift index f68cfe735..0104f9c9a 100644 --- a/Tests/MisticaTests/UI/RadioButtonTests.swift +++ b/Tests/MisticaTests/UI/RadioButtonTests.swift @@ -10,13 +10,14 @@ import Mistica import SnapshotTesting import XCTest +@MainActor final class RadioButtonTests: XCTestCase { private let buttonSize = CGSize(width: 30.0, height: 30.0) - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testRadioButtonEnabled() { diff --git a/Tests/MisticaTests/UI/SheetTests.swift b/Tests/MisticaTests/UI/SheetTests.swift index 035342b7b..715dab5d7 100644 --- a/Tests/MisticaTests/UI/SheetTests.swift +++ b/Tests/MisticaTests/UI/SheetTests.swift @@ -10,12 +10,12 @@ import SnapshotTesting import XCTest +@MainActor final class SheetTests: XCTestCase { - override func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: Common behaviour @@ -216,7 +216,7 @@ final class SheetTests: XCTestCase { // MARK: Actions func testSingleAction() { - assertSnapshot(matching: sheetView( + assertSnapshot(of: sheetView( title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", subtitle: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", description: "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", @@ -225,7 +225,7 @@ final class SheetTests: XCTestCase { } func testActions() { - assertSnapshot(matching: sheetView( + assertSnapshot(of: sheetView( title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", subtitle: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", description: "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", diff --git a/Tests/MisticaTests/UI/SkeletonTests.swift b/Tests/MisticaTests/UI/SkeletonTests.swift index e6298aead..b19167db9 100644 --- a/Tests/MisticaTests/UI/SkeletonTests.swift +++ b/Tests/MisticaTests/UI/SkeletonTests.swift @@ -10,51 +10,52 @@ import SnapshotTesting import XCTest +@MainActor final class SkeletonTests: XCTestCase { - override class func setUp() { - super.setUp() - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testLineSkeleton() { assertSnapshot( - matching: makeSkeletonTemplate(type: .line(width: 300)), + of: makeSkeletonTemplate(type: .line(width: 300)), as: .image ) } func testTextSkeleton() { assertSnapshot( - matching: makeSkeletonTemplate(type: .text()), + of: makeSkeletonTemplate(type: .text()), as: .image ) } func testTextSkeletonWithCustomLines() { assertSnapshot( - matching: makeSkeletonTemplate(type: .text(numberOfLines: 5)), + of: makeSkeletonTemplate(type: .text(numberOfLines: 5)), as: .image ) } func testCircleSkeleton() { assertSnapshot( - matching: makeSkeletonTemplate(type: .circle(size: 40), containerWidth: 40), + of: makeSkeletonTemplate(type: .circle(size: 40), containerWidth: 40), as: .image ) } func testRowSkeleton() { assertSnapshot( - matching: makeSkeletonTemplate(type: .row), + of: makeSkeletonTemplate(type: .row), as: .image ) } func testRectangleSkeleton() { assertSnapshot( - matching: makeSkeletonTemplate(type: .rectangle(size: CGSize(width: 360, height: 180), isRounded: true), containerWidth: 360), + of: makeSkeletonTemplate(type: .rectangle(size: CGSize(width: 360, height: 180), isRounded: true), containerWidth: 360), as: .image ) } diff --git a/Tests/MisticaTests/UI/TabsTests.swift b/Tests/MisticaTests/UI/TabsTests.swift index e8a482bd9..2c29bebf9 100644 --- a/Tests/MisticaTests/UI/TabsTests.swift +++ b/Tests/MisticaTests/UI/TabsTests.swift @@ -10,6 +10,7 @@ import SnapshotTesting import XCTest +@MainActor final class TabsTests: XCTestCase { enum Constants { static let defaultWidth: CGFloat = 390 @@ -57,11 +58,10 @@ final class TabsTests: XCTestCase { ] } - override class func setUp() { - super.setUp() - UIView.setAnimationsEnabled(false) - - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } // MARK: - Mobile Width Styles @@ -77,7 +77,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.threeItem) ), as: .image(on: .iPhoneSe) @@ -88,7 +88,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.threeItemWithoutIcon) ), as: .image(on: .iPhoneSe) @@ -99,7 +99,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.threeItemWithLongText) ), as: .image(on: .iPhoneSe) @@ -110,7 +110,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.threeItemWithLongTextAndNoIcon) ), as: .image(on: .iPhoneSe) @@ -121,7 +121,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.eightItem) ), as: .image(on: .iPhoneSe) @@ -134,7 +134,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.threeItem) ), as: .image(on: .iPadMini) @@ -145,7 +145,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.twoItem) ), as: .image(on: .iPadMini) @@ -156,7 +156,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.threeItemWithLongText) ), as: .image(on: .iPadMini) @@ -167,7 +167,7 @@ final class TabsTests: XCTestCase { MisticaConfig.brandStyle = .movistar assertSnapshot( - matching: TabsTestsViewController( + of: TabsTestsViewController( tabsView: makeTemplateTabsView(tabItems: Constants.eightItem) ), as: .image(on: .iPadMini) @@ -183,7 +183,7 @@ final class TabsTests: XCTestCase { view.tabs.reload(with: Constants.twoItem) assertSnapshot( - matching: view.asRootOfViewController(), + of: view.asRootOfViewController(), as: .image(on: .iPhoneSe) ) } diff --git a/Tests/MisticaTests/UI/TagTests.swift b/Tests/MisticaTests/UI/TagTests.swift index 781efe47c..475d2309b 100644 --- a/Tests/MisticaTests/UI/TagTests.swift +++ b/Tests/MisticaTests/UI/TagTests.swift @@ -10,11 +10,19 @@ import SnapshotTesting import XCTest +@MainActor final class TagTests: XCTestCase { override func setUp() { super.setUp() - UIView.setAnimationsEnabled(false) - isRecording = false + Task { @MainActor in + UIView.setAnimationsEnabled(false) + } + } + + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testPromoTagView() { @@ -73,7 +81,7 @@ final class TagTests: XCTestCase { view.tagView.text = "Xib integration" assertSnapshot( - matching: view, + of: view, as: .image ) } diff --git a/Tests/MisticaTests/UI/TitleHeaderFooterViewTests.swift b/Tests/MisticaTests/UI/TitleHeaderFooterViewTests.swift index a22b50ff7..e54db9714 100644 --- a/Tests/MisticaTests/UI/TitleHeaderFooterViewTests.swift +++ b/Tests/MisticaTests/UI/TitleHeaderFooterViewTests.swift @@ -10,12 +10,19 @@ import SnapshotTesting import XCTest +@MainActor final class TitleHeaderFooterViewTests: XCTestCase { override func setUp() { super.setUp() - UIView.setAnimationsEnabled(false) + Task { @MainActor in + UIView.setAnimationsEnabled(false) + } + } - isRecording = false + override func invokeTest() { + withSnapshotTesting(record: .never) { + super.invokeTest() + } } func testTitle1() { diff --git a/Tests/MisticaTests/Utils/TestHelpers.swift b/Tests/MisticaTests/Utils/TestHelpers.swift index c3c2dc0bd..93189e9e6 100644 --- a/Tests/MisticaTests/Utils/TestHelpers.swift +++ b/Tests/MisticaTests/Utils/TestHelpers.swift @@ -22,18 +22,25 @@ extension UIView { // MARK: - Helpers +@MainActor func assertSnapshotForAllBrandsAndStyles( as snapshotting: Snapshotting, - file: StaticString = #file, + file: StaticString = #filePath, testName: String = #function, line: UInt = #line, - viewBuilder: @autoclosure () -> View + viewBuilder: @autoclosure () -> View, + animationsEnabled: Bool = false ) { + UIView.setAnimationsEnabled(animationsEnabled) + for brand in BrandStyle.allCases { MisticaConfig.brandStyle = brand + var lightView = viewBuilder() + lightView.overrideUserInterfaceStyle = .light + assertSnapshot( - matching: viewBuilder(), + of: lightView, as: snapshotting, named: "with-\(brand)-style", file: file, @@ -45,7 +52,7 @@ func assertSnapshotForAllBrandsAndStyles( darkView.overrideUserInterfaceStyle = .dark assertSnapshot( - matching: darkView, + of: darkView, as: snapshotting, named: "with-\(brand)-dark-style", file: file, @@ -55,11 +62,12 @@ func assertSnapshotForAllBrandsAndStyles( } } +@MainActor func assertSnapshot( for brands: [BrandStyle] = BrandStyle.allCases, and styles: [UIUserInterfaceStyle] = [.light, .dark], as snapshotting: Snapshotting, - file: StaticString = #file, + file: StaticString = #filePath, testName: String = #function, line: UInt = #line, viewBuilder: @autoclosure () -> View @@ -71,7 +79,7 @@ func assertSnapshot( var view = viewBuilder() view.overrideUserInterfaceStyle = style assertSnapshot( - matching: view, + of: view, as: snapshotting, named: "with-\(brand)-\(style.testSuffix)-style", file: file, @@ -82,6 +90,7 @@ func assertSnapshot( } } +@MainActor protocol UserInterfaceStyling { var overrideUserInterfaceStyle: UIUserInterfaceStyle { get set } }