From d99c3fc16efda95b4de1895a20e42072f9022173 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 8 Aug 2024 06:14:09 +0200 Subject: [PATCH 1/3] fix missing inline snapshot testing --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 3ea312104..0e956ef23 100644 --- a/Package.swift +++ b/Package.swift @@ -45,7 +45,7 @@ let package = Package( .testTarget( name: "SnapshotTestingTests", dependencies: [ - "SnapshotTesting" + "SnapshotTesting", "InlineSnapshotTesting" ], exclude: [ "__Fixtures__", From 55efd0ea30a4e07ec812dc53695acb8f4ed8f3b7 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 8 Aug 2024 06:22:21 +0200 Subject: [PATCH 2/3] remove the need to import inline in not inline snapshot target --- Package.swift | 2 +- Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 0e956ef23..3ea312104 100644 --- a/Package.swift +++ b/Package.swift @@ -45,7 +45,7 @@ let package = Package( .testTarget( name: "SnapshotTestingTests", dependencies: [ - "SnapshotTesting", "InlineSnapshotTesting" + "SnapshotTesting" ], exclude: [ "__Fixtures__", diff --git a/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift b/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift index 12528b843..385b0f2b8 100644 --- a/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift +++ b/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift @@ -1,7 +1,6 @@ #if canImport(Testing) import Testing import Foundation - import InlineSnapshotTesting @_spi(Experimental) import SnapshotTesting @Suite( From 4ce398b41c70bcae8bfcdb77cccb56f2a06b4362 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 8 Aug 2024 07:39:41 +0200 Subject: [PATCH 3/3] add strategy to snapshot swiftui views on macos and ios without requiring explicit frame --- .../SnapshotTesting/Snapshotting/NSView.swift | 5 +++ Tests/SnapshotTestingTests/SwiftUITests.swift | 41 ++++++++++++++++++ .../simpleSwiftUIViewAllPlatforms.appkit.png | Bin 0 -> 4975 bytes .../simpleSwiftUIViewAllPlatforms.uikit.png | Bin 0 -> 6860 bytes 4 files changed, 46 insertions(+) create mode 100644 Tests/SnapshotTestingTests/SwiftUITests.swift create mode 100644 Tests/SnapshotTestingTests/__Snapshots__/SwiftUITests/simpleSwiftUIViewAllPlatforms.appkit.png create mode 100644 Tests/SnapshotTestingTests/__Snapshots__/SwiftUITests/simpleSwiftUIViewAllPlatforms.uikit.png diff --git a/Sources/SnapshotTesting/Snapshotting/NSView.swift b/Sources/SnapshotTesting/Snapshotting/NSView.swift index b2e7edfb0..7ccf76378 100644 --- a/Sources/SnapshotTesting/Snapshotting/NSView.swift +++ b/Sources/SnapshotTesting/Snapshotting/NSView.swift @@ -34,8 +34,13 @@ return view.snapshot ?? Async { callback in addImagesForRenderedViews(view).sequence().run { views in + let scaleFactor = NSScreen.main?.backingScaleFactor ?? 1.0 let bitmapRep = view.bitmapImageRepForCachingDisplay(in: view.bounds)! + let scaledSize = CGSize(width: initialSize.width * scaleFactor, height: initialSize.height * scaleFactor) + bitmapRep.size = scaledSize + view.cacheDisplay(in: view.bounds, to: bitmapRep) + let image = NSImage(size: view.bounds.size) image.addRepresentation(bitmapRep) callback(image) diff --git a/Tests/SnapshotTestingTests/SwiftUITests.swift b/Tests/SnapshotTestingTests/SwiftUITests.swift new file mode 100644 index 000000000..08e5ef095 --- /dev/null +++ b/Tests/SnapshotTestingTests/SwiftUITests.swift @@ -0,0 +1,41 @@ +#if compiler(>=6) && canImport(Testing) && canImport(SwiftUI) + import Testing + import SnapshotTesting + import SwiftUI + +/// you can use `NSScreen.main?.backingScaleFactor` or `UIScreen.main.scale` but for more consistent results a fixed value is choosen +let scaleFactor: CGFloat = 4 + +private struct TestView: View { + var body: some View { + ZStack { + Color.white + Text("Hello world") + } + .frame(width: 120) + } +} + +#if canImport(AppKit) +import AppKit + +@MainActor @Test func simpleSwiftUIViewAllPlatforms() async throws { + let renderer = ImageRenderer(content: TestView()) + renderer.scale = scaleFactor + + // Note there is an issue with blurred images https://github.com/pointfreeco/swift-snapshot-testing/issues/428 + // but here the scaleFactor is chosen large so it is nearly not visible. + let nsImage = try #require(renderer.nsImage) + assertSnapshot(of: nsImage, as: .image, named: "appkit") +} +#elseif canImport(UIKit) +@available(iOS 16.0, tvOS 16.0, *) +@MainActor @Test func simpleSwiftUIViewAllPlatforms() async throws { + let renderer = ImageRenderer(content: TestView()) + renderer.scale = scaleFactor + + let nsImage = try #require(renderer.uiImage) + assertSnapshot(of: nsImage, as: .image, named: "uikit") +} +#endif +#endif diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SwiftUITests/simpleSwiftUIViewAllPlatforms.appkit.png b/Tests/SnapshotTestingTests/__Snapshots__/SwiftUITests/simpleSwiftUIViewAllPlatforms.appkit.png new file mode 100644 index 0000000000000000000000000000000000000000..cd195c909f472a56744729189fe76c71564f89c4 GIT binary patch literal 4975 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 gas88{$nY