Skip to content

Commit

Permalink
Add basic support for SwiftUI views
Browse files Browse the repository at this point in the history
  • Loading branch information
aleh committed Apr 10, 2024
1 parent 0cfb521 commit 491ffa8
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 19 deletions.
7 changes: 4 additions & 3 deletions MMMTestCase.podspec
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
#
# MMMTestCase. Part of MMMTemple.
# Copyright (C) 2015-2022 MediaMonks. All rights reserved.
# Copyright (C) 2015-2024 MediaMonks. All rights reserved.
#

Pod::Spec.new do |s|

s.name = "MMMTestCase"
s.version = "1.9.0"
s.version = "1.10.0"
s.summary = "Our helpers for FBTestCase and XCTestCase"
s.description = s.summary
s.homepage = "https://github.com/mediamonks/#{s.name}"
s.license = "MIT"
s.authors = "MediaMonks"
s.source = { :git => "https://github.com/mediamonks/#{s.name}.git", :tag => s.version.to_s }

s.platform = :ios, '11.0'
s.platform = :ios, '15.0'
s.swift_versions = '5.0'

s.framework = 'XCTest'
s.dependency 'FBSnapshotTestCase/Core'
Expand Down
69 changes: 68 additions & 1 deletion Sources/MMMTestCase/MMMTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import Foundation
import MMMCommonCore
import SwiftUI
import UIKit

#if SWIFT_PACKAGE
Expand Down Expand Up @@ -45,7 +46,7 @@ public enum MMMTestCaseSize {
return NSValue(cgSize: CGSize(width: width, height: height))
}
}
};
}

extension MMMTestCase {

Expand Down Expand Up @@ -86,7 +87,73 @@ extension MMMTestCase {
identifier: identifier,
backgroundColor: backgroundColor
)
}

private class WrapperController: UIViewController {

private let viewController: UIViewController

public init(_ viewController: UIViewController) {
self.viewController = viewController
super.init(nibName: nil, bundle: nil)
addChild(viewController)
viewController.didMove(toParent: self)
}

public required init?(coder: NSCoder) { fatalError() }

public var fitSize: CGSize = .init(width: 320, height: 480)

public private(set) lazy var container = MMMTestCaseContainer()

override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(container)
}

override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
container.setChildView(viewController.view, size: fitSize)
let bounds = view.bounds.inset(by: view.safeAreaInsets)
container.frame = .init(origin: bounds.origin, size: container.sizeThatFits(.zero))
}
}

private func sizeForFit(_ fit: MMMTestCaseSize) -> CGSize {
switch fit {
case .natural: return self.fitSize(forPresetFit: .natural)
case .screenWidth: return self.fitSize(forPresetFit: .screenWidth)
case .screenWidthTableHeight: return self.fitSize(forPresetFit: .screenWidthTableHeight)
case let .size(width, height): return .init(width: width, height: height)
}
}

public func verify<T: SwiftUI.View>(view: T, fit: MMMTestCaseSize = .screenWidthTableHeight, identifier: String = "", backgroundColor: UIColor? = nil) {

let window = UIWindow()
let viewController = UIHostingController(rootView: view)
let wrapper = WrapperController(viewController)

window.rootViewController = wrapper
window.windowLevel = .normal - 1 // It could be fun to watch snapshots, but let's keep older behavior.
window.isHidden = false

let fitSize = sizeForFit(fit)
wrapper.fitSize = viewController.sizeThatFits(in: fitSize)
window.setNeedsLayout()
// We need the layout to happen naturally now.
pumpRunLoopABit()

self.verifyView(
wrapper.container,
identifier: [
identifier,
fitSize.width > 0 ? String(format: "w%.f", fitSize.width) : nil,
fitSize.height > 0 ? String(format: "h%.f", fitSize.height) : nil
].compactMap { $0 }.joined(separator: "_"),
suffixes: self.referenceFolderSuffixes(),
tolerance: 0.05
)
}

/// Helps generating parameter dictionaries suitable for `varyParameters` from enums supporting `CaseIterable`.
Expand Down
18 changes: 18 additions & 0 deletions Sources/MMMTestCaseObjC/MMMTestCase.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ typedef void (^RandomOrderBlock)();
*/
- (void)flushMainQueue;

/// Pumps all events ready right now unless there are too many that we don't have time left.
- (void)pumpRunLoopABit;

@end

/**
Expand Down Expand Up @@ -180,4 +183,19 @@ NS_SWIFT_NAME(MMMTestCase.TableViewCellWrapper)

@end

/**
*/
@interface MMMTestCaseContainer : UIView

@property (nonatomic, readwrite) CGRect testViewAlignmentRect;

- (id)init NS_DESIGNATED_INITIALIZER;

- (id)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
- (id)initWithFrame:(CGRect)frame NS_UNAVAILABLE;

- (void)setChildView:(UIView *)childView size:(CGSize)size;

@end

NS_ASSUME_NONNULL_END
17 changes: 2 additions & 15 deletions Sources/MMMTestCaseObjC/MMMTestCase.m
Original file line number Diff line number Diff line change
Expand Up @@ -118,19 +118,6 @@ - (NSString *)identifierForValueWithIndex:(NSInteger)index {

@end

/**
*/
@interface MMMTestCaseContainer : UIView

@property (nonatomic, readwrite) CGRect testViewAlignmentRect;

- (id)init NS_DESIGNATED_INITIALIZER;

- (id)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
- (id)initWithFrame:(CGRect)frame NS_UNAVAILABLE;

@end

@implementation MMMTestCaseContainer {
UIView *_autoLayoutContainer;
UIView *_childView;
Expand Down Expand Up @@ -266,7 +253,7 @@ - (void)drawRect:(CGRect)rect {
CGFloat halfLineWidth = .5;

//
// Safety area backtround.
// Safety area background.
//
[[self safetyAreaColor] setFill];
CGContextFillRect(c, b);
Expand All @@ -278,7 +265,7 @@ - (void)drawRect:(CGRect)rect {
CGContextFillRect(c, [self safetyBounds]);

//
// Guidlines corresponding to the alignment rectangle.
// Guidelines corresponding to the alignment rectangle.
//
{
CGRect r = [_childView alignmentRectForFrame:[self safetyBounds]];
Expand Down

0 comments on commit 491ffa8

Please sign in to comment.