diff --git a/Sources/SnapshotTesting/Snapshotting/SwiftUIView.swift b/Sources/SnapshotTesting/Snapshotting/SwiftUIView.swift index bfc070cb6..2d355e2d3 100644 --- a/Sources/SnapshotTesting/Snapshotting/SwiftUIView.swift +++ b/Sources/SnapshotTesting/Snapshotting/SwiftUIView.swift @@ -90,4 +90,53 @@ } } #endif + + #if os(macOS) + @available(macOS 10.15, *) + extension Snapshotting where Value: SwiftUI.View, Format == NSImage { + + /// A snapshot strategy for comparing SwiftUI Views based on pixel equality. + public static var image: Snapshotting { + .image() + } + + /// A snapshot strategy for comparing SwiftUI Views based on pixel equality. + /// + /// - Parameters: + /// - precision: The percentage of pixels that must match. + /// - perceptualPrecision: The percentage a pixel must match the source pixel to be considered a match. + /// 98-99% mimics [the precision](http://zschuessler.github.io/DeltaE/learn/#toc-defining-delta-e) of the human eye. + /// - layout: A view layout override. + public static func image( + precision: Float = 0.99, + perceptualPrecision: Float = 0.99, + layout: SwiftUISnapshotLayout = .sizeThatFits + ) -> Snapshotting { + SimplySnapshotting + .image(precision: precision, perceptualPrecision: perceptualPrecision) + .asyncPullback { view in + let controller = NSHostingController(rootView: view) + let initialFrame = controller.view.frame + + let size = switch layout { + case let .fixed(width, height): + CGSize(width: width, height: height) + case .sizeThatFits: + controller.sizeThatFits(in: .zero) + } + + let view = controller.view + view.frame.size = size + + return Async { callback in + addImagesForRenderedViews(view).sequence().run { views in + callback(view.convertToImage()) + views.forEach { $0.removeFromSuperview() } + view.frame = initialFrame + } + } + } + } + } + #endif #endif diff --git a/Tests/SnapshotTestingTests/SnapshotTestingTests.swift b/Tests/SnapshotTestingTests/SnapshotTestingTests.swift index caa62bdc0..31c87e34f 100644 --- a/Tests/SnapshotTestingTests/SnapshotTestingTests.swift +++ b/Tests/SnapshotTestingTests/SnapshotTestingTests.swift @@ -1141,6 +1141,26 @@ final class SnapshotTestingTests: XCTestCase { } #endif + #if os(macOS) + @available(macOS 10.15, *) + func testSwiftUIView() { + struct SwiftUIView: View { + var body: some View { + ZStack { + Color.green + Color.yellow.padding() + Color.red.padding().padding() + } + } + } + + let view = SwiftUIView() + + assertSnapshot(of: view, as: .image(layout: .fixed(width: 100, height: 100)), named: "fixed") + assertSnapshot(of: view, as: .image(layout: .sizeThatFits), named: "size-that-fits") + } + #endif + #if os(iOS) @available(iOS 13.0, *) func testSwiftUIView_iOS() { diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView.fixed.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView.fixed.png new file mode 100644 index 000000000..7c94cb363 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView.fixed.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView.size-that-fits.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView.size-that-fits.png new file mode 100644 index 000000000..9d5eaa795 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView.size-that-fits.png differ