diff --git a/.editorconfig b/.editorconfig index 410ff6c9e..08891d83f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,8 @@ -# editorconfig.org - root = true [*] indent_style = space -indent_size = 2 -trim_trailing_whitespace = true +indent_size = 4 +end_of_line = lf insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.swift-format b/.swift-format new file mode 100644 index 000000000..f8153414d --- /dev/null +++ b/.swift-format @@ -0,0 +1,62 @@ +{ + "version" : 1, + "indentation" : { + "spaces" : 4 + }, + "tabWidth" : 4, + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "spacesAroundRangeFormationOperators" : false, + "indentConditionalCompilationBlocks" : false, + "indentSwitchCaseLabels" : false, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : true, + "lineBreakBeforeEachGenericRequirement" : true, + "lineLength" : 120, + "maximumBlankLines" : 1, + "respectsExistingLineBreaks" : true, + "prioritizeKeepingFunctionOutputTogether" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : false, + "AmbiguousTrailingClosureOverload" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : true, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : false, + "UseLetInEveryBoundCaseVariable" : false, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : false, + "UseSynthesizedInitializer" : false, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index c28ea7f2c..00d94b9a6 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,6 @@ format: --ignore-unparsable-files \ --in-place \ --recursive \ - ./Package.swift ./Sources ./Tests + Package.swift ./Tests $(find ./Sources -name "*.swift" \ -not -path "./Sources/Deprecated/*") test-all: test-linux test-macos test-ios diff --git a/Package.resolved b/Package.resolved index bf61dcf39..106bfb945 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "fbc61be280c96a8a35bc87505ffcc9ac4f8153be541ef6f7906c6f61b079f514", "pins" : [ { "identity" : "swift-custom-dump", @@ -14,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "0687f71944021d616d34d922343dcef086855920", - "version" : "600.0.1" + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" } }, { @@ -28,5 +29,5 @@ } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index c7a6f7e66..bba2e83de 100644 --- a/Package.swift +++ b/Package.swift @@ -1,69 +1,135 @@ -// swift-tools-version:5.9 +// swift-tools-version: 6.1 import PackageDescription let package = Package( - name: "swift-snapshot-testing", - platforms: [ - .iOS(.v13), - .macOS(.v10_15), - .tvOS(.v13), - .watchOS(.v6), - ], - products: [ - .library( - name: "SnapshotTesting", - targets: ["SnapshotTesting"] - ), - .library( - name: "InlineSnapshotTesting", - targets: ["InlineSnapshotTesting"] - ), - .library( - name: "SnapshotTestingCustomDump", - targets: ["SnapshotTestingCustomDump"] - ), - ], - dependencies: [ - .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), - .package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"602.0.0"), - ], - targets: [ - .target( - name: "SnapshotTesting" - ), - .testTarget( - name: "SnapshotTestingTests", - dependencies: [ - "SnapshotTesting" - ], - exclude: [ - "__Fixtures__", - "__Snapshots__", - ] - ), - .target( - name: "InlineSnapshotTesting", - dependencies: [ - "SnapshotTesting", - "SnapshotTestingCustomDump", - .product(name: "SwiftParser", package: "swift-syntax"), - .product(name: "SwiftSyntax", package: "swift-syntax"), - .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), - ] - ), - .testTarget( - name: "InlineSnapshotTestingTests", - dependencies: [ - "InlineSnapshotTesting" - ] - ), - .target( - name: "SnapshotTestingCustomDump", - dependencies: [ - "SnapshotTesting", - .product(name: "CustomDump", package: "swift-custom-dump"), - ] - ), - ] + name: "swift-snapshot-testing", + platforms: [ + .iOS(.v13), + .macOS(.v10_15), + .tvOS(.v13), + .watchOS(.v6), + ], + products: [ + .library( + name: "XCSnapshotTesting", + targets: ["XCSnapshotTesting"] + ), + .library( + name: "SnapshotTesting", + targets: ["SnapshotTesting"] + ), + .library( + name: "SnapshotTestingCustomDump", + targets: ["SnapshotTestingCustomDump"] + ), + .library( + name: "InlineSnapshotTesting", + targets: ["InlineSnapshotTesting"] + ), + ], + dependencies: [ + .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), + .package(url: "https://github.com/swiftlang/swift-syntax", "601.0.0"..<"602.0.0"), + ], + targets: [ + /* DEPRECATED TARGETS */ + .target( + name: "_SnapshotTesting", + path: "Sources/Deprecated/SnapshotTesting", + swiftSettings: [.swiftLanguageMode(.v5)] + ), + .target( + name: "_SnapshotTestingCustomDump", + dependencies: [ + "_SnapshotTesting", + .product(name: "CustomDump", package: "swift-custom-dump"), + ], + path: "Sources/Deprecated/SnapshotTestingCustomDump", + swiftSettings: [.swiftLanguageMode(.v5)] + ), + .target( + name: "_InlineSnapshotTesting", + dependencies: [ + "_SnapshotTesting", + "_SnapshotTestingCustomDump", + .product(name: "SwiftParser", package: "swift-syntax"), + .product(name: "SwiftSyntax", package: "swift-syntax"), + .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), + ], + path: "Sources/Deprecated/InlineSnapshotTesting", + swiftSettings: [.swiftLanguageMode(.v5)] + ), + /* TARGETS */ + .target( + name: "XCSnapshotTesting", + dependencies: ["_SnapshotTesting"] + ), + .target( + name: "SnapshotTesting", + dependencies: ["XCSnapshotTesting"] + ), + .target( + name: "SnapshotTestingCustomDump", + dependencies: [ + "XCSnapshotTesting", + .product(name: "CustomDump", package: "swift-custom-dump"), + "_SnapshotTestingCustomDump", + ] + ), + .target( + name: "InlineSnapshotTesting", + dependencies: [ + "SnapshotTesting", + "SnapshotTestingCustomDump", + .product(name: "SwiftParser", package: "swift-syntax"), + .product(name: "SwiftSyntax", package: "swift-syntax"), + .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), + "_InlineSnapshotTesting", + ] + ), + /* DEPRECATED TESTS */ + .testTarget( + name: "_SnapshotTestingTests", + dependencies: ["XCSnapshotTesting", "SnapshotTesting"], + path: "Tests/Deprecated/SnapshotTestingTests", + exclude: [ + "__Fixtures__", + "__Snapshots__", + ], + swiftSettings: [.swiftLanguageMode(.v5)] + ), + .testTarget( + name: "_InlineSnapshotTestingTests", + dependencies: ["InlineSnapshotTesting"], + path: "Tests/Deprecated/InlineSnapshotTestingTests", + swiftSettings: [.swiftLanguageMode(.v5)] + ), + /* TESTS */ + .testTarget( + name: "XCSnapshotTestingTests", + dependencies: ["XCSnapshotTesting"], + exclude: [ + "__Fixtures__", + "__Snapshots__", + ] + ), + .testTarget( + name: "SnapshotTestingTests", + dependencies: ["SnapshotTesting"], + exclude: ["__Snapshots__"] + ), + .testTarget( + name: "SnapshotTestingCustomDumpTests", + dependencies: [ + "SnapshotTestingCustomDump" + ] + ), + .testTarget( + name: "InlineSnapshotTestingTests", + dependencies: [ + "InlineSnapshotTesting" + ] + ), + ] ) diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift deleted file mode 100644 index 68cfa74ce..000000000 --- a/Package@swift-6.0.swift +++ /dev/null @@ -1,70 +0,0 @@ -// swift-tools-version:6.0 - -import PackageDescription - -let package = Package( - name: "swift-snapshot-testing", - platforms: [ - .iOS(.v13), - .macOS(.v10_15), - .tvOS(.v13), - .watchOS(.v6), - ], - products: [ - .library( - name: "SnapshotTesting", - targets: ["SnapshotTesting"] - ), - .library( - name: "InlineSnapshotTesting", - targets: ["InlineSnapshotTesting"] - ), - .library( - name: "SnapshotTestingCustomDump", - targets: ["SnapshotTestingCustomDump"] - ), - ], - dependencies: [ - .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), - .package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"602.0.0"), - ], - targets: [ - .target( - name: "SnapshotTesting" - ), - .testTarget( - name: "SnapshotTestingTests", - dependencies: [ - "SnapshotTesting" - ], - exclude: [ - "__Fixtures__", - "__Snapshots__", - ] - ), - .target( - name: "InlineSnapshotTesting", - dependencies: [ - "SnapshotTesting", - "SnapshotTestingCustomDump", - .product(name: "SwiftParser", package: "swift-syntax"), - .product(name: "SwiftSyntax", package: "swift-syntax"), - .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), - ] - ), - .testTarget( - name: "InlineSnapshotTestingTests", - dependencies: [ - "InlineSnapshotTesting" - ] - ), - .target( - name: "SnapshotTestingCustomDump", - dependencies: [ - "SnapshotTesting", - .product(name: "CustomDump", package: "swift-custom-dump"), - ] - ), - ], - swiftLanguageModes: [.v5] -) diff --git a/README.md b/README.md index c26595221..3ac58046a 100644 --- a/README.md +++ b/README.md @@ -9,192 +9,74 @@ Delightful Swift snapshot testing. ## Usage -Once [installed](#installation), _no additional configuration is required_. You can import the -`SnapshotTesting` module and call the `assertSnapshot` function. +Once [installed](#installation), no additional configuration is required. You can import the `SnapshotTesting` module and call the `assert` function when using Swift Testing. -``` swift +```swift import SnapshotTesting -import Testing @MainActor -struct MyViewControllerTests { - @Test func myViewController() { - let vc = MyViewController() - - assertSnapshot(of: vc, as: .image) - } -} -``` - -When an assertion first runs, a snapshot is automatically recorded to disk and the test will fail, -printing out the file path of any newly-recorded reference. - -> ❌ failed - No reference was found on disk. Automatically recorded snapshot: … -> -> open "…/MyAppTests/\_\_Snapshots\_\_/MyViewControllerTests/testMyViewController.png" -> -> Re-run "testMyViewController" to test against the newly-recorded snapshot. - -Repeat test runs will load this reference and compare it with the runtime value. If they don't -match, the test will fail and describe the difference. Failures can be inspected from Xcode's Report -Navigator or by inspecting the file URLs of the failure. - -You can record a new reference by customizing snapshots inline with the assertion, or using the -`withSnapshotTesting` tool: - -```swift -// Record just this one snapshot -assertSnapshot(of: vc, as: .image, record: .all) - -// Record all snapshots in a scope: -withSnapshotTesting(record: .all) { - assertSnapshot(of: vc1, as: .image) - assertSnapshot(of: vc2, as: .image) - assertSnapshot(of: vc3, as: .image) -} - -// Record all snapshot failures in a Swift Testing suite: -@Suite(.snapshots(record: .failed)) -struct FeatureTests {} - -// Record all snapshot failures in an 'XCTestCase' subclass: -class FeatureTests: XCTestCase { - override func invokeTest() { - withSnapshotTesting(record: .failed) { - super.invokeTest() +final class MyViewControllerTests: XCTestCase { + func testMyViewController() async throws { + let vc = MyViewController() + try await assert(of: vc, as: .image) } - } } ``` -## Snapshot Anything - -While most snapshot testing libraries in the Swift community are limited to `UIImage`s of `UIView`s, -SnapshotTesting can work with _any_ format of _any_ value on _any_ Swift platform! - -The `assertSnapshot` function accepts a value and any snapshot strategy that value supports. This -means that a view or view controller can be tested against an image representation _and_ against a -textual representation of its properties and subview hierarchy. - -``` swift -assertSnapshot(of: vc, as: .image) -assertSnapshot(of: vc, as: .recursiveDescription) -``` - -View testing is highly configurable. You can override trait collections (for specific size classes -and content size categories) and generate device-agnostic snapshots, all from a single simulator. - -``` swift -assertSnapshot(of: vc, as: .image(on: .iPhoneSe)) -assertSnapshot(of: vc, as: .recursiveDescription(on: .iPhoneSe)) - -assertSnapshot(of: vc, as: .image(on: .iPhoneSe(.landscape))) -assertSnapshot(of: vc, as: .recursiveDescription(on: .iPhoneSe(.landscape))) - -assertSnapshot(of: vc, as: .image(on: .iPhoneX)) -assertSnapshot(of: vc, as: .recursiveDescription(on: .iPhoneX)) +> When an assertion runs for the first time, a snapshot is automatically recorded to disk, and the test will fail, printing the file path of the newly recorded reference. -assertSnapshot(of: vc, as: .image(on: .iPadMini(.portrait))) -assertSnapshot(of: vc, as: .recursiveDescription(on: .iPadMini(.portrait))) -``` +> Repeat test runs will load this reference and compare it with the runtime value. If they don't match, the test will fail and describe the difference. -> **Warning** -> Snapshots must be compared using the exact same simulator that originally took the reference to -> avoid discrepancies between images. +You can record a new reference by customizing snapshots inline with the assertion or using the `withTestingEnvironment` method. -Better yet, SnapshotTesting isn't limited to views and view controllers! There are a number of -available snapshot strategies to choose from. +## Snapshot Anything -For example, you can snapshot test URL requests (_e.g._, those that your API client prepares). +SnapshotTesting isn't limited to `UIView`s and `UIViewController`s. You can snapshot test any value on any Swift platform! -``` swift -assertSnapshot(of: urlRequest, as: .raw) -// POST http://localhost:8080/account -// Cookie: pf_session={"userId":"1"} -// -// email=blob%40pointfree.co&name=Blob +```swift +try await assert(of: user, as: .json) +try await assert(of: user, as: .plist) +try await assert(of: user, as: .customDump) ``` -And you can snapshot test `Encodable` values against their JSON _and_ property list representations. - -``` swift -assertSnapshot(of: user, as: .json) -// { -// "bio" : "Blobbed around the world.", -// "id" : 1, -// "name" : "Blobby" -// } - -assertSnapshot(of: user, as: .plist) -// -// -// -// -// bio -// Blobbed around the world. -// id -// 1 -// name -// Blobby -// -// -``` +## Documentation -In fact, _any_ value can be snapshot-tested by default using its -[mirror](https://developer.apple.com/documentation/swift/mirror)! +The latest documentation is available for both main components of the framework: -``` swift -assertSnapshot(of: user, as: .dump) -// ▿ User -// - bio: "Blobbed around the world." -// - id: 1 -// - name: "Blobby" -``` +- For **XCSnapshotTesting** (the core snapshot testing functionality): + [XCSnapshotTesting Documentation](https://swiftpackageindex.com/pointfreeco/swift-snapshot-testing/main/documentation/xcsnapshottesting) -If your data can be represented as an image, text, or data, you can write a snapshot test for it! +- For **SnapshotTesting** (the Swift Testing integration and utilities): + [SnapshotTesting Documentation](https://swiftpackageindex.com/pointfreeco/swift-snapshot-testing/main/documentation/snapshottesting) -## Documentation - -The latest documentation is available -[here](https://swiftpackageindex.com/pointfreeco/swift-snapshot-testing/main/documentation/snapshottesting). +These documents provide detailed information on how to use each component effectively in your testing workflows. ## Installation ### Xcode -> **Warning** -> By default, Xcode will try to add the SnapshotTesting package to your project's main -> application/framework target. Please ensure that SnapshotTesting is added to a _test_ target -> instead, as documented in the last step, below. - - 1. From the **File** menu, navigate through **Swift Packages** and select - **Add Package Dependency…**. - 2. Enter package repository URL: `https://github.com/pointfreeco/swift-snapshot-testing`. - 3. Confirm the version and let Xcode resolve the package. - 4. On the final dialog, update SnapshotTesting's **Add to Target** column to a test target that - will contain snapshot tests (if you have more than one test target, you can later add - SnapshotTesting to them by manually linking the library in its build phase). +1. From the **File** menu, navigate to **Swift Packages** and select **Add Package Dependency…**. +2. Enter the package repository URL: `https://github.com/pointfreeco/swift-snapshot-testing`. +3. Confirm the version and let Xcode resolve the package. +4. Ensure SnapshotTesting is added to a test target. ### Swift Package Manager -If you want to use SnapshotTesting in any other project that uses -[SwiftPM](https://swift.org/package-manager/), add the package as a dependency in `Package.swift`: +Add the package as a dependency in `Package.swift`: ```swift dependencies: [ .package( url: "https://github.com/pointfreeco/swift-snapshot-testing", - from: "1.12.0" + from: "2.0.0" ), ] ``` -Next, add `SnapshotTesting` as a dependency of your test target: +Next, add `SnapshotTesting` to your test target: ```swift targets: [ - .target(name: "MyApp"), .testTarget( name: "MyAppTests", dependencies: [ @@ -207,76 +89,58 @@ targets: [ ## Features - - [**Dozens of snapshot strategies**][available-strategies]. Snapshot - testing isn't just for `UIView`s and `CALayer`s. Write snapshots against _any_ value. - - [**Write your own snapshot strategies**][defining-strategies]. - If you can convert it to an image, string, data, or your own diffable format, you can snapshot - test it! Build your own snapshot strategies from scratch or transform existing ones. - - **No configuration required.** Don't fuss with scheme settings and environment variables. - Snapshots are automatically saved alongside your tests. - - **More hands-off.** New snapshots are recorded whether `isRecording` mode is `true` or not. - - **Subclass-free.** Assert from any XCTest case or Quick spec. - - **Device-agnostic snapshots.** Render views and view controllers for specific devices and trait - collections from a single simulator. - - **First-class Xcode support.** Image differences are captured as XCTest attachments. Text - differences are rendered in inline error messages. - - **Supports any platform that supports Swift.** Write snapshot tests for iOS, Linux, macOS, and - tvOS. - - **SceneKit, SpriteKit, and WebKit support.** Most snapshot testing libraries don't support these - view subclasses. - - **`Codable` support**. Snapshot encodable data structures into their JSON and property list - representations. - - **Custom diff tool integration**. Configure failure messages to print diff commands for - [Kaleidoscope](https://kaleidoscope.app) or your diff tool of choice. - ``` swift - SnapshotTesting.diffToolCommand = { "ksdiff \($0) \($1)" } - ``` - -[available-strategies]: https://swiftpackageindex.com/pointfreeco/swift-snapshot-testing/main/documentation/snapshottesting/snapshotting -[defining-strategies]: https://swiftpackageindex.com/pointfreeco/swift-snapshot-testing/main/documentation/snapshottesting/customstrategies - -## Plug-ins - - - [AccessibilitySnapshot](https://github.com/cashapp/AccessibilitySnapshot) adds easy regression - testing for iOS accessibility. - - - [AccessibilitySnapshotColorBlindness](https://github.com/Sherlouk/AccessibilitySnapshotColorBlindness) - adds snapshot strategies for color blindness simulation on iOS views, view controllers and images. - - - [GRDBSnapshotTesting](https://github.com/SebastianOsinski/GRDBSnapshotTesting) adds snapshot - strategy for testing SQLite database migrations made with [GRDB](https://github.com/groue/GRDB.swift). - - - [Nimble-SnapshotTesting](https://github.com/tahirmt/Nimble-SnapshotTesting) adds - [Nimble](https://github.com/Quick/Nimble) matchers for SnapshotTesting to be used by Swift - Package Manager. - - - [Prefire](https://github.com/BarredEwe/Prefire) generating Snapshot Tests via - [Swift Package Plugins](https://github.com/apple/swift-package-manager/blob/main/Documentation/Plugins.md) - using SwiftUI `Preview` - - - [PreviewSnapshots](https://github.com/doordash-oss/swiftui-preview-snapshots) share `View` - configurations between SwiftUI Previews and snapshot tests and generate several snapshots with a - single test assertion. - - - [swift-html](https://github.com/pointfreeco/swift-html) is a Swift DSL for type-safe, - extensible, and transformable HTML documents and includes an `HtmlSnapshotTesting` module to - snapshot test its HTML documents. - - - [swift-snapshot-testing-nimble](https://github.com/Killectro/swift-snapshot-testing-nimble) adds - [Nimble](https://github.com/Quick/Nimble) matchers for SnapshotTesting. - - - [swift-snapshot-testing-stitch](https://github.com/Sherlouk/swift-snapshot-testing-stitch/) adds - the ability to stitch multiple UIView's or UIViewController's together in a single test. - - - [SnapshotTestingDump](https://github.com/tahirmt/swift-snapshot-testing-dump) Adds support to - use [swift-custom-dump](https://github.com/pointfreeco/swift-custom-dump/) by using `customDump` - strategy for `Any` - - - [SnapshotTestingHEIC](https://github.com/alexey1312/SnapshotTestingHEIC) adds image support - using the HEIC storage format which reduces file sizes in comparison to PNG. - - - [SnapshotVision](https://github.com/gregersson/swift-snapshot-testing-vision) adds snapshot - strategy for text recognition on views and images. Uses Apples Vision framework. +- **Versatile Snapshot Strategies**: Test any value, not just UI components. +- **Custom Snapshot Strategies**: Create your own snapshot strategies. +- **No Configuration Required**: Snapshots are saved alongside your tests automatically. +- **Device-Agnostic Snapshots**: Render views for specific devices from a single simulator. +- **Xcode Integration**: Image differences are captured as XCTest attachments. +- **Cross-Platform Support**: Supports iOS, macOS, tvOS, and more. +- **SceneKit, SpriteKit, and WebKit Support**: Test these specialized views. +- **Codable Support**: Snapshot encodable data structures into JSON and property list representations. +- **Custom Diff Tool Integration**: Configure failure messages to print diff commands for tools like Kaleidoscope. + +## Plugins + +- [AccessibilitySnapshot](https://github.com/cashapp/AccessibilitySnapshot) adds easy regression + testing for iOS accessibility. + +- [AccessibilitySnapshotColorBlindness](https://github.com/Sherlouk/AccessibilitySnapshotColorBlindness) + adds snapshot strategies for color blindness simulation on iOS views, view controllers and images. + +- [GRDBSnapshotTesting](https://github.com/SebastianOsinski/GRDBSnapshotTesting) adds snapshot + strategy for testing SQLite database migrations made with [GRDB](https://github.com/groue/GRDB.swift). + +- [Nimble-SnapshotTesting](https://github.com/tahirmt/Nimble-SnapshotTesting) adds + [Nimble](https://github.com/Quick/Nimble) matchers for SnapshotTesting to be used by Swift + Package Manager. + +- [Prefire](https://github.com/BarredEwe/Prefire) generating Snapshot Tests via + [Swift Package Plugins](https://github.com/apple/swift-package-manager/blob/main/Documentation/Plugins.md) + using SwiftUI `Preview` + +- [PreviewSnapshots](https://github.com/doordash-oss/swiftui-preview-snapshots) share `View` + configurations between SwiftUI Previews and snapshot tests and generate several snapshots with a + single test assertion. + +- [swift-html](https://github.com/pointfreeco/swift-html) is a Swift DSL for type-safe, + extensible, and transformable HTML documents and includes an `HtmlSnapshotTesting` module to + snapshot test its HTML documents. + +- [swift-snapshot-testing-nimble](https://github.com/Killectro/swift-snapshot-testing-nimble) adds + [Nimble](https://github.com/Quick/Nimble) matchers for SnapshotTesting. + +- [swift-snapshot-testing-stitch](https://github.com/Sherlouk/swift-snapshot-testing-stitch/) adds + the ability to stitch multiple UIView's or UIViewController's together in a single test. + +- [SnapshotTestingDump](https://github.com/tahirmt/swift-snapshot-testing-dump) Adds support to + use [swift-custom-dump](https://github.com/pointfreeco/swift-custom-dump/) by using `customDump` + strategy for `Any` + +- [SnapshotTestingHEIC](https://github.com/alexey1312/SnapshotTestingHEIC) adds image support +using the HEIC storage format which reduces file sizes in comparison to PNG. + +- [SnapshotVision](https://github.com/gregersson/swift-snapshot-testing-vision) adds snapshot + strategy for text recognition on views and images. Uses Apples Vision framework. Have you written your own SnapshotTesting plug-in? [Add it here](https://github.com/pointfreeco/swift-snapshot-testing/edit/master/README.md) and @@ -284,13 +148,13 @@ submit a pull request! ## Related Tools - - [`iOSSnapshotTestCase`](https://github.com/uber/ios-snapshot-test-case/) helped introduce screen +- [`iOSSnapshotTestCase`](https://github.com/uber/ios-snapshot-test-case/) helped introduce screen shot testing to a broad audience in the iOS community. Experience with it inspired the creation of this library. - - [Jest](https://jestjs.io) brought generalized snapshot testing to the JavaScript community with - a polished user experience. Several features of this library (diffing, automatically capturing - new snapshots) were directly influenced. +- [Jest](https://jestjs.io) brought generalized snapshot testing to the JavaScript community with + a polished user experience. Several features of this library (diffing, automatically capturing + new snapshots) were directly influenced. ## Learn More diff --git a/Sources/InlineSnapshotTesting/AssertInlineSnapshot.swift b/Sources/Deprecated/InlineSnapshotTesting/AssertInlineSnapshot.swift similarity index 97% rename from Sources/InlineSnapshotTesting/AssertInlineSnapshot.swift rename to Sources/Deprecated/InlineSnapshotTesting/AssertInlineSnapshot.swift index facf79d4b..88a14ebc6 100644 --- a/Sources/InlineSnapshotTesting/AssertInlineSnapshot.swift +++ b/Sources/Deprecated/InlineSnapshotTesting/AssertInlineSnapshot.swift @@ -1,7 +1,7 @@ import Foundation -#if canImport(SwiftSyntax509) - @_spi(Internals) import SnapshotTesting +#if canImport(SwiftSyntax601) + @_spi(Internals) import _SnapshotTesting import SwiftParser import SwiftSyntax import SwiftSyntaxBuilder @@ -33,6 +33,7 @@ import Foundation /// function was called. /// - column: The column on which failure occurred. Defaults to the column on which this /// function was called. + @available(*, deprecated, renamed: "assertInline") public func assertInlineSnapshot( of value: @autoclosure () throws -> Value?, as snapshotting: Snapshotting, @@ -216,6 +217,7 @@ import Foundation /// Provide this structure when defining custom snapshot functions that call /// ``assertInlineSnapshot(of:as:message:record:timeout:syntaxDescriptor:matches:file:function:line:column:)`` /// under the hood. +@available(*, deprecated, renamed: "SnapshotClosureDescriptor") public struct InlineSnapshotSyntaxDescriptor: Hashable { /// The default label describing an inline snapshot. public static let defaultTrailingClosureLabel = "matches" @@ -270,7 +272,7 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { self.trailingClosureOffset = trailingClosureOffset } - #if canImport(SwiftSyntax509) + #if canImport(SwiftSyntax601) /// Generates a test failure immediately and unconditionally at the described trailing closure. /// /// This method will attempt to locate the line of the trailing closure described by this type @@ -333,7 +335,8 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { // MARK: - Private -#if canImport(SwiftSyntax509) +#if canImport(SwiftSyntax601) + @available(*, deprecated) private let installTestObserver: Void = { final class InlineSnapshotObserver: NSObject, XCTestObservation { func testBundleDidFinish(_ testBundle: Bundle) { @@ -343,12 +346,13 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { if Thread.isMainThread { XCTestObservationCenter.shared.addTestObserver(InlineSnapshotObserver()) } else { - DispatchQueue.main.sync { - XCTestObservationCenter.shared.addTestObserver(InlineSnapshotObserver()) - } + DispatchQueue.main.sync { + XCTestObservationCenter.shared.addTestObserver(InlineSnapshotObserver()) + } } }() + @available(*, deprecated) @_spi(Internals) public struct File: Hashable { public let path: StaticString public static func == (lhs: Self, rhs: Self) -> Bool { @@ -359,6 +363,7 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { } } + @available(*, deprecated) @_spi(Internals) public struct InlineSnapshot: Hashable { public var expected: String? public var actual: String? @@ -369,14 +374,17 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { public var column: UInt } + @available(*, deprecated) @_spi(Internals) public var inlineSnapshotState: [File: [InlineSnapshot]] = [:] + @available(*, deprecated) private struct TestSource { let source: String let sourceFile: SourceFileSyntax let sourceLocationConverter: SourceLocationConverter } + @available(*, deprecated) private func testSource(file: File) throws -> TestSource { guard let testSource = testSourceCache[file] else { @@ -395,8 +403,10 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { return testSource } + @available(*, deprecated) private var testSourceCache: [File: TestSource] = [:] + @available(*, deprecated) private func writeInlineSnapshots() { defer { inlineSnapshotState.removeAll() } for (file, snapshots) in inlineSnapshotState { @@ -425,6 +435,7 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { } } + @available(*, deprecated) private final class SnapshotRewriter: SyntaxRewriter { let file: File var function: String? @@ -659,6 +670,7 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { } } + @available(*, deprecated) private final class SnapshotVisitor: SyntaxVisitor { let functionCallColumn: Int let functionCallLine: Int @@ -736,6 +748,7 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable { } } + @available(*, deprecated) extension String { fileprivate func indenting(by count: Int) -> String { self.indenting(with: String(repeating: " ", count: count)) diff --git a/Sources/Deprecated/InlineSnapshotTesting/Exports.swift b/Sources/Deprecated/InlineSnapshotTesting/Exports.swift new file mode 100644 index 000000000..5374a3efc --- /dev/null +++ b/Sources/Deprecated/InlineSnapshotTesting/Exports.swift @@ -0,0 +1 @@ +@_exported import _SnapshotTesting diff --git a/Sources/SnapshotTesting/AssertSnapshot.swift b/Sources/Deprecated/SnapshotTesting/AssertSnapshot.swift similarity index 95% rename from Sources/SnapshotTesting/AssertSnapshot.swift rename to Sources/Deprecated/SnapshotTesting/AssertSnapshot.swift index 11e761422..9a562e846 100644 --- a/Sources/SnapshotTesting/AssertSnapshot.swift +++ b/Sources/Deprecated/SnapshotTesting/AssertSnapshot.swift @@ -6,12 +6,7 @@ import XCTest /// Enhances failure messages with a command line diff tool expression that can be copied and pasted /// into a terminal. -@available( - *, - deprecated, - message: - "Use 'withSnapshotTesting' to customize the diff tool. See the documentation for more information." -) +@available(*, deprecated, renamed: "withTestingEnvironment(diffTool:operation:)") public var diffTool: SnapshotTestingConfiguration.DiffTool { get { _diffTool @@ -19,6 +14,7 @@ public var diffTool: SnapshotTestingConfiguration.DiffTool { set { _diffTool = newValue } } +@available(*, deprecated, renamed: "withTestingEnvironment(diffTool:operation:)") @_spi(Internals) public var _diffTool: SnapshotTestingConfiguration.DiffTool { get { @@ -38,20 +34,18 @@ public var _diffTool: SnapshotTestingConfiguration.DiffTool { } } +@available(*, deprecated, renamed: "withTestingEnvironment(diffTool:operation:)") @_spi(Internals) public var __diffTool: SnapshotTestingConfiguration.DiffTool = .default /// Whether or not to record all new references. -@available( - *, deprecated, - message: - "Use 'withSnapshotTesting' to customize the record mode. See the documentation for more information." -) +@available(*, deprecated, renamed: "withTestingEnvironment(record:operation:)") public var isRecording: Bool { get { SnapshotTestingConfiguration.current?.record ?? _record == .all } set { _record = newValue ? .all : .missing } } +@available(*, deprecated, renamed: "withTestingEnvironment(record:operation:)") @_spi(Internals) public var _record: SnapshotTestingConfiguration.Record { get { @@ -71,6 +65,7 @@ public var _record: SnapshotTestingConfiguration.Record { } } +@available(*, deprecated, renamed: "withTestingEnvironment(record:operation:)") @_spi(Internals) public var __record: SnapshotTestingConfiguration.Record = { if let value = ProcessInfo.processInfo.environment["SNAPSHOT_TESTING_RECORD"], @@ -99,6 +94,9 @@ public var __record: SnapshotTestingConfiguration.Record = { /// function was called. /// - column: The column on which failure occurred. Defaults to the column on which this function /// was called. +@available( + *, deprecated, renamed: "assert(of:as:named:record:timeout:fileID:file:testName:line:column:)" +) public func assertSnapshot( of value: @autoclosure () throws -> Value, as snapshotting: Snapshotting, @@ -151,6 +149,7 @@ public func assertSnapshot( /// function was called. /// - column: The column on which failure occurred. Defaults to the column on which this function /// was called. +@available(*, deprecated, renamed: "assert(of:as:record:timeout:fileID:file:testName:line:column:)") public func assertSnapshots( of value: @autoclosure () throws -> Value, as strategies: [String: Snapshotting], @@ -195,6 +194,7 @@ public func assertSnapshots( /// function was called. /// - column: The column on which failure occurred. Defaults to the column on which this function /// was called. +@available(*, deprecated, renamed: "assert(of:as:record:timeout:fileID:file:testName:line:column:)") public func assertSnapshots( of value: @autoclosure () throws -> Value, as strategies: [Snapshotting], @@ -272,6 +272,10 @@ public func assertSnapshots( /// - line: The line number on which failure occurred. Defaults to the line number on which this /// function was called. /// - Returns: A failure message or, if the value matches, nil. +@available( + *, deprecated, + renamed: "verify(of:as:named:record:snapshotDirectory:timeout:fileID:file:testName:line:column:)" +) public func verifySnapshot( of value: @autoclosure () throws -> Value, as snapshotting: Snapshotting, diff --git a/Sources/SnapshotTesting/Async.swift b/Sources/Deprecated/SnapshotTesting/Async.swift similarity index 97% rename from Sources/SnapshotTesting/Async.swift rename to Sources/Deprecated/SnapshotTesting/Async.swift index 2c16adda5..1357b8dfb 100644 --- a/Sources/SnapshotTesting/Async.swift +++ b/Sources/Deprecated/SnapshotTesting/Async.swift @@ -13,6 +13,7 @@ /// } /// } /// ``` +@available(*, deprecated, renamed: "Sync") public struct Async { public let run: (@escaping (Value) -> Void) -> Void diff --git a/Sources/SnapshotTesting/Common/Internal.swift b/Sources/Deprecated/SnapshotTesting/Common/Internal.swift similarity index 100% rename from Sources/SnapshotTesting/Common/Internal.swift rename to Sources/Deprecated/SnapshotTesting/Common/Internal.swift diff --git a/Sources/SnapshotTesting/Common/PlistEncoder.swift b/Sources/Deprecated/SnapshotTesting/Common/PlistEncoder.swift similarity index 100% rename from Sources/SnapshotTesting/Common/PlistEncoder.swift rename to Sources/Deprecated/SnapshotTesting/Common/PlistEncoder.swift diff --git a/Sources/SnapshotTesting/Common/String+SpecialCharacters.swift b/Sources/Deprecated/SnapshotTesting/Common/String+SpecialCharacters.swift similarity index 100% rename from Sources/SnapshotTesting/Common/String+SpecialCharacters.swift rename to Sources/Deprecated/SnapshotTesting/Common/String+SpecialCharacters.swift diff --git a/Sources/SnapshotTesting/Common/View.swift b/Sources/Deprecated/SnapshotTesting/Common/View.swift similarity index 99% rename from Sources/SnapshotTesting/Common/View.swift rename to Sources/Deprecated/SnapshotTesting/Common/View.swift index 753153749..07754da41 100644 --- a/Sources/SnapshotTesting/Common/View.swift +++ b/Sources/Deprecated/SnapshotTesting/Common/View.swift @@ -12,6 +12,7 @@ #endif #if os(iOS) || os(tvOS) + @available(*, deprecated, renamed: "LayoutConfiguration") public struct ViewImageConfig: Sendable { public enum Orientation { case landscape @@ -502,6 +503,7 @@ #endif } + @available(*, deprecated) extension UITraitCollection { #if os(iOS) public static func iPhoneSe(_ orientation: ViewImageConfig.Orientation) @@ -808,6 +810,7 @@ } #endif + @available(*, deprecated) func addImagesForRenderedViews(_ view: View) -> [Async] { return view.snapshot .map { async in @@ -830,6 +833,7 @@ ?? view.subviews.flatMap(addImagesForRenderedViews) } + @available(*, deprecated) extension View { var snapshot: Async? { func inWindow(_ perform: () -> T) -> T { @@ -926,6 +930,7 @@ } } + @available(*, deprecated) func prepareView( config: ViewImageConfig, drawHierarchyInKeyWindow: Bool, @@ -965,6 +970,7 @@ return dispose } + @available(*, deprecated) func snapshotView( config: ViewImageConfig, drawHierarchyInKeyWindow: Bool, @@ -1084,6 +1090,7 @@ return window } + @available(*, deprecated) private final class Window: UIWindow { var config: ViewImageConfig @@ -1136,6 +1143,7 @@ #endif #endif +@available(*, deprecated) extension Array { func sequence() -> Async<[A]> where Element == Async { guard !self.isEmpty else { return Async(value: []) } diff --git a/Sources/SnapshotTesting/Common/XCTAttachment.swift b/Sources/Deprecated/SnapshotTesting/Common/XCTAttachment.swift similarity index 76% rename from Sources/SnapshotTesting/Common/XCTAttachment.swift rename to Sources/Deprecated/SnapshotTesting/Common/XCTAttachment.swift index 117dd26c0..f17d254f1 100644 --- a/Sources/SnapshotTesting/Common/XCTAttachment.swift +++ b/Sources/Deprecated/SnapshotTesting/Common/XCTAttachment.swift @@ -1,6 +1,7 @@ #if os(Linux) || os(Android) || os(Windows) import Foundation + @available(*, deprecated, message: "Not available anymore") public struct XCTAttachment { public init(data: Data) {} public init(data: Data, uniformTypeIdentifier: String) {} diff --git a/Sources/SnapshotTesting/Diff.swift b/Sources/Deprecated/SnapshotTesting/Diff.swift similarity index 100% rename from Sources/SnapshotTesting/Diff.swift rename to Sources/Deprecated/SnapshotTesting/Diff.swift diff --git a/Sources/SnapshotTesting/Diffing.swift b/Sources/Deprecated/SnapshotTesting/Diffing.swift similarity index 94% rename from Sources/SnapshotTesting/Diffing.swift rename to Sources/Deprecated/SnapshotTesting/Diffing.swift index c189578ec..9ebc15ad5 100644 --- a/Sources/SnapshotTesting/Diffing.swift +++ b/Sources/Deprecated/SnapshotTesting/Diffing.swift @@ -2,6 +2,7 @@ import Foundation import XCTest /// The ability to compare `Value`s and convert them to and from `Data`. +@available(*, deprecated, renamed: "DiffAttachmentGenerator") public struct Diffing { /// Converts a value _to_ data. public var toData: (Value) -> Data diff --git a/Sources/SnapshotTesting/Extensions/Wait.swift b/Sources/Deprecated/SnapshotTesting/Extensions/Wait.swift similarity index 97% rename from Sources/SnapshotTesting/Extensions/Wait.swift rename to Sources/Deprecated/SnapshotTesting/Extensions/Wait.swift index f15c7da19..f300c48f7 100644 --- a/Sources/SnapshotTesting/Extensions/Wait.swift +++ b/Sources/Deprecated/SnapshotTesting/Extensions/Wait.swift @@ -1,6 +1,7 @@ import Foundation import XCTest +@available(*, deprecated) extension Snapshotting { /// Transforms an existing snapshot strategy into one that waits for some amount of time before /// taking the snapshot. This can be useful for waiting for animations to complete or for UIKit diff --git a/Sources/SnapshotTesting/Internal/Deprecations.swift b/Sources/Deprecated/SnapshotTesting/Internal/Deprecations.swift similarity index 100% rename from Sources/SnapshotTesting/Internal/Deprecations.swift rename to Sources/Deprecated/SnapshotTesting/Internal/Deprecations.swift diff --git a/Sources/SnapshotTesting/Internal/RecordIssue.swift b/Sources/Deprecated/SnapshotTesting/Internal/RecordIssue.swift similarity index 89% rename from Sources/SnapshotTesting/Internal/RecordIssue.swift rename to Sources/Deprecated/SnapshotTesting/Internal/RecordIssue.swift index 214761180..0d68f0c37 100644 --- a/Sources/SnapshotTesting/Internal/RecordIssue.swift +++ b/Sources/Deprecated/SnapshotTesting/Internal/RecordIssue.swift @@ -13,6 +13,7 @@ var isSwiftTesting: Bool { } @_spi(Internals) +@available(*, deprecated, renamed: "TestingSystem.shared.record(_:fileID:filePath:line:column:)") public func recordIssue( _ message: @autoclosure () -> String, fileID: StaticString, diff --git a/Sources/SnapshotTesting/SnapshotTestingConfiguration.swift b/Sources/Deprecated/SnapshotTesting/SnapshotTestingConfiguration.swift similarity index 91% rename from Sources/SnapshotTesting/SnapshotTestingConfiguration.swift rename to Sources/Deprecated/SnapshotTesting/SnapshotTestingConfiguration.swift index de8908173..1b78c4720 100644 --- a/Sources/SnapshotTesting/SnapshotTestingConfiguration.swift +++ b/Sources/Deprecated/SnapshotTesting/SnapshotTestingConfiguration.swift @@ -23,6 +23,7 @@ /// - record: The record mode to use while asserting snapshots. /// - diffTool: The diff tool to use while asserting snapshots. /// - operation: The operation to perform. +@available(*, deprecated, renamed: "withTestingEnvironment(record:diffTool:operation:)") public func withSnapshotTesting( record: SnapshotTestingConfiguration.Record? = nil, diffTool: SnapshotTestingConfiguration.DiffTool? = nil, @@ -32,7 +33,7 @@ public func withSnapshotTesting( SnapshotTestingConfiguration( record: record ?? SnapshotTestingConfiguration.current?.record ?? _record, diffTool: diffTool ?? SnapshotTestingConfiguration.current?.diffTool - ?? SnapshotTesting._diffTool + ?? _SnapshotTesting._diffTool ) ) { try operation() @@ -42,6 +43,7 @@ public func withSnapshotTesting( /// Customizes `assertSnapshot` for the duration of an asynchronous operation. /// /// See ``withSnapshotTesting(record:diffTool:operation:)-2kuyr`` for more information. +@available(*, deprecated, renamed: "withTestingEnvironment(record:diffTool:operation:)") public func withSnapshotTesting( record: SnapshotTestingConfiguration.Record? = nil, diffTool: SnapshotTestingConfiguration.DiffTool? = nil, @@ -58,6 +60,7 @@ public func withSnapshotTesting( } /// The configuration for a snapshot test. +@available(*, deprecated, message: "Migrate to withTestingEnvironment(operation:)") public struct SnapshotTestingConfiguration: Sendable { @_spi(Internals) @TaskLocal public static var current: Self? @@ -85,6 +88,7 @@ public struct SnapshotTestingConfiguration: Sendable { /// There are 4 primary strategies for recording: ``Record-swift.struct/all``, /// ``Record-swift.struct/missing``, ``Record-swift.struct/never`` and /// ``Record-swift.struct/failed`` + @available(*, deprecated, renamed: "RecordMode") public struct Record: Equatable, Sendable { private let storage: Storage @@ -153,6 +157,7 @@ public struct SnapshotTestingConfiguration: Sendable { /// command for opening [Kaleidoscope](https://kaleidoscope.app), and /// ``DiffTool-swift.struct/default`` for simply printing the two URLs to the test failure /// message. + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") public struct DiffTool: Sendable, ExpressibleByStringLiteral { var tool: @Sendable (_ currentFilePath: String, _ failedFilePath: String) -> String @@ -192,31 +197,7 @@ public struct SnapshotTestingConfiguration: Sendable { } } -@available( - iOS, - deprecated: 9999, - message: "Use '.all' instead of 'true', and '.missing' instead of 'false'." -) -@available( - macOS, - deprecated: 9999, - message: "Use '.all' instead of 'true', and '.missing' instead of 'false'." -) -@available( - tvOS, - deprecated: 9999, - message: "Use '.all' instead of 'true', and '.missing' instead of 'false'." -) -@available( - watchOS, - deprecated: 9999, - message: "Use '.all' instead of 'true', and '.missing' instead of 'false'." -) -@available( - visionOS, - deprecated: 9999, - message: "Use '.all' instead of 'true', and '.missing' instead of 'false'." -) +@available(*, deprecated) extension SnapshotTestingConfiguration.Record: ExpressibleByBooleanLiteral { public init(booleanLiteral value: BooleanLiteralType) { self = value ? .all : .missing diff --git a/Sources/SnapshotTesting/SnapshotsTestTrait.swift b/Sources/Deprecated/SnapshotTesting/SnapshotsTestTrait.swift similarity index 81% rename from Sources/SnapshotTesting/SnapshotsTestTrait.swift rename to Sources/Deprecated/SnapshotTesting/SnapshotsTestTrait.swift index 9fa4f9a53..e20a27363 100644 --- a/Sources/SnapshotTesting/SnapshotsTestTrait.swift +++ b/Sources/Deprecated/SnapshotTesting/SnapshotsTestTrait.swift @@ -2,13 +2,16 @@ import Testing /// A type representing the configuration of snapshot testing. + @available(*, deprecated, message: "Migrate to new the SnapshotTesting API") public struct _SnapshotsTestTrait: SuiteTrait, TestTrait { public let isRecursive = true let configuration: SnapshotTestingConfiguration } + @available(*, deprecated) extension Trait where Self == _SnapshotsTestTrait { /// Configure snapshot testing in a suite or test. + @available(*, deprecated, message: "Replace with .record(..) or .diffTool(..)") public static var snapshots: Self { snapshots() } @@ -18,6 +21,7 @@ /// - Parameters: /// - record: The record mode of the test. /// - diffTool: The diff tool to use in failure messages. + @available(*, deprecated, message: "Replace with .record(..) or .diffTool(..)") public static func snapshots( record: SnapshotTestingConfiguration.Record? = nil, diffTool: SnapshotTestingConfiguration.DiffTool? = nil @@ -33,6 +37,7 @@ /// Configure snapshot testing in a suite or test. /// /// - Parameter configuration: The configuration to use. + @available(*, deprecated, message: "Replace with .record(..) or .diffTool(..)") public static func snapshots( _ configuration: SnapshotTestingConfiguration ) -> Self { @@ -41,6 +46,7 @@ } #if compiler(>=6.1) + @available(*, deprecated) extension _SnapshotsTestTrait: TestScoping { public func provideScope( for test: Test, diff --git a/Sources/SnapshotTesting/Snapshotting.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting.swift similarity index 97% rename from Sources/SnapshotTesting/Snapshotting.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting.swift index 4e984f783..43a27a947 100644 --- a/Sources/SnapshotTesting/Snapshotting.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting.swift @@ -3,6 +3,7 @@ import XCTest /// A type representing the ability to transform a snapshottable value into a diffable format (like /// text or an image) for snapshot testing. +@available(*, deprecated, renamed: "Snapshot") public struct Snapshotting { /// The path extension applied to references saved to disk. public var pathExtension: String? @@ -109,9 +110,12 @@ public struct Snapshotting { } /// A snapshot strategy where the type being snapshot is also a diffable type. +@available(*, deprecated, renamed: "IdentitySyncSnapshot") public typealias SimplySnapshotting = Snapshotting +@available(*, deprecated) extension Snapshotting where Value == Format { + public init(pathExtension: String?, diffing: Diffing) { self.init( pathExtension: pathExtension, diff --git a/Sources/SnapshotTesting/Snapshotting/Any.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/Any.swift similarity index 93% rename from Sources/SnapshotTesting/Snapshotting/Any.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/Any.swift index eaa2e3a60..1ee5e2f63 100644 --- a/Sources/SnapshotTesting/Snapshotting/Any.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/Any.swift @@ -1,5 +1,6 @@ import Foundation +@available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Format == String { /// A snapshot strategy that captures a value's textual description from `String`'s /// `init(describing:)` initializer. @@ -18,6 +19,7 @@ extension Snapshotting where Format == String { } } +@available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Format == String { /// A snapshot strategy for comparing any structure based on a sanitized text dump. /// @@ -67,6 +69,7 @@ extension Snapshotting where Format == String { } @available(macOS 10.13, watchOS 4.0, tvOS 11.0, *) +@available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Format == String { /// A snapshot strategy for comparing any structure based on their JSON representation. public static var json: Snapshotting { @@ -86,6 +89,7 @@ extension Snapshotting where Format == String { } } +@available(*, deprecated) private func snap( _ value: T, name: String? = nil, @@ -121,7 +125,7 @@ private func snap( return "\(indentation)- \(name.map { "\($0): " } ?? "")\(value.snapshotDescription)\n" case (let value as CustomStringConvertible, _): description = value.description - case let (value as AnyObject, .class?): + case (let value as AnyObject, .class?): let objectID = ObjectIdentifier(value) if visitedValues.contains(objectID) { return "\(indentation)\(bullet) \(name ?? "value") (circular reference detected)\n" @@ -149,6 +153,7 @@ private func snap( return lines.joined() } +@available(*, deprecated) private func sort(_ children: Mirror.Children, visitedValues: Set) -> Mirror.Children { @@ -164,6 +169,7 @@ private func sort(_ children: Mirror.Children, visitedValues: Set Snapshotting diff --git a/Sources/SnapshotTesting/Snapshotting/SpriteKit.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/SpriteKit.swift similarity index 90% rename from Sources/SnapshotTesting/Snapshotting/SpriteKit.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/SpriteKit.swift index ad515050a..ae827f4f5 100644 --- a/Sources/SnapshotTesting/Snapshotting/SpriteKit.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/SpriteKit.swift @@ -7,6 +7,7 @@ #endif #if os(macOS) + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == SKScene, Format == NSImage { /// A snapshot strategy for comparing SpriteKit scenes based on pixel equality. /// @@ -24,6 +25,7 @@ } } #elseif os(iOS) || os(tvOS) + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == SKScene, Format == UIImage { /// A snapshot strategy for comparing SpriteKit scenes based on pixel equality. /// @@ -42,6 +44,7 @@ } #endif + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == SKScene, Format == Image { fileprivate static func skScene(precision: Float, perceptualPrecision: Float, size: CGSize) -> Snapshotting diff --git a/Sources/SnapshotTesting/Snapshotting/String.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/String.swift similarity index 83% rename from Sources/SnapshotTesting/Snapshotting/String.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/String.swift index 44aeab0b4..ff07d71fd 100644 --- a/Sources/SnapshotTesting/Snapshotting/String.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/String.swift @@ -1,11 +1,13 @@ import Foundation import XCTest +@available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == String, Format == String { /// A snapshot strategy for comparing strings based on equality. public static let lines = Snapshotting(pathExtension: "txt", diffing: .lines) } +@available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Diffing where Value == String { /// A line-diffing strategy for UTF-8 text. public static let lines = Diffing( @@ -14,7 +16,7 @@ extension Diffing where Value == String { ) { old, new in guard old != new else { return nil } let hunks = chunk( - diff: SnapshotTesting.diff( + diff: _SnapshotTesting.diff( old.split(separator: "\n", omittingEmptySubsequences: false).map(String.init), new.split(separator: "\n", omittingEmptySubsequences: false).map(String.init) )) diff --git a/Sources/SnapshotTesting/Snapshotting/SwiftUIView.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/SwiftUIView.swift similarity index 93% rename from Sources/SnapshotTesting/Snapshotting/SwiftUIView.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/SwiftUIView.swift index 8d85e1f0b..2118dd74c 100644 --- a/Sources/SnapshotTesting/Snapshotting/SwiftUIView.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/SwiftUIView.swift @@ -3,6 +3,7 @@ import SwiftUI /// The size constraint for a snapshot (similar to `PreviewLayout`). + @available(*, deprecated, renamed: "LayoutConfiguration") public enum SwiftUISnapshotLayout { #if os(iOS) || os(tvOS) /// Center the view in a device container described by`config`. @@ -16,6 +17,7 @@ #if os(iOS) || os(tvOS) @available(iOS 13.0, tvOS 13.0, *) + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value: SwiftUI.View, Format == UIImage { /// A snapshot strategy for comparing SwiftUI Views based on pixel equality. @@ -49,12 +51,12 @@ switch layout { #if os(iOS) || os(tvOS) - case let .device(config: deviceConfig): + case .device(config: let deviceConfig): config = deviceConfig #endif case .sizeThatFits: config = .init(safeArea: .zero, size: nil, traits: traits) - case let .fixed(width: width, height: height): + case .fixed(let width, let height): let size = CGSize(width: width, height: height) config = .init(safeArea: .zero, size: size, traits: traits) } diff --git a/Sources/SnapshotTesting/Snapshotting/UIBezierPath.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/UIBezierPath.swift similarity index 93% rename from Sources/SnapshotTesting/Snapshotting/UIBezierPath.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/UIBezierPath.swift index 6b48d622d..73cdd18f9 100644 --- a/Sources/SnapshotTesting/Snapshotting/UIBezierPath.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/UIBezierPath.swift @@ -1,6 +1,7 @@ #if os(iOS) || os(tvOS) import UIKit + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == UIBezierPath, Format == UIImage { /// A snapshot strategy for comparing bezier paths based on pixel equality. public static var image: Snapshotting { @@ -38,6 +39,7 @@ } @available(iOS 11.0, tvOS 11.0, *) + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == UIBezierPath, Format == String { /// A snapshot strategy for comparing bezier paths based on pixel equality. public static var elementsDescription: Snapshotting { diff --git a/Sources/SnapshotTesting/Snapshotting/UIImage.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/UIImage.swift similarity index 98% rename from Sources/SnapshotTesting/Snapshotting/UIImage.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/UIImage.swift index 3d1bb5319..e51bc46f6 100644 --- a/Sources/SnapshotTesting/Snapshotting/UIImage.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/UIImage.swift @@ -2,6 +2,7 @@ import UIKit import XCTest + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Diffing where Value == UIImage { /// A pixel-diffing strategy for UIImage's which requires a 100% match. public static let image = Diffing.image() @@ -35,7 +36,7 @@ let message = compare( old, new, precision: precision, perceptualPrecision: perceptualPrecision) else { return nil } - let difference = SnapshotTesting.diff(old, new) + let difference = _SnapshotTesting.diff(old, new) let oldAttachment = XCTAttachment(image: old) oldAttachment.name = "reference" let isEmptyImage = new.size == .zero @@ -62,6 +63,7 @@ } } + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == UIImage, Format == UIImage { /// A snapshot strategy for comparing images based on pixel equality. public static var image: Snapshotting { diff --git a/Sources/SnapshotTesting/Snapshotting/UIView.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/UIView.swift similarity index 95% rename from Sources/SnapshotTesting/Snapshotting/UIView.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/UIView.swift index 7244f67d1..f43c1d58e 100644 --- a/Sources/SnapshotTesting/Snapshotting/UIView.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/UIView.swift @@ -1,6 +1,7 @@ #if os(iOS) || os(tvOS) import UIKit + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == UIView, Format == UIImage { /// A snapshot strategy for comparing views based on pixel equality. public static var image: Snapshotting { @@ -44,6 +45,7 @@ } } + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == UIView, Format == String { /// A snapshot strategy for comparing views based on a recursive description of their properties /// and hierarchies. diff --git a/Sources/SnapshotTesting/Snapshotting/UIViewController.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/UIViewController.swift similarity index 97% rename from Sources/SnapshotTesting/Snapshotting/UIViewController.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/UIViewController.swift index 7b86e51aa..c276679c0 100644 --- a/Sources/SnapshotTesting/Snapshotting/UIViewController.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/UIViewController.swift @@ -1,6 +1,7 @@ #if os(iOS) || os(tvOS) import UIKit + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == UIViewController, Format == UIImage { /// A snapshot strategy for comparing view controller views based on pixel equality. public static var image: Snapshotting { @@ -83,6 +84,7 @@ } } + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == UIViewController, Format == String { /// A snapshot strategy for comparing view controllers based on their embedded controller /// hierarchy. diff --git a/Sources/SnapshotTesting/Snapshotting/URLRequest.swift b/Sources/Deprecated/SnapshotTesting/Snapshotting/URLRequest.swift similarity index 98% rename from Sources/SnapshotTesting/Snapshotting/URLRequest.swift rename to Sources/Deprecated/SnapshotTesting/Snapshotting/URLRequest.swift index c2699405f..dfcbc31d1 100644 --- a/Sources/SnapshotTesting/Snapshotting/URLRequest.swift +++ b/Sources/Deprecated/SnapshotTesting/Snapshotting/URLRequest.swift @@ -5,6 +5,7 @@ import FoundationNetworking #endif + @available(*, deprecated, message: "Migrate to the new SnapshotTesting API") extension Snapshotting where Value == URLRequest, Format == String { /// A snapshot strategy for comparing requests based on raw equality. /// diff --git a/Sources/Deprecated/SnapshotTestingCustomDump/CustomDump.swift b/Sources/Deprecated/SnapshotTestingCustomDump/CustomDump.swift new file mode 100644 index 000000000..02acb16f8 --- /dev/null +++ b/Sources/Deprecated/SnapshotTestingCustomDump/CustomDump.swift @@ -0,0 +1,25 @@ +import CustomDump +import _SnapshotTesting + +@available(*, deprecated, message: "Migrate to the new SnapshotTesting API") +extension Snapshotting where Format == String { + /// A snapshot strategy for comparing any structure based on a + /// [custom dump](https://github.com/pointfreeco/swift-custom-dump). + /// + /// ```swift + /// assertSnapshot(of: user, as: .customDump) + /// ``` + /// + /// Records: + /// + /// ``` + /// User( + /// bio: "Blobbed around the world.", + /// id: 1, + /// name: "Blobby" + /// ) + /// ``` + public static var customDump: Snapshotting { + SimplySnapshotting.lines.pullback(String.init(customDumping:)) + } +} diff --git a/Sources/InlineSnapshotTesting/AssertInline.swift b/Sources/InlineSnapshotTesting/AssertInline.swift new file mode 100644 index 000000000..32ae03a26 --- /dev/null +++ b/Sources/InlineSnapshotTesting/AssertInline.swift @@ -0,0 +1,216 @@ +import Foundation +@_spi(Internals) import XCSnapshotTesting + +#if canImport(SwiftSyntax601) +@_spi(Internals) import XCSnapshotTesting +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Asserts that a given value matches an inline string snapshot using the specified snapshot testing strategy. +/// +/// This function compares the output of a value—evaluated lazily—with an inline snapshot string, which is stored directly in your test source code. +/// If the output does not match the inline snapshot, the test will fail and optionally provide a descriptive message. +/// You can optionally record new snapshots, customize serialization, and specify the snapshot comparison strategy. +/// +/// - Parameters: +/// - value: A closure that returns the value to compare against the snapshot. This is evaluated only when the assertion runs. +/// - snapshot: The snapshot testing strategy to use for serialization and comparison. +/// - message: An optional closure that returns a description for test results. Defaults to an empty string. +/// - record: An optional mode indicating whether to record a new reference snapshot. If `nil`, recording is determined automatically. +/// - timeout: The number of seconds to wait for the snapshot operation to complete. Defaults to 5. +/// - name: An optional name to distinguish this snapshot from others in the same test. +/// - serialization: The strategy used to serialize the snapshot data. Defaults to `DataSerialization()`. +/// - closureDescriptor: An optional descriptor describing the inline snapshot’s location. Typically not needed unless implementing custom helpers. +/// - expected: An optional closure that returns a previously generated snapshot value. When omitted, the expected value will be populated inline at the call site. +/// - isolation: Optionally specify an actor for input evaluation, supporting thread/actor isolation. Defaults to current context. +/// - fileID: The file ID in which the assertion was called. Defaults to the file ID of the test case. +/// - filePath: The file path in which the assertion was called. Defaults to the file path of the test case. +/// - function: The function name in which the assertion was called. Defaults to the test method name. +/// - line: The line number on which the assertion was called. Defaults to the line number of the call site. +/// - column: The column on which the assertion was called. Defaults to the column number of the call site. +/// - Throws: Rethrows any error thrown by the value provider or snapshot strategy. +/// - Important: When using the Swift Testing framework, you must explicitly set the @Suite(.finalizeSnapshots) trait to ensure inline snapshots are written correctly. +/// - SeeAlso: +public func assertInline( + of value: @autoclosure @Sendable () async throws -> Input, + as snapshot: AsyncSnapshot, + message: @autoclosure @escaping @Sendable () -> String = "", + record: RecordMode? = nil, + timeout: TimeInterval = 5, + name: String? = nil, + serialization: DataSerialization = DataSerialization(), + closureDescriptor: SnapshotClosureDescriptor = SnapshotClosureDescriptor(), + matches expected: (@Sendable () -> Output.RawValue)? = nil, + isolation: isolated Actor? = #isolation, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) async throws { + let engine = SnapshotInlineEngine>( + expected: expected, + message: message, + closureDescriptor: closureDescriptor + ) + + let tester = SnapshotTester( + engine: engine, + record: record, + timeout: timeout, + name: name, + serialization: serialization, + fileID: fileID, + filePath: filePath, + function: function, + line: line, + column: column + ) + + guard let failure = try await tester(value(), for: snapshot) else { + return + } + + switch failure.reason { + case .doesNotMatch: + try closureDescriptor.fail( + failure.message, + fileID: fileID, + file: filePath, + line: line, + column: column + ) + default: + try TestingSystem.shared.record( + message: failure.message, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + } +} + +/// Asserts that a value matches an inline string snapshot using a snapshot testing strategy. +/// +/// This function compares the output of a value—evaluated lazily—with an inline snapshot string +/// stored directly in your test source code. If the output does not match the inline snapshot, +/// the test will fail and optionally provide a descriptive message. You can optionally record new +/// snapshots, customize serialization, and specify the snapshot comparison strategy. +/// +/// - Parameters: +/// - value: A closure that returns the value to compare against the snapshot. This is evaluated only when the assertion runs. +/// - snapshot: The snapshot testing strategy to use for serialization and comparison. +/// - message: An optional closure that returns a description for test results. Defaults to an empty string. +/// - record: An optional mode indicating whether to record a new reference snapshot. If `nil`, recording is determined automatically. +/// - timeout: The number of seconds to wait for the snapshot operation to complete. Defaults to 5. +/// - name: An optional name to distinguish this snapshot from others in the same test. +/// - serialization: The strategy used to serialize the snapshot data. Defaults to `DataSerialization()`. +/// - closureDescriptor: An optional descriptor for the inline snapshot’s location. Typically not needed unless implementing custom helpers. +/// - expected: An optional closure that returns a previously generated snapshot value. When omitted, the expected value will be populated inline at the call site. +/// - fileID: The file ID in which the assertion was called. Defaults to the file ID of the test case. +/// - filePath: The file path in which the assertion was called. Defaults to the file path of the test case. +/// - function: The function name in which the assertion was called. Defaults to the test method name. +/// - line: The line number on which the assertion was called. Defaults to the line number of the call site. +/// - column: The column on which the assertion was called. Defaults to the column number of the call site. +/// - Throws: Rethrows any error thrown by the value provider or snapshot strategy. +/// - Important: When using the Swift Testing framework, you must explicitly set the @Suite(.finalizeSnapshots) trait to ensure inline snapshots are written correctly. +/// - SeeAlso: +public func assertInline( + of value: @autoclosure @Sendable () throws -> Input, + as snapshot: SyncSnapshot, + message: @autoclosure @escaping @Sendable () -> String = "", + record: RecordMode? = nil, + timeout: TimeInterval = 5, + name: String? = nil, + serialization: DataSerialization = DataSerialization(), + closureDescriptor: SnapshotClosureDescriptor = SnapshotClosureDescriptor(), + matches expected: (@Sendable () -> Output.RawValue)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) throws { + let engine = SnapshotInlineEngine>( + expected: expected, + message: message, + closureDescriptor: closureDescriptor + ) + + let tester = SnapshotTester( + engine: engine, + record: record, + timeout: timeout, + name: name, + serialization: serialization, + fileID: fileID, + filePath: filePath, + function: function, + line: line, + column: column + ) + + guard let failure = try tester(value(), for: snapshot) else { + return + } + + switch failure.reason { + case .doesNotMatch: + try closureDescriptor.fail( + failure.message, + fileID: fileID, + file: filePath, + line: line, + column: column + ) + default: + try TestingSystem.shared.record( + message: failure.message, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + } +} +#else +@available(*, unavailable, message: "'assertInline' requires 'swift-syntax' >= 509.0.0") +public func assertInline( + of value: @autoclosure @Sendable () throws -> Input, + as snapshot: AsyncSnapshot, + message: @autoclosure @escaping @Sendable () -> String = "", + record: RecordMode? = nil, + timeout: TimeInterval = 5, + serialization: DataSerialization = DataSerialization(), + closureDescriptor: SnapshotClosureDescriptor = SnapshotClosureDescriptor(), + matches expected: (@Sendable () -> Output.RawValue)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) async throws { + fatalError() +} + +@available(*, unavailable, message: "'assertInline' requires 'swift-syntax' >= 509.0.0") +public func assertInline( + of value: @autoclosure @Sendable () throws -> Input, + as snapshot: SyncSnapshot, + message: @autoclosure @escaping @Sendable () -> String = "", + record: RecordMode? = nil, + timeout: TimeInterval = 5, + serialization: DataSerialization = DataSerialization(), + closureDescriptor: SnapshotClosureDescriptor = SnapshotClosureDescriptor(), + matches expected: (@Sendable () -> Output.RawValue)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) throws { + fatalError() +} +#endif diff --git a/Sources/InlineSnapshotTesting/Exports.swift b/Sources/InlineSnapshotTesting/Exports.swift index 44c34dda7..877ad117c 100644 --- a/Sources/InlineSnapshotTesting/Exports.swift +++ b/Sources/InlineSnapshotTesting/Exports.swift @@ -1 +1,5 @@ -@_exported import SnapshotTesting +@_exported import XCSnapshotTesting + +#if !os(visionOS) +@_exported import _InlineSnapshotTesting +#endif diff --git a/Sources/InlineSnapshotTesting/Extensions/String+.swift b/Sources/InlineSnapshotTesting/Extensions/String+.swift new file mode 100644 index 000000000..1d487e579 --- /dev/null +++ b/Sources/InlineSnapshotTesting/Extensions/String+.swift @@ -0,0 +1,30 @@ +import Foundation + +extension String { + + func indenting(by count: Int) -> String { + self.indenting(with: String(repeating: " ", count: count)) + } + + func indenting(with prefix: String) -> String { + guard !prefix.isEmpty else { return self } + return self.replacingOccurrences( + of: #"([^\n]+)"#, + with: "\(prefix)$1", + options: .regularExpression + ) + } + + func hashCount(isMultiline: Bool) -> Int { + let (quote, offset) = isMultiline ? ("\"\"\"", 2) : ("\"", 0) + var substring = self[...] + var hashCount = self.contains(#"\"#) ? 1 : 0 + let pattern = "(\(quote)[#]*)" + while let range = substring.range(of: pattern, options: .regularExpression) { + let count = substring.distance(from: range.lowerBound, to: range.upperBound) - offset + hashCount = max(count, hashCount) + substring = substring[range.upperBound...] + } + return hashCount + } +} diff --git a/Sources/InlineSnapshotTesting/SnapshotInlineEngine.swift b/Sources/InlineSnapshotTesting/SnapshotInlineEngine.swift new file mode 100644 index 000000000..b9916b78e --- /dev/null +++ b/Sources/InlineSnapshotTesting/SnapshotInlineEngine.swift @@ -0,0 +1,183 @@ +#if canImport(SwiftSyntax601) +import SwiftSyntax +import Foundation +@_spi(Internals) import XCSnapshotTesting + +struct SnapshotInlineEngine: SnapshotEngine where Executor.Output: BytesRepresentable { + + let expected: (@Sendable () -> Executor.Output.RawValue)? + let message: @Sendable () -> String + let closureDescriptor: SnapshotClosureDescriptor + + init( + expected: (@Sendable () -> Executor.Output.RawValue)?, + message: @Sendable @escaping () -> String, + closureDescriptor: SnapshotClosureDescriptor + ) { + SnapshotInlineObservation.shared.registerIfNeeded() + if TestingSystem.shared.isSwiftTestingRunning { + precondition( + TestingSystem.shared.isSwiftTestingCompletionAttached, + "To run InlineSnapshotTesting on Swift Testing, you need to add @Suite(.finalizeSnapshots)" + ) + } + self.expected = expected + self.message = message + self.closureDescriptor = closureDescriptor + } + + func sourceURL( + for filePath: StaticString, + using tester: SnapshotTester> + ) throws -> URL { + try InlineSnapshotManager.current.registerTestSource(.init(path: filePath)) + + return URL( + fileURLWithPath: String(describing: filePath), + isDirectory: false + ) + } + + func temporaryURL( + for filePath: StaticString, + using tester: SnapshotTester> + ) throws -> URL? { + nil + } + + func contentExists( + at url: URL + ) -> Bool { + expected != nil || InlineSnapshotManager.current.recordExists(at: url) + } + + func loadSnapshot( + from url: URL, + using tester: SnapshotTester> + ) throws -> Executor.Output { + if InlineSnapshotManager.current.recordExists(at: url) { + let snapshot = try InlineSnapshotManager.current.record(at: url) + return try tester.serialization.deserialize(Executor.Output.self, from: snapshot.diffable) + } else if let expected { + return Executor.Output(rawValue: expected()) + } else { + throw URLError(.fileDoesNotExist) + } + } + + func perform( + _ operation: SnapshotPerformOperation, + contents: Data, + to url: URL, + using tester: SnapshotTester> + ) throws { + InlineSnapshotManager.current.write( + InlineSnapshot( + reference: (try? InlineSnapshotManager.current.record(at: url))?.diffable, + diffable: contents, + wasRecording: operation == .write, + closureDescriptor: closureDescriptor, + function: String(describing: tester.function), + line: tester.line, + column: tester.column + ), + to: url + ) + } + + func generateFailureMessage( + for context: SnapshotFailContext, + using tester: SnapshotTester> + ) -> String { + switch context.reason { + case .missing: + return missing(context) + case .doesNotMatch: + return doesNotMatch(context) + case .allRecordMode: + return allRecordMode(context) + case .timeout: + return timeout(context, timeout: tester.timeout) + } + } +} + +extension SnapshotInlineEngine { + + fileprivate func missing(_ context: SnapshotFailContext) -> String { + let name = String(describing: context.function) + + guard context.didWriteNewSnapshot else { + return + "No reference was found on disk. New snapshot was not recorded because recording is disabled" + } + + let failure: String + if closureDescriptor.trailingClosureLabel + == SnapshotClosureDescriptor.defaultTrailingClosureLabel + { + failure = "Automatically recorded a new snapshot." + } else { + failure = """ + Automatically recorded a new snapshot for "\(closureDescriptor.trailingClosureLabel)". + """ + } + + return """ + No reference was found on disk. \(failure): + + Re-run "\(name)" to assert against the newly-recorded snapshot. + """ + } + + fileprivate func doesNotMatch(_ context: SnapshotFailContext) -> String { + var message: String = message() + + if message.isEmpty { + message += "Snapshot does not match reference. Difference: …" + } + + if let additionalInformation = context.additionalInformation { + message += "\n\n" + additionalInformation.indenting(by: 2) + } + + if context.didWriteNewSnapshot { + message += "\n\nA new snapshot was automatically recorded." + } + + return message + } + + fileprivate func allRecordMode(_ context: SnapshotFailContext) -> String { + let name = String(describing: context.function) + + let failure: String + + if closureDescriptor.trailingClosureLabel + == SnapshotClosureDescriptor.defaultTrailingClosureLabel + { + failure = "Automatically recorded a new snapshot." + } else { + failure = """ + Automatically recorded a new snapshot for "\(closureDescriptor.trailingClosureLabel)". + """ + } + + return """ + \(failure) + + Turn record mode off and re-run "\(name)" to assert against the newly-recorded snapshot + """ + } + + fileprivate func timeout(_ context: SnapshotFailContext, timeout: TimeInterval) -> String { + """ + Exceeded timeout of \(timeout) seconds waiting for snapshot. + + This can happen when an asynchronously loaded value (like a network response) has not \ + loaded. If a timeout is unavoidable, consider setting the "timeout" parameter of + "assertInline" to a higher value. + """ + } +} +#endif diff --git a/Sources/InlineSnapshotTesting/SnapshotInlineObservation.swift b/Sources/InlineSnapshotTesting/SnapshotInlineObservation.swift new file mode 100644 index 000000000..bc3509884 --- /dev/null +++ b/Sources/InlineSnapshotTesting/SnapshotInlineObservation.swift @@ -0,0 +1,49 @@ +import Foundation +@_spi(Internals) import XCSnapshotTesting + +final class SnapshotInlineObservation: @unchecked Sendable { + + static let shared = SnapshotInlineObservation() + + private let lock = NSLock() + + private var _isObserving = false + private var _xcObserver: NSObjectProtocol? + private var _swiftTestingObserver: NSObjectProtocol? + + func registerIfNeeded() { + lock.withLock { + guard !_isObserving else { + return + } + + _isObserving = true + + _xcObserver = NotificationCenter.default.addObserver( + forName: XCTestBundleDidFinishNotification, + object: nil, + queue: .current, + using: { [weak self] in self?.xcTestDidFinish($0) } + ) + + _swiftTestingObserver = NotificationCenter.default.addObserver( + forName: SwiftTestingDidFinishNotification, + object: nil, + queue: .current, + using: { [weak self] in self?.swiftTestDidFinish($0) } + ) + } + } + + private func xcTestDidFinish(_ notification: Notification) { + InlineSnapshotManager.current.writeInlineSnapshots() + } + + private func swiftTestDidFinish(_ notification: Notification) { + guard let testName = notification.userInfo?[kTestFileName] as? String else { + return + } + + InlineSnapshotManager.current.writeInlineSnapshots(for: testName) + } +} diff --git a/Sources/InlineSnapshotTesting/SnapshotURL.swift b/Sources/InlineSnapshotTesting/SnapshotURL.swift new file mode 100644 index 000000000..9355d055b --- /dev/null +++ b/Sources/InlineSnapshotTesting/SnapshotURL.swift @@ -0,0 +1,16 @@ +struct SnapshotURL: Sendable, Hashable { + + let path: StaticString + + init(path: StaticString) { + self.path = path + } + + static func == (lhs: Self, rhs: Self) -> Bool { + String(describing: lhs.path) == String(describing: rhs.path) + } + + func hash(into hasher: inout Hasher) { + hasher.combine(String(describing: path)) + } +} diff --git a/Sources/InlineSnapshotTesting/SwiftSyntax/InlineSnapshot.swift b/Sources/InlineSnapshotTesting/SwiftSyntax/InlineSnapshot.swift new file mode 100644 index 000000000..f6b70f868 --- /dev/null +++ b/Sources/InlineSnapshotTesting/SwiftSyntax/InlineSnapshot.swift @@ -0,0 +1,33 @@ +#if canImport(SwiftSyntax601) +import SwiftSyntax +import Foundation + +public struct InlineSnapshot: Sendable, Hashable { + + public let reference: Data? + public let diffable: Data + public let wasRecording: Bool + public let closureDescriptor: SnapshotClosureDescriptor + public let function: String + public let line: UInt + public let column: UInt + + public init( + reference: Data?, + diffable: Data, + wasRecording: Bool, + closureDescriptor: SnapshotClosureDescriptor, + function: String, + line: UInt, + column: UInt + ) { + self.reference = reference + self.diffable = diffable + self.wasRecording = wasRecording + self.closureDescriptor = closureDescriptor + self.function = function + self.line = line + self.column = column + } +} +#endif diff --git a/Sources/InlineSnapshotTesting/SwiftSyntax/InlineSnapshotManager.swift b/Sources/InlineSnapshotTesting/SwiftSyntax/InlineSnapshotManager.swift new file mode 100644 index 000000000..7cbedcad9 --- /dev/null +++ b/Sources/InlineSnapshotTesting/SwiftSyntax/InlineSnapshotManager.swift @@ -0,0 +1,203 @@ +#if canImport(SwiftSyntax601) +import Foundation +import SwiftSyntax +import SwiftParser + +final class InlineSnapshotManager: @unchecked Sendable { + + static var current: InlineSnapshotManager { + local ?? shared + } + + @TaskLocal + fileprivate static var local: InlineSnapshotManager? + + private static let shared = InlineSnapshotManager() + + private let lock = NSLock() + + private var _snapshots: [URL: InlineSnapshot] = [:] + private var _testSourceCache: [SnapshotURL: TestSource] = [:] + + fileprivate init() {} + + subscript(_ url: SnapshotURL) -> TestSource? { + lock.withLock { + _testSourceCache[url] + } + } + + func registerTestSource(_ url: SnapshotURL) throws { + try lock.withLock { + if _testSourceCache[url] != nil { + return + } + + let path = String(describing: url.path) + + let source = try String(contentsOfFile: path) + + let sourceFile = Parser.parse(source: source) + + let sourceLocationConverter = SourceLocationConverter( + fileName: path, + tree: sourceFile + ) + + let testSource = TestSource( + source: source, + sourceFile: sourceFile, + sourceLocationConverter: sourceLocationConverter + ) + + _testSourceCache[url] = testSource + } + } + + func write(_ snapshot: InlineSnapshot, to url: URL) { + lock.withLock { + _snapshots[url] = snapshot + } + } + + func record(at url: URL) throws -> InlineSnapshot { + try lock.withLock { + guard let snapshot = _snapshots[url] else { + throw URLError(.fileDoesNotExist) + } + + return snapshot + } + } + + func recordExists( + at url: URL + ) -> Bool { + lock.withLock { + _snapshots[url] != nil + } + } + + func writeInlineSnapshots() { + lock.withLock { + while let (url, testSource) = _testSourceCache.popFirst() { + _writeInlineSnapshots( + _records(for: url.path), + at: url, + testSource: testSource + ) + } + } + } + + func writeInlineSnapshots(for testName: String) { + lock.withLock { + for (snapshotURL, testSource) in _testSourceCache { + let url = URL( + fileURLWithPath: String(describing: snapshotURL.path) + ) + + guard url.lastPathComponent == testName else { + continue + } + + _testSourceCache[snapshotURL] = nil + + return _writeInlineSnapshots( + _records(for: snapshotURL.path), + at: snapshotURL, + testSource: testSource + ) + } + } + } + + func records(for filePath: StaticString) -> [InlineSnapshot] { + lock.withLock { + _records(for: filePath) + } + } + + // MARK: - Unsafe methods + + private func _records(for filePath: StaticString) -> [InlineSnapshot] { + let url = URL(fileURLWithPath: String(describing: filePath)) + + var records = [InlineSnapshot]() + for (snapshotURL, snapshot) in _snapshots + where snapshotURL.absoluteString.starts(with: url.absoluteString) { + records.append(snapshot) + } + return records + } + + private func _writeInlineSnapshots( + _ snapshots: [InlineSnapshot], + at url: SnapshotURL, + testSource: TestSource + ) { + for snapshot in snapshots { + let line = snapshot.line + + let snapshotRewriter = SnapshotRewriter( + file: url, + snapshots: snapshots.sorted { + $0.line != $1.line + ? $0.line < $1.line + : $0.closureDescriptor.trailingClosureOffset + < $1.closureDescriptor.trailingClosureOffset + }, + sourceLocationConverter: testSource.sourceLocationConverter + ) + + let updatedSource = snapshotRewriter.visit(testSource.sourceFile).description + + if testSource.source != updatedSource { + do { + try updatedSource.write( + toFile: String(describing: url.path), + atomically: true, + encoding: .utf8 + ) + } catch { + fatalError("Threw error: \(error)", file: url.path, line: line) + } + } + } + } + + deinit { + writeInlineSnapshots() + } +} + +@_spi(Internals) +public func withInlineSnapshotManager( + _ operation: () async throws -> R, + isolation: isolated Actor? = #isolation, + file: String = #file, + line: UInt = #line +) async rethrows -> R { + try await InlineSnapshotManager.$local.withValue( + InlineSnapshotManager(), + operation: operation, + isolation: isolation, + file: file, + line: line + ) +} + +@_spi(Internals) +public func withInlineSnapshotManager( + _ operation: () throws -> R, + file: String = #file, + line: UInt = #line +) rethrows -> R { + try InlineSnapshotManager.$local.withValue( + InlineSnapshotManager(), + operation: operation, + file: file, + line: line + ) +} +#endif diff --git a/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotClosureDescriptor.swift b/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotClosureDescriptor.swift new file mode 100644 index 000000000..a05091d33 --- /dev/null +++ b/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotClosureDescriptor.swift @@ -0,0 +1,140 @@ +@_spi(Internals) import XCSnapshotTesting + +/// A structure that describes the location of an inline snapshot. +/// +/// Provide this structure when defining custom snapshot functions that call +/// ``InlineSnapshotTesting/assertInline(of:as:message:record:timeout:name:serialization:closureDescriptor:matches:fileID:file:function:line:column:)`` +/// under the hood. +/// A descriptor for the trailing closure used to supply an inline snapshot in a custom snapshot assertion. +/// +/// Use this type when implementing custom snapshot assertion utilities that ultimately delegate to +/// ``InlineSnapshotTesting/assertInline(of:as:message:record:timeout:name:serialization:closureDescriptor:matches:fileID:file:function:line:column:)``. +/// It describes the structure of the inline snapshot trailing closure, supporting both current and deprecated closure labels, +/// as well as more advanced cases where multiple trailing closures may exist. +/// +/// This type enables tools and assertion infrastructure to accurately associate test failures or snapshot updates +/// with the correct closure in the source code, even in the presence of multiple or labeled trailing closures. +/// +/// For example: +/// - A function with a single trailing closure for the snapshot uses the default offset (0) and label ("matches"). +/// - A function with an additional preceding trailing closure requires a higher offset and possibly a custom label. +/// +/// Deprecated closure labels are supported for migration scenarios, allowing detection and management of legacy call sites. +/// +/// The type is `Sendable` and `Hashable`, making it suitable for concurrent and collection-based use. +public struct SnapshotClosureDescriptor: Sendable, Hashable { + + /// The default label describing an inline snapshot. + public static let defaultTrailingClosureLabel = "matches" + + /// A list of trailing closure labels from deprecated interfaces. + /// + /// Useful for providing migration paths for custom snapshot functions. + public var deprecatedTrailingClosureLabels: [String] + + /// The label of the trailing closure that returns the inline snapshot. + public var trailingClosureLabel: String + + /// The offset of the trailing closure that returns the inline snapshot, relative to the first + /// trailing closure. + /// + /// For example, a helper function with a few parameters and a single trailing closure has a + /// trailing closure offset of 0: + /// + /// ```swift + /// customInlineSnapshot(of: value, "Should match") { + /// // Inline snapshot... + /// } + /// ``` + /// + /// While a helper function with a trailing closure preceding the snapshot closure has an offset + /// of 1: + /// + /// ```swift + /// customInlineSnapshot("Should match") { + /// // Some other parameter... + /// } matches: { + /// // Inline snapshot... + /// } + /// ``` + public var trailingClosureOffset: Int + + /// Initializes an inline snapshot syntax descriptor. + /// + /// - Parameters: + /// - deprecatedTrailingClosureLabels: An array of deprecated labels to consider for the inline + /// snapshot. + /// - trailingClosureLabel: The label of the trailing closure that returns the inline snapshot. + /// - trailingClosureOffset: The offset of the trailing closure that returns the inline + /// snapshot, relative to the first trailing closure. + public init( + deprecatedTrailingClosureLabels: [String] = [], + trailingClosureLabel: String = Self.defaultTrailingClosureLabel, + trailingClosureOffset: Int = 0 + ) { + self.deprecatedTrailingClosureLabels = deprecatedTrailingClosureLabels + self.trailingClosureLabel = trailingClosureLabel + self.trailingClosureOffset = trailingClosureOffset + } + + #if canImport(SwiftSyntax601) + /// Generates a test failure immediately and unconditionally at the described trailing closure. + /// + /// This method will attempt to locate the line of the trailing closure described by this type + /// and call `XCTFail` with it. If the trailing closure cannot be located, the failure will be + /// associated with the given line, instead. + /// + /// - Parameters: + /// - message: An optional description of the assertion, for inclusion in test results. + /// - fileID: The file ID in which failure occurred. Defaults to the file ID of the test case + /// in which this function was called. + /// - filePath: The file in which failure occurred. Defaults to the file path of the test case in + /// which this function was called. + /// - line: The line number on which failure occurred. Defaults to the line number on which + /// this function was called. + /// - column: The column on which failure occurred. Defaults to the column on which this + /// function was called. + public func fail( + _ message: @autoclosure () -> String, + fileID: StaticString, + file filePath: StaticString, + line: UInt, + column: UInt + ) throws { + var trailingClosureLine: Int? + if let testSource = InlineSnapshotManager.current[SnapshotURL(path: filePath)] { + let visitor = SnapshotVisitor( + functionCallLine: Int(line), + functionCallColumn: Int(column), + sourceLocationConverter: testSource.sourceLocationConverter, + closureDescriptor: self + ) + visitor.walk(testSource.sourceFile) + trailingClosureLine = visitor.trailingClosureLine + } + + try TestingSystem.shared.record( + message: message(), + fileID: fileID, + filePath: filePath, + line: trailingClosureLine.map(UInt.init) ?? line, + column: column + ) + } + + func contains(_ label: String) -> Bool { + self.trailingClosureLabel == label || self.deprecatedTrailingClosureLabels.contains(label) + } + #else + @available(*, unavailable, message: "'assertInline' requires 'swift-syntax' >= 509.0.0") + public func fail( + _ message: @autoclosure () -> String = "", + fileID: StaticString, + file filePath: StaticString, + line: UInt, + column: UInt + ) { + fatalError() + } + #endif +} diff --git a/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotRewriter.swift b/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotRewriter.swift new file mode 100644 index 000000000..3342f516c --- /dev/null +++ b/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotRewriter.swift @@ -0,0 +1,238 @@ +#if canImport(SwiftSyntax601) +import SwiftSyntax + +final class SnapshotRewriter: SyntaxRewriter { + + let file: SnapshotURL + var function: String? + let indent: String + let line: UInt? + var newRecordings: [(snapshot: InlineSnapshot, line: UInt)] = [] + var snapshots: [InlineSnapshot] + let sourceLocationConverter: SourceLocationConverter + let wasRecording: Bool + + init( + file: SnapshotURL, + snapshots: [InlineSnapshot], + sourceLocationConverter: SourceLocationConverter + ) { + self.file = file + self.line = snapshots.first?.line + self.wasRecording = snapshots.contains(where: \.wasRecording) + self.indent = String( + sourceLocationConverter.sourceLines + .first { $0.first?.isWhitespace == true && $0.contains { !$0.isWhitespace } }? + .prefix { $0.isWhitespace } + ?? " " + ) + self.snapshots = snapshots + self.sourceLocationConverter = sourceLocationConverter + } + + override func visit(_ functionCallExpr: FunctionCallExprSyntax) -> ExprSyntax { + let location = functionCallExpr.calledExpression + .endLocation(converter: self.sourceLocationConverter, afterTrailingTrivia: true) + let snapshots = self.snapshots.prefix { snapshot in + Int(snapshot.line) == location.line && Int(snapshot.column) == location.column + } + + guard !snapshots.isEmpty + else { return super.visit(functionCallExpr) } + + defer { self.snapshots.removeFirst(snapshots.count) } + + var functionCallExpr = functionCallExpr + for snapshot in snapshots { + guard snapshot.reference != snapshot.diffable, snapshot.wasRecording else { continue } + + let diffable = String(data: snapshot.diffable, encoding: .utf8) + + self.function = + self.function + ?? functionCallExpr.calledExpression.as(DeclReferenceExprSyntax.self)?.baseName.text + + let leadingTrivia = String( + self.sourceLocationConverter.sourceLines[Int(snapshot.line) - 1] + .prefix(while: { $0 == " " || $0 == "\t" }) + ) + let delimiter = String( + repeating: "#", + count: (diffable ?? "").hashCount(isMultiline: true) + ) + let leadingIndent = leadingTrivia + self.indent + let snapshotLabel = TokenSyntax( + stringLiteral: snapshot.closureDescriptor.trailingClosureLabel + ) + let snapshotClosure = diffable.map { actual in + ClosureExprSyntax( + leftBrace: .leftBraceToken(trailingTrivia: .newline), + statements: CodeBlockItemListSyntax { + StringLiteralExprSyntax( + leadingTrivia: Trivia(stringLiteral: leadingIndent), + openingPounds: .rawStringPoundDelimiter(delimiter), + openingQuote: .multilineStringQuoteToken(trailingTrivia: .newline), + segments: [ + .stringSegment( + StringSegmentSyntax( + content: .stringSegment( + actual + .replacingOccurrences(of: "\r", with: #"\\#(delimiter)r"#) + .indenting(with: leadingIndent) + ) + ) + ) + ], + closingQuote: .multilineStringQuoteToken( + leadingTrivia: .newline + Trivia(stringLiteral: leadingIndent) + ), + closingPounds: .rawStringPoundDelimiter(delimiter) + ) + }, + rightBrace: .rightBraceToken( + leadingTrivia: .newline + Trivia(stringLiteral: leadingTrivia) + ) + ) + } + + let arguments = functionCallExpr.arguments + let firstTrailingClosureOffset = + arguments + .enumerated() + .reversed() + .prefix(while: { $0.element.expression.is(ClosureExprSyntax.self) }) + .last? + .offset ?? arguments.count + + let trailingClosureOffset = + firstTrailingClosureOffset + + snapshot.closureDescriptor.trailingClosureOffset + + let centeredTrailingClosureOffset = trailingClosureOffset - arguments.count + + switch centeredTrailingClosureOffset { + case ..<0: + let index = arguments.index(arguments.startIndex, offsetBy: trailingClosureOffset) + if let snapshotClosure { + functionCallExpr.arguments[index].label = snapshotLabel + functionCallExpr.arguments[index].expression = ExprSyntax(snapshotClosure) + } else { + functionCallExpr.arguments.remove(at: index) + } + + case 0: + functionCallExpr.rightParen?.trailingTrivia = .space + let trailingClosureTrivia = functionCallExpr.trailingClosure?.trailingTrivia + if let snapshotClosure { + // FIXME: ?? multipleTrailingClosures.removeFirst() + functionCallExpr.trailingClosure = + if let trailingClosureTrivia, trailingClosureTrivia.count > 0 { + snapshotClosure.with( + \.trailingTrivia, + snapshotClosure.trailingTrivia + trailingClosureTrivia + ) + } else { + snapshotClosure + } + } else if !functionCallExpr.additionalTrailingClosures.isEmpty { + let additionalTrailingClosure = functionCallExpr.additionalTrailingClosures.remove( + at: functionCallExpr.additionalTrailingClosures.startIndex + ) + functionCallExpr.trailingClosure = + if let trailingClosureTrivia, trailingClosureTrivia.count > 0 { + additionalTrailingClosure.closure.with( + \.trailingTrivia, + additionalTrailingClosure.closure.trailingTrivia + trailingClosureTrivia + ) + } else { + additionalTrailingClosure.closure + } + } else { + functionCallExpr.rightParen?.trailingTrivia = "" + functionCallExpr.trailingClosure = nil + } + + case 1...: + var newElement: MultipleTrailingClosureElementSyntax? { + snapshotClosure.map { snapshotClosure in + MultipleTrailingClosureElementSyntax( + label: snapshotLabel, + closure: snapshotClosure.with( + \.leadingTrivia, + snapshotClosure.leadingTrivia + .space + ) + ) + } + } + + if !functionCallExpr.additionalTrailingClosures.isEmpty, + let endIndex = functionCallExpr.additionalTrailingClosures.index( + functionCallExpr.additionalTrailingClosures.endIndex, + offsetBy: -1, + limitedBy: functionCallExpr.additionalTrailingClosures.startIndex + ), + let index = functionCallExpr.additionalTrailingClosures.index( + functionCallExpr.additionalTrailingClosures.startIndex, + offsetBy: centeredTrailingClosureOffset - 1, + limitedBy: endIndex + ) + { + if snapshot.closureDescriptor.contains( + functionCallExpr.additionalTrailingClosures[index].label.text + ) { + if let snapshotClosure { + functionCallExpr.additionalTrailingClosures[index].label = snapshotLabel + let trailingTrivia = functionCallExpr.additionalTrailingClosures[index].closure + .trailingTrivia + functionCallExpr.additionalTrailingClosures[index].closure = + if trailingTrivia.count > 0 { + snapshotClosure.with( + \.trailingTrivia, + snapshotClosure.trailingTrivia + trailingTrivia + ) + } else { + snapshotClosure + } + } else { + functionCallExpr.additionalTrailingClosures.remove(at: index) + } + } else if let newElement { + functionCallExpr.additionalTrailingClosures.insert( + newElement.with(\.trailingTrivia, .space), + at: index + ) + } + } else if centeredTrailingClosureOffset >= 1, let newElement { + if let index = functionCallExpr.additionalTrailingClosures.index( + functionCallExpr.additionalTrailingClosures.endIndex, + offsetBy: -1, + limitedBy: functionCallExpr.additionalTrailingClosures.startIndex + ) { + functionCallExpr.additionalTrailingClosures[index].trailingTrivia = .space + } else { + functionCallExpr.trailingClosure?.trailingTrivia = .space + } + functionCallExpr.additionalTrailingClosures.append(newElement) + } else { + fatalError() + } + + default: + fatalError() + } + } + + if functionCallExpr.arguments.isEmpty, + functionCallExpr.trailingClosure != nil, + functionCallExpr.leftParen != nil, + functionCallExpr.rightParen != nil + { + functionCallExpr.leftParen = nil + functionCallExpr.rightParen = nil + functionCallExpr.calledExpression.trailingTrivia = .space + } + + return ExprSyntax(functionCallExpr) + } +} +#endif diff --git a/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotVisitor.swift b/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotVisitor.swift new file mode 100644 index 000000000..d90eac737 --- /dev/null +++ b/Sources/InlineSnapshotTesting/SwiftSyntax/SnapshotVisitor.swift @@ -0,0 +1,81 @@ +#if canImport(SwiftSyntax601) +import SwiftSyntax + +final class SnapshotVisitor: SyntaxVisitor { + + let functionCallColumn: Int + let functionCallLine: Int + let sourceLocationConverter: SourceLocationConverter + let closureDescriptor: SnapshotClosureDescriptor + var trailingClosureLine: Int? + + init( + functionCallLine: Int, + functionCallColumn: Int, + sourceLocationConverter: SourceLocationConverter, + closureDescriptor: SnapshotClosureDescriptor + ) { + self.functionCallColumn = functionCallColumn + self.functionCallLine = functionCallLine + self.sourceLocationConverter = sourceLocationConverter + self.closureDescriptor = closureDescriptor + super.init(viewMode: .all) + } + + override func visit(_ functionCallExpr: FunctionCallExprSyntax) -> SyntaxVisitorContinueKind { + let location = functionCallExpr.calledExpression.endLocation( + converter: self.sourceLocationConverter, + afterTrailingTrivia: true + ) + + guard + self.functionCallLine == location.line, + self.functionCallColumn == location.column + else { return .visitChildren } + + let arguments = functionCallExpr.arguments + let firstTrailingClosureOffset = + arguments + .enumerated() + .reversed() + .prefix(while: { $0.element.expression.is(ClosureExprSyntax.self) }) + .last? + .offset ?? arguments.count + + let trailingClosureOffset = + firstTrailingClosureOffset + + self.closureDescriptor.trailingClosureOffset + + let centeredTrailingClosureOffset = trailingClosureOffset - arguments.count + + switch centeredTrailingClosureOffset { + case ..<0: + let index = arguments.index(arguments.startIndex, offsetBy: trailingClosureOffset) + self.trailingClosureLine = + arguments[index] + .startLocation(converter: self.sourceLocationConverter) + .line + + case 0: + self.trailingClosureLine = functionCallExpr.trailingClosure.map { + $0.startLocation(converter: self.sourceLocationConverter).line + } + + case 1...: + let index = functionCallExpr.additionalTrailingClosures.index( + functionCallExpr.additionalTrailingClosures.startIndex, + offsetBy: centeredTrailingClosureOffset - 1 + ) + if centeredTrailingClosureOffset - 1 < functionCallExpr.additionalTrailingClosures.count { + self.trailingClosureLine = + functionCallExpr.additionalTrailingClosures[index] + .startLocation(converter: self.sourceLocationConverter) + .line + } + default: + break + } + return .skipChildren + } +} +#endif diff --git a/Sources/InlineSnapshotTesting/SwiftSyntax/TestSource.swift b/Sources/InlineSnapshotTesting/SwiftSyntax/TestSource.swift new file mode 100644 index 000000000..3227f6d0d --- /dev/null +++ b/Sources/InlineSnapshotTesting/SwiftSyntax/TestSource.swift @@ -0,0 +1,9 @@ +#if canImport(SwiftSyntax601) +import SwiftSyntax + +struct TestSource { + let source: String + let sourceFile: SourceFileSyntax + let sourceLocationConverter: SourceLocationConverter +} +#endif diff --git a/Sources/SnapshotTesting/Exports.swift b/Sources/SnapshotTesting/Exports.swift new file mode 100644 index 000000000..f90e2c7bc --- /dev/null +++ b/Sources/SnapshotTesting/Exports.swift @@ -0,0 +1 @@ +@_exported import XCSnapshotTesting diff --git a/Sources/SnapshotTesting/TestCompletionNotifier.swift b/Sources/SnapshotTesting/TestCompletionNotifier.swift new file mode 100644 index 000000000..d842df976 --- /dev/null +++ b/Sources/SnapshotTesting/TestCompletionNotifier.swift @@ -0,0 +1,100 @@ +import Foundation +import Testing +@_spi(Internals) import XCSnapshotTesting + +public struct FinalizeSnapshotsSuiteTrait: SuiteTrait { + + public let isRecursive = false + + public func scopeProvider( + for test: Test, + testCase: Test.Case? + ) -> TestScopeProvider? { + TestScopeProvider() + } +} + +extension Trait where Self == FinalizeSnapshotsSuiteTrait { + + /// A suite trait that finalizes all snapshot tests after the suite completes execution. + /// + /// Use this trait to automatically trigger snapshot finalization logic— + /// such as emitting notifications or performing cleanup—after all tests + /// in a suite have finished running. This is useful for ensuring that any + /// resources or state associated with snapshot testing are properly handled + /// after tests conclude. + /// + /// Apply this trait to your test suite using the `@Suite(.finalizeSnapshots)` + /// macro. + /// + /// Example: + /// ```swift + /// @Suite(.finalizeSnapshots) + /// struct MySnapshotTests { ... } + /// ``` + /// + /// - Note: This trait is non-recursive and will only apply to the suite where it is specified. + public static var finalizeSnapshots: Self { + .init() + } +} + +extension FinalizeSnapshotsSuiteTrait { + + public struct TestScopeProvider: TestScoping { + + public func provideScope( + for test: Test, + testCase: Test.Case?, + performing function: @Sendable () async throws -> Void + ) async throws { + try await withTestCompletionTracking( + for: test, + operation: function + ) + } + } +} + +private func withTestCompletionTracking( + for test: Test, + operation: () async throws -> R, + isolation: isolated Actor? = #isolation, + file: String = #file, + line: UInt = #line +) async throws -> R { + precondition(test.isSuite) + + if TestCompletionNotifier.current != nil { + return try await operation() + } + + return try await TestCompletionNotifier.$current.withValue( + TestCompletionNotifier(test.sourceLocation), + operation: operation, + isolation: isolation, + file: file, + line: line + ) +} + +final class TestCompletionNotifier: Sendable { + + @TaskLocal static var current: TestCompletionNotifier? + + private let sourceLocation: SourceLocation + + fileprivate init(_ sourceLocation: SourceLocation) { + self.sourceLocation = sourceLocation + } + + deinit { + NotificationCenter.default.post( + name: SwiftTestingDidFinishNotification, + object: nil, + userInfo: [ + kTestFileName: sourceLocation.fileName + ] + ) + } +} diff --git a/Sources/SnapshotTesting/TestingSystem+SwiftTestingSystem.swift b/Sources/SnapshotTesting/TestingSystem+SwiftTestingSystem.swift new file mode 100644 index 000000000..6504a780d --- /dev/null +++ b/Sources/SnapshotTesting/TestingSystem+SwiftTestingSystem.swift @@ -0,0 +1,112 @@ +import Foundation +import Testing +@_spi(Internals) import XCSnapshotTesting + +extension TestingSystem: SwiftTestingSystem { + + public var environment: TestingSystemEnvironment? { + Test.current?.traits.mapIntoTestingEnvironment() + } + + public var isRunning: Bool { + Test.current != nil + } + + public var isTestCompletionAttached: Bool { + isRunning && TestCompletionNotifier.current != nil + } + + public func add( + _ name: String, + attachments: [SnapshotAttachment], + fileID: StaticString, + filePath: StaticString, + line: UInt, + column: UInt + ) { + #if swift(>=6.2) + for attachment in attachments { + guard let payload = attachment.payload else { + continue + } + + let attachmentName: String + if let name = attachment.name, !name.isEmpty { + attachmentName = name + } else { + attachmentName = attachment.uniformTypeIdentifier + } + + Attachment.record( + payload, + named: attachmentName, + sourceLocation: SourceLocation( + fileID: String(describing: fileID), + filePath: String(describing: filePath), + line: Int(line), + column: Int(column) + ) + ) + } + #endif + } + + public func record( + message: String, + fileID: StaticString, + filePath: StaticString, + line: UInt, + column: UInt + ) { + Issue.record( + Comment(rawValue: message), + sourceLocation: SourceLocation( + fileID: String(describing: fileID), + filePath: String(describing: filePath), + line: Int(line), + column: Int(column) + ) + ) + } +} + +extension Array where Element == any Trait { + + func mapIntoTestingEnvironment() -> TestingSystemEnvironment { + var environment = TestingSystemEnvironment() + + for trait in reversed() { + switch trait { + case let recordTrait as RecordTrait: + if environment.recordMode == nil { + environment.recordMode = recordTrait.recordMode + } + case let diffToolTrait as DiffToolTrait: + if environment.diffTool == nil { + environment.diffTool = diffToolTrait.diffTool + } + case let maxConcurrentTestsTrait as MaxConcurrentTestsTrait: + if environment.maxConcurrentTests == nil { + environment.maxConcurrentTests = maxConcurrentTestsTrait.maxConcurrentTests + } + case let platformTrait as PlatformTrait: + if environment.platform == nil { + environment.platform = platformTrait.platform + } + default: + continue + } + + guard + environment.diffTool != nil, + environment.recordMode != nil, + environment.maxConcurrentTests != nil, + environment.platform != nil + else { continue } + + break + } + + return environment + } +} diff --git a/Sources/SnapshotTesting/Traits/DiffToolTrait.swift b/Sources/SnapshotTesting/Traits/DiffToolTrait.swift new file mode 100644 index 000000000..baa21fe35 --- /dev/null +++ b/Sources/SnapshotTesting/Traits/DiffToolTrait.swift @@ -0,0 +1,29 @@ +import Foundation +import Testing +@_spi(Internals) import XCSnapshotTesting + +public struct DiffToolTrait: SuiteTrait, TestTrait { + public let isRecursive = true + let diffTool: DiffTool +} + +extension Trait where Self == DiffToolTrait { + + /// Returns a trait that specifies a custom diff tool for snapshot comparisons. + /// + /// Use this method to override the default diff tool used by snapshot testing frameworks + /// when comparing reference images or files. This is useful for customizing how differences are presented, + /// such as by launching a specific visual diff application or using a particular command-line utility. + /// + /// - Parameter diffTool: The `DiffTool` instance to use for diffing snapshots within the scope of this trait. + /// - Returns: A `DiffToolTrait` that can be applied at the suite or test level. + /// + /// Example usage: + /// ```swift + /// @Suite(.diffTool(.ksdiff)) + /// struct MySnapshotTests { ... } + /// ``` + public static func diffTool(_ diffTool: DiffTool) -> Self { + .init(diffTool: diffTool) + } +} diff --git a/Sources/SnapshotTesting/Traits/MaxConcurrentTestsTrait.swift b/Sources/SnapshotTesting/Traits/MaxConcurrentTestsTrait.swift new file mode 100644 index 000000000..0a6151939 --- /dev/null +++ b/Sources/SnapshotTesting/Traits/MaxConcurrentTestsTrait.swift @@ -0,0 +1,28 @@ +import Foundation +import Testing +@_spi(Internals) import XCSnapshotTesting + +public struct MaxConcurrentTestsTrait: SuiteTrait, TestTrait { + public let isRecursive = true + let maxConcurrentTests: Int +} + +extension Trait where Self == MaxConcurrentTestsTrait { + + /// Limits the maximum number of tests that can execute concurrently within a suite or for an individual test. + /// + /// Use this trait to control the level of parallelism during test execution, which is helpful for tests that are not thread-safe + /// or when you want to limit resource consumption. When applied to a suite, the limit is enforced recursively for all contained tests and nested suites. + /// + /// - Parameter maxConcurrentTests: The maximum number of tests allowed to execute simultaneously. Must be greater than zero. + /// - Returns: A trait that constrains the test or suite's concurrency. + /// + /// Example: + /// ```swift + /// @Suite(.maxConcurrentTests(2)) + /// struct MySuite { ... } + /// ``` + public static func maxConcurrentTests(_ maxConcurrentTests: Int) -> Self { + .init(maxConcurrentTests: maxConcurrentTests) + } +} diff --git a/Sources/SnapshotTesting/Traits/PlatformTrait.swift b/Sources/SnapshotTesting/Traits/PlatformTrait.swift new file mode 100644 index 000000000..3c114ff51 --- /dev/null +++ b/Sources/SnapshotTesting/Traits/PlatformTrait.swift @@ -0,0 +1,30 @@ +import Foundation +import Testing +@_spi(Internals) import XCSnapshotTesting + +public struct PlatformTrait: SuiteTrait, TestTrait { + public let isRecursive = true + let platform: String +} + +extension Trait where Self == PlatformTrait { + + /// Adds a platform trait to a test or suite, allowing it to be conditionally + /// included or configured based on the specified platform identifier. + /// + /// You can use this trait to annotate tests for filtering or to provide specialized + /// behaviors on certain platforms (such as "iOS", "macOS", "watchOS", or "visionOS"). + /// + /// Example usage: + /// + /// @Suite(.platform("iOS")) + /// + /// - Parameter platform: An optional string specifying the platform identifier. + /// + /// - Returns: A `PlatformTrait` configured with the given platform. + /// + /// - Note: Setting this value to `nil` or an empty string (e.g., `.platform(nil)` or `.platform("")`) makes all snapshots for this suite or test shared between platforms, removing any platform-specific distinction. + public static func platform(_ platform: String?) -> Self { + .init(platform: platform ?? "") + } +} diff --git a/Sources/SnapshotTesting/Traits/RecordTrait.swift b/Sources/SnapshotTesting/Traits/RecordTrait.swift new file mode 100644 index 000000000..355ff9c8d --- /dev/null +++ b/Sources/SnapshotTesting/Traits/RecordTrait.swift @@ -0,0 +1,40 @@ +import Foundation +import Testing +@_spi(Internals) import XCSnapshotTesting + +public struct RecordTrait: SuiteTrait, TestTrait { + public let isRecursive = true + let recordMode: RecordMode +} + +extension Trait where Self == RecordTrait { + + /// Adds record mode configuration to a suite or test. + /// + /// Use this trait to specify the snapshot recording mode for the test or suite. + /// This is typically used in snapshot testing to control whether expected + /// snapshots are created or updated. + /// + /// The default value is `.missing`, which means a snapshot will only be recorded if no file is found (i.e., a snapshot is missing). + /// + /// ## Examples + /// + /// ```swift + /// @Test(.record(.all)) + /// func testRecordAllSnapshots() async throws { + /// // Test code here + /// } + /// + /// @Suite(.record(.never)) + /// struct NoSnapshotRecordingTests { + /// // Suite contents here + /// } + /// ``` + /// + /// - Parameter recordMode: The `RecordMode` value to use when running tests. + /// This controls whether snapshots should be recorded (created/updated) or not. + /// - Returns: A `RecordTrait` configured with the given `recordMode`. + public static func record(_ recordMode: RecordMode) -> Self { + .init(recordMode: recordMode) + } +} diff --git a/Sources/SnapshotTestingCustomDump/CustomDump.swift b/Sources/SnapshotTestingCustomDump/CustomDump.swift index afdeabcf0..179e34bef 100644 --- a/Sources/SnapshotTestingCustomDump/CustomDump.swift +++ b/Sources/SnapshotTestingCustomDump/CustomDump.swift @@ -1,24 +1,31 @@ import CustomDump -import SnapshotTesting +import XCSnapshotTesting -extension Snapshotting where Format == String { - /// A snapshot strategy for comparing any structure based on a - /// [custom dump](https://github.com/pointfreeco/swift-custom-dump). - /// - /// ```swift - /// assertSnapshot(of: user, as: .customDump) - /// ``` - /// - /// Records: - /// - /// ``` - /// User( - /// bio: "Blobbed around the world.", - /// id: 1, - /// name: "Blobby" - /// ) - /// ``` - public static var customDump: Snapshotting { - SimplySnapshotting.lines.pullback(String.init(customDumping:)) - } +#if !os(visionOS) +@_exported import _SnapshotTestingCustomDump +#endif + +extension SyncSnapshot where Output == StringBytes { + + /// A snapshot strategy for comparing any structure based on a + /// [custom dump](https://github.com/pointfreeco/swift-custom-dump). + /// + /// ```swift + /// try assert(of: user, as: .customDump) + /// ``` + /// + /// Records: + /// + /// ``` + /// User( + /// bio: "Blobbed around the world.", + /// id: 1, + /// name: "Blobby" + /// ) + /// ``` + public static var customDump: SyncSnapshot { + IdentitySyncSnapshot.lines.pullback { + String(customDumping: $0) + } + } } diff --git a/Sources/XCSnapshotTesting/Assert/Assert.swift b/Sources/XCSnapshotTesting/Assert/Assert.swift new file mode 100644 index 000000000..281b9e5be --- /dev/null +++ b/Sources/XCSnapshotTesting/Assert/Assert.swift @@ -0,0 +1,560 @@ +import Foundation + +// MARK: - Assert snapshot + +/// Validates a single snapshot of an asynchronously computed input value using a specified snapshot strategy. +/// +/// This function serializes and compares the provided input value against a previously recorded snapshot, +/// using the supplied `AsyncSnapshot` strategy and serialization configuration. Designed for use in +/// snapshot and regression testing, it ensures that the current representation of the input matches the stored +/// reference, or updates the reference if recording is enabled. +/// +/// - Parameters: +/// - input: An autoclosure that asynchronously produces the input value to be tested. The closure is executed during the assertion. +/// - snapshot: An `AsyncSnapshot` describing how the input should be converted to bytes and compared (e.g., image rendering, text output). +/// - serialization: Controls output transformation details, such as image encoding or precision. Defaults to `.init()`. +/// - name: An optional identifier for the snapshot. Useful to differentiate multiple assertions in a single test method. +/// - recording: Optionally override the snapshot recording mode for this assertion (e.g., `.always` to force update, `.never` to only compare). +/// - snapshotDirectory: Optionally specify a custom directory for stored snapshots, overriding the default. +/// - timeout: Time in seconds before the assertion fails for taking too long. Defaults to zero (no timeout). +/// - isolation: Optionally specify an actor for input evaluation, supporting thread/actor isolation. Defaults to current context. +/// - fileID: The unique identifier for the file in which the assertion appears. Supplied automatically by the compiler. +/// - filePath: The file path where the assertion is called. Supplied automatically by the compiler. +/// - testName: The name of the test function calling the assertion. Supplied automatically by the compiler. +/// - line: The source line number where the assertion is called. Supplied automatically by the compiler. +/// - column: The source column where the assertion is called. Supplied automatically by the compiler. +/// +/// - Throws: An error if input evaluation, snapshotting, or comparison fails, or if recording fails in recording mode. +/// +/// - Important: For multiple assertions within the same test (e.g., in a loop), supply unique `name` values to +/// avoid snapshot counting issues. Automatic snapshot naming relies on the call site position. +/// +/// - Note: The recording mode falls back to session-level or environment settings if not provided. +/// Use testing traits, or ``withTestingEnvironment(record:diffTool:maxConcurrentTests:platform:operation:file:line:)`` +/// to control globally. +/// +/// - Example: +/// ```swift +/// try await assert( +/// of: view, +/// as: .image(layout: .device(.iPhone15ProMax)), +/// named: "light_mode" +/// ) +/// ``` +public func assert( + of input: @Sendable @autoclosure () async throws -> Input, + as snapshot: AsyncSnapshot, + serialization: DataSerialization = DataSerialization(), + named name: String? = nil, + record recording: RecordMode? = nil, + snapshotDirectory: String? = nil, + timeout: TimeInterval = .zero, + isolation: isolated Actor? = #isolation, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) async throws { + let failure = try await verify( + of: await input(), + as: snapshot, + serialization: serialization, + named: name, + record: recording, + snapshotDirectory: snapshotDirectory, + timeout: timeout, + isolation: isolation, + fileID: fileID, + file: filePath, + testName: testName, + line: line, + column: column + ) + + guard let message = failure else { return } + + try TestingSystem.shared.record( + message: message, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) +} + +/// Asserts that an asynchronously produced value matches multiple named snapshots, each using a distinct strategy. +/// +/// For each entry in the provided dictionary, this function generates an input value asynchronously and compares it +/// against a previously recorded reference snapshot using the corresponding `AsyncSnapshot` strategy. Each assertion +/// uses the key as a unique snapshot name. If in recording mode, the reference is updated instead. Failures are +/// reported using the testing system. +/// +/// - Parameters: +/// - input: An autoclosure that asynchronously produces the value to be snapshotted and compared. Evaluated once per strategy. +/// - strategies: A dictionary mapping unique snapshot names to `AsyncSnapshot` strategies, allowing different configurations or formats per assertion. +/// - serialization: Settings for how output is serialized (e.g., image encoding, text precision). Defaults to `.init()`. +/// - recording: Optionally override the recording mode for all snapshots in this assertion (e.g., `.always`, `.never`). +/// - snapshotDirectory: Optionally specify a custom directory for storing or comparing snapshots, overriding the default. +/// - timeout: Maximum seconds to wait for input evaluation per assertion. Defaults to `.zero`. +/// - isolation: Optionally specify an actor context for input evaluation, supporting actor/thread isolation. Defaults to current context. +/// - fileID: The unique identifier of the source file. Provided automatically by the compiler. +/// - filePath: The path to the source file. Provided automatically. +/// - testName: The name of the test function. Provided automatically. +/// - line: The line number of the assertion in the source file. Provided automatically. +/// - column: The column number of the assertion in the source file. Provided automatically. +/// +/// - Throws: An error if input evaluation, snapshotting, or comparison fails, or if writing fails in recording mode. +/// +/// - Important: Each snapshot uses the dictionary key as its unique name. If you need to assert multiple snapshots within a loop, +/// provide unique keys to prevent snapshot overwrites or counting issues. +/// +/// - Example: +/// ```swift +/// try await assert( +/// of: view, +/// as: [ +/// "light": .image(layout: .device(.iPhone15ProMax)), +/// "dark": .image(layout: .device(.iPhone15ProMaxDark)) +/// ] +/// ) +/// ``` +public func assert( + of input: @Sendable @autoclosure () async throws -> Input, + as strategies: [String: AsyncSnapshot], + serialization: DataSerialization = DataSerialization(), + record recording: RecordMode? = nil, + snapshotDirectory: String? = nil, + timeout: TimeInterval = .zero, + isolation: isolated Actor? = #isolation, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) async throws { + for (name, configuration) in strategies { + try await assert( + of: await input(), + as: configuration, + serialization: serialization, + named: name, + record: recording, + snapshotDirectory: snapshotDirectory, + timeout: timeout, + isolation: isolation, + fileID: fileID, + file: filePath, + testName: testName, + line: line, + column: column + ) + } +} + +/// Asserts that an asynchronously produced value matches multiple snapshots, each using a different strategy from the provided array. +/// +/// For each strategy in the array, this function generates an input value asynchronously and compares it against a previously recorded +/// reference snapshot using the corresponding `AsyncSnapshot` strategy. Each assertion is uniquely named using a combination of the +/// test function and the zero-based index (e.g., `testName().1@1`, `testName().1@2`, ...), ensuring distinct snapshot identities per call site. +/// +/// - Parameters: +/// - input: An autoclosure that asynchronously produces the value to be snapshotted and compared. Evaluated once per strategy. +/// - strategies: An array of `AsyncSnapshot` strategies to apply to the input. Each entry results in a separate snapshot assertion. +/// - serialization: Settings for how output is serialized (e.g., image encoding, text precision). Defaults to `.init()`. +/// - recording: Optionally override the recording mode for all snapshots in this assertion (e.g., `.always`, `.never`). +/// - snapshotDirectory: Optionally specify a custom directory for storing or comparing snapshots, overriding the default. +/// - timeout: Maximum seconds to wait for input evaluation per assertion. Defaults to `.zero`. +/// - isolation: Optionally specify an actor context for input evaluation, supporting actor/thread isolation. Defaults to current context. +/// - fileID: The unique identifier of the source file. Provided automatically by the compiler. +/// - filePath: The path to the source file. Provided automatically. +/// - testName: The name of the test function. Provided automatically. +/// - line: The line number of the assertion in the source file. Provided automatically. +/// - column: The column number of the assertion in the source file. Provided automatically. +/// +/// - Throws: An error if input evaluation, snapshotting, or comparison fails, or if writing fails in recording mode. +/// +/// - Important: Each snapshot uses a unique name based on the test function and its index (e.g., `testName().1@1`). This allows safe use in loops or repeated calls without naming collisions. +/// If you require meaningful snapshot names, prefer the dictionary overload. +/// +/// - Example: +/// ```swift +/// let strategies = [ +/// .image(layout: .device(.iPhone15ProMax)), +/// .image(precision: 0.95) +/// ] +/// try await assert( +/// of: view, +/// as: strategies +/// ) +/// ``` +public func assert( + of input: @Sendable @autoclosure () async throws -> Input, + as strategies: [AsyncSnapshot], + serialization: DataSerialization = DataSerialization(), + record recording: RecordMode? = nil, + snapshotDirectory: String? = nil, + timeout: TimeInterval = .zero, + isolation: isolated Actor? = #isolation, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) async throws { + let uniqueID = TestingSession.shared.forLoop( + fileID: fileID, + filePath: filePath, + function: String(describing: testName), + line: line, + column: column + ) + + for (index, strategy) in strategies.enumerated() { + try await assert( + of: await input(), + as: strategy, + serialization: serialization, + named: "\(uniqueID)@\(index + 1)", + record: recording, + snapshotDirectory: snapshotDirectory, + timeout: timeout, + isolation: isolation, + fileID: fileID, + file: filePath, + testName: testName, + line: line, + column: column + ) + } +} + +// MARK: - Sync snapshot + +/// Asserts that a synchronously produced value matches a previously recorded snapshot using the specified strategy. +/// +/// This function serializes and compares the given input value against a stored reference using the provided `SyncSnapshot` strategy +/// and serialization configuration. It is designed for snapshot or regression testing, ensuring the current value matches the reference, +/// or updates the reference if recording is enabled. Failures are reported using the testing system. +/// +/// - Parameters: +/// - input: An autoclosure producing the value to be tested. Evaluated during the assertion. +/// - snapshot: The `SyncSnapshot` strategy describing how to serialize and compare the input (e.g., as an image, as text). +/// - serialization: Settings for output transformation, such as image encoding or precision. Defaults to `.init()`. +/// - name: An optional identifier for the snapshot. Useful for disambiguating multiple assertions in a single test method. +/// - recording: Optionally override the recording mode for this assertion (e.g., `.always` to update, `.never` to only compare). +/// - snapshotDirectory: Optionally specify a custom directory for stored snapshots, overriding the default. +/// - timeout: Time in seconds before the assertion fails if too long. Defaults to 5 seconds. +/// - fileID: The unique identifier of the source file. Provided automatically by the compiler. +/// - filePath: The path to the source file. Provided automatically. +/// - testName: The name of the test function. Provided automatically. +/// - line: The line number of the assertion call. Provided automatically. +/// - column: The column number of the assertion call. Provided automatically. +/// +/// - Throws: An error if input evaluation, snapshotting, or comparison fails, or if recording fails when enabled. +/// +/// - Important: For multiple assertions within the same test (such as in a loop), supply unique `name` values to avoid snapshot overwrites or collisions. Automatic naming uses the call site location. +/// +/// - Note: If `recording` is not provided, global or session-level settings apply. Use test traits or `withTestingEnvironment(record:operation:)` to control recording globally. +/// +/// - Example: +/// ```swift +/// try assert( +/// of: renderedView, +/// as: .image(layout: .device(.iPadPro)), +/// named: "dark_mode" +/// ) +/// ``` +public func assert( + of input: @autoclosure () throws -> Input, + as snapshot: SyncSnapshot, + serialization: DataSerialization = DataSerialization(), + named name: String? = nil, + record recording: RecordMode? = nil, + snapshotDirectory: String? = nil, + timeout: TimeInterval = 5, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) throws { + let failure = try verify( + of: input(), + as: snapshot, + serialization: serialization, + named: name, + record: recording, + snapshotDirectory: snapshotDirectory, + timeout: timeout, + fileID: fileID, + file: filePath, + testName: testName, + line: line, + column: column + ) + + guard let message = failure else { return } + + try TestingSystem.shared.record( + message: message, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) +} + +/// Asserts that a synchronously produced value matches multiple named snapshots, each using a distinct strategy. +/// +/// For each entry in the provided dictionary, this function compares the input value against a previously recorded reference snapshot +/// using the corresponding `SyncSnapshot` strategy. Each assertion uses the dictionary key as a unique snapshot name. If in recording mode, +/// the reference is updated instead. Failures are reported using the testing system. +/// +/// - Parameters: +/// - input: The value to be snapshotted and compared. Used for all strategies. +/// - strategies: A dictionary mapping unique snapshot names to `SyncSnapshot` strategies, allowing different configurations or formats per assertion. +/// - serialization: Settings for how output is serialized (e.g., image encoding, text precision). Defaults to `.init()`. +/// - recording: Optionally override the recording mode for all snapshots in this assertion (e.g., `.always`, `.never`). +/// - snapshotDirectory: Optionally specify a custom directory for storing or comparing snapshots, overriding the default. +/// - timeout: Maximum seconds to wait for each assertion. Defaults to 5. +/// - fileID: The unique identifier of the source file. Provided automatically by the compiler. +/// - filePath: The path to the source file. Provided automatically. +/// - testName: The name of the test function. Provided automatically. +/// - line: The line number of the assertion in the source file. Provided automatically. +/// - column: The column number of the assertion in the source file. Provided automatically. +/// +/// - Throws: An error if snapshotting or comparison fails, or if writing fails in recording mode. +/// +/// - Important: Each snapshot uses the dictionary key as its unique name. If you need to assert multiple snapshots within a loop, provide unique keys to prevent snapshot overwrites or counting issues. +/// +/// - Example: +/// ```swift +/// try assert( +/// of: renderedView, +/// as: [ +/// "light": .image(layout: .device(.iPadPro)), +/// "dark": .image(layout: .device(.iPadProDark)) +/// ] +/// ) +/// ``` +public func assert( + of input: Input, + as strategies: [String: SyncSnapshot], + serialization: DataSerialization = DataSerialization(), + record recording: RecordMode? = nil, + snapshotDirectory: String? = nil, + timeout: TimeInterval = 5, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) throws { + for (name, configuration) in strategies { + try? assert( + of: input, + as: configuration, + serialization: serialization, + named: name, + record: recording, + snapshotDirectory: snapshotDirectory, + timeout: timeout, + fileID: fileID, + file: filePath, + testName: testName, + line: line, + column: column + ) + } +} + +/// Asserts that a synchronously produced value matches multiple snapshots, each using a different strategy from the provided array. +/// +/// For each strategy in the array, this function compares the input value against a previously recorded reference snapshot using the +/// corresponding `SyncSnapshot` strategy. Each assertion is uniquely named using a combination of the test function and the index +/// (e.g., `testName().1@1`, `testName().1@2`, ...), ensuring distinct snapshot identities per call site. +/// +/// - Parameters: +/// - input: The value to be snapshotted and compared. Used for all strategies. +/// - strategies: An array of `SyncSnapshot` strategies to apply to the input. Each entry results in a separate snapshot assertion. +/// - serialization: Settings for how output is serialized (e.g., image encoding, text precision). Defaults to `.init()`. +/// - recording: Optionally override the recording mode for all snapshots in this assertion (e.g., `.always`, `.never`). +/// - snapshotDirectory: Optionally specify a custom directory for storing or comparing snapshots, overriding the default. +/// - timeout: Maximum seconds to wait for each assertion. Defaults to 5. +/// - fileID: The unique identifier of the source file. Provided automatically by the compiler. +/// - filePath: The path to the source file. Provided automatically. +/// - testName: The name of the test function. Provided automatically. +/// - line: The line number of the assertion in the source file. Provided automatically. +/// - column: The column number of the assertion in the source file. Provided automatically. +/// +/// - Throws: An error if snapshotting or comparison fails, or if writing fails in recording mode. +/// +/// - Important: Each snapshot uses a unique name based on the test function and its index (e.g., `testName().1@1`). This allows safe use in loops or repeated calls without naming collisions. If you require meaningful snapshot names, prefer the dictionary overload. +/// +/// - Example: +/// ```swift +/// let strategies = [ +/// .image(layout: .device(.iPadPro)), +/// .image(precision: 0.95) +/// ] +/// try await assert( +/// of: renderedView, +/// as: strategies +/// ) +/// ``` +public func assert( + of input: Input, + as strategies: [SyncSnapshot], + serialization: DataSerialization = DataSerialization(), + record recording: RecordMode? = nil, + snapshotDirectory: String? = nil, + timeout: TimeInterval = 5, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) async throws { + for strategy in strategies { + try? assert( + of: input, + as: strategy, + serialization: serialization, + record: recording, + snapshotDirectory: snapshotDirectory, + timeout: timeout, + fileID: fileID, + file: filePath, + testName: testName, + line: line, + column: column + ) + } +} + +/// Validates an asynchronously produced value against a stored snapshot using a specified strategy and serialization configuration. +/// +/// This function evaluates the provided input value asynchronously, serializes it using the supplied `AsyncSnapshot` strategy and `DataSerialization`, +/// and compares it to a previously recorded snapshot on disk. If the assertion fails, it returns a descriptive failure message; +/// otherwise, it returns `nil`. Supports optional snapshot recording, custom directories, timeouts, and actor isolation. +/// +/// - Parameters: +/// - input: An autoclosure that asynchronously produces the value to snapshot and compare. Executed at assertion time. +/// - snapshot: An `AsyncSnapshot` describing how to serialize and compare the input (e.g., image rendering, text output). +/// - serialization: Settings for output transformation and encoding. Defaults to `.init()`. +/// - name: An optional identifier for the snapshot, used to disambiguate multiple assertions within a test. +/// - recording: Optionally override the recording mode for this assertion (e.g., `.always` to update, `.never` to only compare). +/// - snapshotDirectory: Optionally specify a directory for storing or comparing snapshots, overriding the default. +/// - timeout: Maximum seconds to wait for input evaluation or snapshotting. Defaults to `.zero`. +/// - isolation: Optionally specify an actor for input evaluation, supporting thread/actor isolation. Defaults to current context. +/// - fileID: The unique identifier of the source file. Provided automatically by the compiler. +/// - filePath: The path to the source file. Provided automatically. +/// - testName: The name of the test function. Provided automatically. +/// - line: The line number of the assertion in the source file. Provided automatically. +/// - column: The column number of the assertion in the source file. Provided automatically. +/// +/// - Returns: An optional failure message string if the assertion fails; otherwise, `nil`. +/// +/// - Throws: An error if input evaluation, snapshotting, comparison, or recording fails. +/// +/// - Important: For multiple assertions within the same test (e.g., in a loop), use unique `name` values. +/// - Note: Recording mode falls back to environment or session-level settings if not specified. +public func verify( + of input: @Sendable @autoclosure () async throws -> Input, + as snapshot: AsyncSnapshot, + serialization: DataSerialization = DataSerialization(), + named name: String? = nil, + record recording: RecordMode? = nil, + snapshotDirectory: String? = nil, + timeout: TimeInterval = .zero, + isolation: isolated Actor? = #isolation, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) async throws -> String? { + let engine = SnapshotFileEngine>( + sourceURL: snapshotDirectory.map { + URL(fileURLWithPath: $0, isDirectory: true) + } + ) + + let tester = SnapshotTester( + engine: engine, + record: recording, + timeout: timeout, + name: name, + serialization: serialization, + fileID: fileID, + filePath: filePath, + function: testName, + line: line, + column: column + ) + + return try await tester(input(), for: snapshot)?.message +} + +/// Validates a synchronously produced value against a stored snapshot using a specified strategy and serialization configuration. +/// +/// This function evaluates the provided input value, serializes it using the supplied `SyncSnapshot` strategy and `DataSerialization`, +/// and compares it to a previously recorded snapshot on disk. If the assertion fails, it returns a descriptive failure message; +/// otherwise, it returns `nil`. Supports optional snapshot recording, custom directories, and timeouts. +/// +/// - Parameters: +/// - input: An autoclosure that produces the value to snapshot and compare. Executed at assertion time. +/// - snapshot: A `SyncSnapshot` describing how to serialize and compare the input (e.g., image rendering, text output). +/// - serialization: Settings for output transformation and encoding. Defaults to `.init()`. +/// - name: An optional identifier for the snapshot, used to disambiguate multiple assertions within a test. +/// - recording: Optionally override the recording mode for this assertion (e.g., `.always` to update, `.never` to only compare). +/// - snapshotDirectory: Optionally specify a directory for storing or comparing snapshots, overriding the default. +/// - timeout: Maximum seconds to wait for input evaluation or snapshotting. Defaults to 5 seconds. +/// - fileID: The unique identifier of the source file. Provided automatically by the compiler. +/// - filePath: The path to the source file. Provided automatically. +/// - testName: The name of the test function. Provided automatically. +/// - line: The line number of the assertion in the source file. Provided automatically. +/// - column: The column number of the assertion in the source file. Provided automatically. +/// +/// - Returns: An optional failure message string if the assertion fails; otherwise, `nil`. +/// +/// - Throws: An error if input evaluation, snapshotting, comparison, or recording fails. +/// +/// - Important: For multiple assertions within the same test (e.g., in a loop), use unique `name` values. +/// - Note: Recording mode falls back to environment or session-level settings if not specified. +public func verify( + of input: @autoclosure () throws -> Input, + as snapshot: SyncSnapshot, + serialization: DataSerialization = DataSerialization(), + named name: String? = nil, + record recording: RecordMode? = nil, + snapshotDirectory: String? = nil, + timeout: TimeInterval = 5, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + testName: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) throws -> String? { + let engine = SnapshotFileEngine>( + sourceURL: snapshotDirectory.map { + URL(fileURLWithPath: $0, isDirectory: true) + } + ) + + let tester = SnapshotTester( + engine: engine, + record: recording, + timeout: timeout, + name: name, + serialization: serialization, + fileID: fileID, + filePath: filePath, + function: testName, + line: line, + column: column + ) + + return try tester(input(), for: snapshot)?.message +} diff --git a/Sources/XCSnapshotTesting/Assert/CleanCounterBetweenTestCases.swift b/Sources/XCSnapshotTesting/Assert/CleanCounterBetweenTestCases.swift new file mode 100644 index 000000000..49250e1c8 --- /dev/null +++ b/Sources/XCSnapshotTesting/Assert/CleanCounterBetweenTestCases.swift @@ -0,0 +1,44 @@ +#if canImport(XCTest) +import XCTest + +final class CleanCounterBetweenTestCases: NSObject, XCTestObservation { + + @MainActor + private static var registered = false + + fileprivate static func registerIfNeeded() { + performOnMainThread { + guard !registered else { return } + registered = true + XCTestObservationCenter.shared.addTestObserver(CleanCounterBetweenTestCases()) + } + } + + func testBundleDidFinish(_ testBundle: Bundle) { + NotificationCenter.default.post( + name: XCTestBundleDidFinishNotification, + object: nil + ) + } +} + +extension XCTestCase { + + static func registerObserverIfNeeded() { + CleanCounterBetweenTestCases.registerIfNeeded() + } +} +#endif + +@_spi(Internals) +public let XCTestBundleDidFinishNotification = Notification.Name( + "XCTestBundleDidFinishNotification" +) + +@_spi(Internals) +public let SwiftTestingDidFinishNotification = Notification.Name( + "SwiftTestingDidFinishNotification" +) + +@_spi(Internals) +public let kTestFileName = "kTestFileName" diff --git a/Sources/XCSnapshotTesting/Assert/RecordMode.swift b/Sources/XCSnapshotTesting/Assert/RecordMode.swift new file mode 100644 index 000000000..7b179a9aa --- /dev/null +++ b/Sources/XCSnapshotTesting/Assert/RecordMode.swift @@ -0,0 +1,19 @@ +import Foundation + +/// Specifies the recording behavior for snapshots during testing. +/// +/// Adjusts whether and how snapshot files are created or updated during test execution. +public enum RecordMode: Int16, Sendable { + + /// Never records new snapshots. Tests fail if results differ from existing ones; no files are updated. + case never + + /// Records only if a snapshot is missing. Existing snapshots are not replaced, even if mismatches occur. + case missing + + /// Records snapshots only when tests fail due to a mismatch. Useful for automatically updating failing snapshots. + case failed + + /// Always records snapshots, overwriting any existing files. Use to intentionally update all snapshots after UI changes. + case all +} diff --git a/Sources/XCSnapshotTesting/Assert/SnapshotAttachment.swift b/Sources/XCSnapshotTesting/Assert/SnapshotAttachment.swift new file mode 100644 index 000000000..af3ec1bcc --- /dev/null +++ b/Sources/XCSnapshotTesting/Assert/SnapshotAttachment.swift @@ -0,0 +1,112 @@ +import Foundation + +#if os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) +import UIKit +#elseif os(macOS) +import AppKit +#endif + +/// A structure representing a snapshot attachment, including its uniform type identifier, name, and payload data. +/// +/// This type is designed to encapsulate information about a snapshot, including its type, optional name, and binary payload. +/// It provides multiple initializers to create instances from different data types, such as `Data`, `String`, or platform-specific image types. +public struct SnapshotAttachment: Sendable { + + /// The uniform type identifier (UTI) for the snapshot attachment. + /// + /// This identifies the type of data contained in the payload. Defaults to `"public.data"` if not provided. + public let uniformTypeIdentifier: String + + /// The optional name of the snapshot attachment. + /// + /// This can be used to provide a human-readable identifier for the attachment. + public var name: String? + + /// The binary payload data of the snapshot. + /// + /// This represents the actual data stored in the attachment. May be `nil` if no data is provided. + public let payload: Data? + + /// Initializes a `SnapshotAttachment` with a custom uniform type identifier, name, and payload. + /// + /// - Parameters: + /// - identifier: The uniform type identifier for the attachment. Defaults to `"public.data"` if `nil`. + /// - name: An optional name for the attachment. + /// - payload: The binary data payload. Defaults to `nil` if not provided. + public init(uniformTypeIdentifier identifier: String?, name: String?, payload: Data?) { + self.uniformTypeIdentifier = identifier ?? "public.data" + self.name = name + self.payload = payload + } + + /// Initializes a `SnapshotAttachment` with raw data, using a default uniform type identifier. + /// + /// - Parameters: + /// - payload: The binary data to use as the payload. + /// + /// - SeeAlso: Uses `"public.data"` as the uniform type identifier. + public init(data payload: Data) { + self.init( + data: payload, + uniformTypeIdentifier: "public.data" + ) + } + + /// Initializes a `SnapshotAttachment` with raw data and a specified uniform type identifier. + /// + /// - Parameters: + /// - payload: The binary data to use as the payload. + /// - identifier: The uniform type identifier for the attachment. + public init(data payload: Data, uniformTypeIdentifier identifier: String) { + self.init( + uniformTypeIdentifier: identifier, + name: "", + payload: payload + ) + } + + /// Initializes a `SnapshotAttachment` from a string, converting it to UTF-8 data. + /// + /// - Parameter: + /// - string: The string to use as the payload. Uses `"public.plain-text"` as the uniform type identifier. + public init(string: String) { + self.init( + data: Data(string.utf8), + uniformTypeIdentifier: "public.plain-text" + ) + } + + #if os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) + /// Initializes a `SnapshotAttachment` from a platform-specific image (iOS/tvOS/visionOS/watchOS). + /// + /// - Parameter: + /// - image: The `UIImage` to convert to PNG data. Returns `nil` if conversion fails. + public init?(image: UIImage) { + guard let payload = image.pngData() else { + return nil + } + + self.init( + uniformTypeIdentifier: "public.png", + name: "", + payload: payload + ) + } + #elseif os(macOS) + /// Initializes a `SnapshotAttachment` from a platform-specific image (macOS). + /// + /// - Parameter: + /// - image: The `NSImage` to convert to PNG data. Returns `nil` if conversion fails. + public init?(image: NSImage) { + guard let payload = image.pngData() else { + return nil + } + + self.init( + uniformTypeIdentifier: "public.png", + name: "", + payload: payload + ) + } + #endif +} diff --git a/Sources/XCSnapshotTesting/Assert/TestingSession.swift b/Sources/XCSnapshotTesting/Assert/TestingSession.swift new file mode 100644 index 000000000..b5be22619 --- /dev/null +++ b/Sources/XCSnapshotTesting/Assert/TestingSession.swift @@ -0,0 +1,124 @@ +import Foundation +import Testing + +final class TestingSession: Sendable { + + static let shared = TestingSession() + + // MARK: - Private properties + + private let testCounter = TestCounter() + private let forLoopCounter = TestCounter() + + private init() {} + + func functionPosition( + fileID: StaticString, + filePath: StaticString, + function: String, + line: UInt, + column: UInt + ) -> Int { + testCounter( + fileID: fileID, + filePath: filePath, + function: function, + line: line, + column: column + ) + } + + func forLoop( + fileID: StaticString, + filePath: StaticString, + function: String, + line: UInt, + column: UInt + ) -> Int { + forLoopCounter( + fileID: fileID, + filePath: filePath, + function: function, + line: line, + column: column + ) + } +} + +extension TestingSession { + + fileprivate final class TestCounter: @unchecked Sendable { + + // MARK: - Private properties + + private let lock = NSLock() + + // MARK: - Unsafe properties + + private var _registry: [TestLocation: [TestPosition]] = [:] + + init() {} + + func callAsFunction( + fileID: StaticString, + filePath: StaticString, + function: String, + line: UInt, + column: UInt + ) -> Int { + let key = TestLocation( + fileID: fileID, + filePath: filePath, + function: function + ) + + let position = TestPosition( + line: line, + column: column + ) + + return lock.withLock { + var items = _registry[key, default: []] + + if let index = items.firstIndex(of: position) { + return index + 1 + } + + items.append(position) + _registry[key] = items + return items.count + } + } + } +} + +extension TestingSession.TestCounter { + + fileprivate struct TestLocation: Hashable { + + private let fileID: String + private let filePath: String + private let function: String + + init( + fileID: StaticString, + filePath: StaticString, + function: String + ) { + self.fileID = String(describing: fileID) + self.filePath = String(describing: filePath) + self.function = function + } + } + + fileprivate struct TestPosition: Hashable { + + private let line: UInt + private let column: UInt + + init(line: UInt, column: UInt) { + self.line = line + self.column = column + } + } +} diff --git a/Sources/XCSnapshotTesting/Async.swift b/Sources/XCSnapshotTesting/Async.swift new file mode 100644 index 000000000..9f7394bf9 --- /dev/null +++ b/Sources/XCSnapshotTesting/Async.swift @@ -0,0 +1,127 @@ +import Foundation + +/// A wrapper for asynchronous operations that allows composing and transforming asynchronous workflows with input and output values. +/// +/// `Async` provides a functional interface for executing asynchronous tasks, with support for mapping outputs, pulling back inputs, and inserting delays. +/// +/// - Parameters: +/// - Input: The type of input value the async operation accepts. +/// - Output: The type of output value the async operation produces. +public struct Async: SnapshotExecutor { + + fileprivate let block: @Sendable (Input) async throws -> Output + + /// Initializes an `Async` instance with a specific input type and block. + /// + /// - Parameters: + /// - inputType: The type of input value. This is inferred from the block if not explicitly provided. + /// - block: An asynchronous closure that takes an input value and returns an output value, potentially throwing an error. + public init( + _ inputType: Input.Type = Input.self, + _ block: @escaping @Sendable (Input) async throws -> Output + ) { + self.block = block + } + + /// Executes the asynchronous operation with the provided input value. + /// + /// - Parameter input: The input value to pass to the asynchronous operation. + /// - Returns: The output value produced by the operation. + /// - Throws: Any error thrown by the asynchronous operation. + public func callAsFunction(_ input: Input) async throws -> Output { + try await block(input) + } +} + +extension Async { + + /// Transforms the output of the asynchronous operation using a new closure. + /// + /// - Parameter block: A closure that takes the original output and returns a new value of type `NewOutput`. + /// - Returns: A new `Async` instance that applies this transformation. + public func map( + _ block: @escaping @Sendable (Output) async throws -> NewOutput + ) -> Async { + .init { input in + let output = try await self(input) + return try await block(output) + } + } + + /// Transforms the input of the asynchronous operation using a new closure. + /// + /// - Parameter block: A closure that takes a new input value and returns a value of type `Input` to pass to this operation. + /// - Returns: A new `Async` instance that applies this input transformation. + public func pullback( + _ block: @escaping @Sendable (NewInput) async throws -> Input + ) -> Async { + .init { newInput in + let input = try await block(newInput) + return try await self(input) + } + } +} + +extension Async { + + /// Adds a delay before completing the asynchronous operation. + /// + /// - Parameter duration: The duration of the delay in nanoseconds. + /// - Returns: A new `Async` instance with the delay applied. + public func sleep(nanoseconds duration: UInt64) -> Async { + guard duration > .zero else { + return self + } + + return map { + try await Task.sleep(nanoseconds: duration) + return $0 + } + } + + /// Adds a delay until a specific deadline before completing the asynchronous operation. + /// + /// - Parameters: + /// - deadline: The instant at which the delay should end. + /// - tolerance: The allowed tolerance for the deadline. + /// - clock: The clock to use for measuring time. + /// - Returns: A new `Async` instance with the delay applied. + @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) + public func sleep( + until deadline: C.Instant, + tolerance: C.Instant.Duration? = nil, + clock: C = .continuous + ) -> Async where C: Clock { + map { + try await Task.sleep( + until: deadline, + tolerance: tolerance, + clock: clock + ) + return $0 + } + } + + /// Adds a delay for a specific duration before completing the asynchronous operation. + /// + /// - Parameters: + /// - duration: The duration of the delay. + /// - tolerance: The allowed tolerance for the duration. + /// - clock: The clock to use for measuring time. + /// - Returns: A new `Async` instance with the delay applied. + @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) + public func sleep( + for duration: C.Instant.Duration, + tolerance: C.Instant.Duration? = nil, + clock: C = .continuous + ) -> Async where C: Clock { + map { + try await Task.sleep( + for: duration, + tolerance: tolerance, + clock: clock + ) + return $0 + } + } +} diff --git a/Sources/XCSnapshotTesting/Async/AsyncLock.swift b/Sources/XCSnapshotTesting/Async/AsyncLock.swift new file mode 100644 index 000000000..255570b7d --- /dev/null +++ b/Sources/XCSnapshotTesting/Async/AsyncLock.swift @@ -0,0 +1,62 @@ +import Foundation + +actor AsyncLock { + + private var isLocked = false + private var pendingOperations = [AsyncOperation]() + + init() {} + + func withLock(_ block: @Sendable () async throws -> Value) async throws -> Value { + try await lock() + defer { unlock() } + + return try await block() + } + + func withLockVoid(_ block: @Sendable () async throws -> Void) async throws { + try await lock() + defer { unlock() } + + try await block() + } + + func lock() async throws { + guard isLocked else { + isLocked = true + return + } + + let operation = AsyncOperation() + + try await withTaskCancellationHandler { [weak operation] in + try await withUnsafeThrowingContinuation { + guard let operation else { + return + } + + operation.schedule($0) + pendingOperations.insert(operation, at: .zero) + } + } onCancel: { + operation.cancelled() + } + } + + func unlock() { + guard isLocked else { + return + } + + let continuation = pendingOperations.popLast() + isLocked = !pendingOperations.isEmpty + + continuation?.resume() + } + + deinit { + for operation in pendingOperations { + operation.dispose() + } + } +} diff --git a/Sources/XCSnapshotTesting/Async/AsyncOperation.swift b/Sources/XCSnapshotTesting/Async/AsyncOperation.swift new file mode 100644 index 000000000..7abb40165 --- /dev/null +++ b/Sources/XCSnapshotTesting/Async/AsyncOperation.swift @@ -0,0 +1,45 @@ +import Foundation + +final class AsyncOperation: @unchecked Sendable { + + private enum State { + case idle + case scheduled(UnsafeContinuation) + case cancelled + } + + private let lock = NSLock() + private var _state: State = .idle + + init() {} + + func schedule(_ continuation: UnsafeContinuation) { + lock.withLock { + if case .idle = _state { + _state = .scheduled(continuation) + } + } + } + + func resume() { + lock.withLock { + if case .scheduled(let continuation) = _state { + continuation.resume() + } + } + } + + func cancelled() { + lock.withLock { + _state = .cancelled + } + } + + func dispose() { + lock.withLock { + if case .scheduled(let continuation) = _state { + continuation.resume(throwing: CancellationError()) + } + } + } +} diff --git a/Sources/XCSnapshotTesting/Async/AsyncSignal.swift b/Sources/XCSnapshotTesting/Async/AsyncSignal.swift new file mode 100644 index 000000000..869e58996 --- /dev/null +++ b/Sources/XCSnapshotTesting/Async/AsyncSignal.swift @@ -0,0 +1,50 @@ +import Foundation + +actor AsyncSignal { + + private var isLocked: Bool + private var pendingOperations = [AsyncOperation]() + + init(_ locked: Bool = true) { + isLocked = locked + } + + func lock() { + isLocked = true + } + + func wait() async throws { + guard isLocked else { + return + } + + let operation = AsyncOperation() + + try await withTaskCancellationHandler { [weak operation] in + try await withUnsafeThrowingContinuation { + guard let operation else { + return + } + + operation.schedule($0) + pendingOperations.insert(operation, at: .zero) + } + } onCancel: { + operation.cancelled() + } + } + + func signal() { + isLocked = false + + while let operation = pendingOperations.popLast() { + operation.resume() + } + } + + deinit { + for operation in pendingOperations { + operation.dispose() + } + } +} diff --git a/Sources/XCSnapshotTesting/Diff/Defaults/DataDiffAttachmentGenerator.swift b/Sources/XCSnapshotTesting/Diff/Defaults/DataDiffAttachmentGenerator.swift new file mode 100644 index 000000000..2b1ec8ea6 --- /dev/null +++ b/Sources/XCSnapshotTesting/Diff/Defaults/DataDiffAttachmentGenerator.swift @@ -0,0 +1,70 @@ +import Foundation + +/// A diff attachment generator that compares two data blobs (typically images or binary snapshots) and generates an attachment describing any differences. +/// +/// `DataDiffAttachmentGenerator` is designed for use in snapshot or data comparison tests. When invoked, it compares the raw values of two `DataBytes` inputs (such as reference and generated images). +/// +/// If the data do not match, it returns a `DiffAttachment` containing a message indicating the mismatch. If the data are identical, it returns `nil`. +/// +/// This type is intended for use with snapshot testing frameworks and utilities that display or log detailed differences between binary artifacts such as images. +/// +/// Usage: +/// ```swift +/// let diff = DataDiffAttachmentGenerator() +/// let result = diff(from: referenceData, with: newData) +/// // Use `result` to inspect or report differences. +/// ``` +/// +/// - Note: This implementation does not attempt to visualize binary differences; it only reports on mismatches and provides a simple message. +/// - SeeAlso: `DiffAttachment`, `DiffAttachmentGenerator` +public struct DataDiffAttachmentGenerator: DiffAttachmentGenerator { + + public init() {} + + /// Compares two data blobs and generates a diff attachment describing any differences. + /// + /// - Parameters: + /// - reference: The reference data against which to compare (typically the baseline or "golden" data). + /// - diffable: The data to compare against the reference (typically the newly generated data). + /// - Returns: A `DiffAttachment` containing a message describing the difference if the data do not match, or `nil` if the data are identical. + /// + /// This method is intended for snapshot or binary comparison testing, where two data blobs—such as images or other binary artifacts—need to be compared for equality. + /// When the data differ, the returned `DiffAttachment` includes a brief message describing the mismatch. + /// No visual diff or binary details are attached; the result is meant for simple reporting of mismatches. + public func callAsFunction( + from reference: DataBytes, + with diffable: DataBytes + ) -> DiffAttachment? { + guard reference.rawValue != diffable.rawValue else { + return nil + } + + let message = + reference.rawValue.count == diffable.rawValue.count + ? "Expected data to match" + : "Expected \(diffable.rawValue) to match \(reference.rawValue)" + + return DiffAttachment( + message: message, + attachments: [] + ) + } +} + +extension DiffAttachmentGenerator where Self == DataDiffAttachmentGenerator { + + /// A convenience static property for accessing the standard data diff attachment generator. + /// + /// Use this property to obtain a `DataDiffAttachmentGenerator`, which compares two data blobs (such as images or binary files) and generates a diff attachment if they do not match. + /// + /// Example usage: + /// ```swift + /// let generator = DiffAttachmentGenerator.data + /// let diff = generator(from: referenceData, with: newData) + /// ``` + /// + /// - Returns: A `DataDiffAttachmentGenerator` instance for performing data comparisons in tests. + public static var data: Self { + DataDiffAttachmentGenerator() + } +} diff --git a/Sources/XCSnapshotTesting/Diff/Defaults/ImageDiffAttachmentGenerator.swift b/Sources/XCSnapshotTesting/Diff/Defaults/ImageDiffAttachmentGenerator.swift new file mode 100644 index 000000000..a8abb63ce --- /dev/null +++ b/Sources/XCSnapshotTesting/Diff/Defaults/ImageDiffAttachmentGenerator.swift @@ -0,0 +1,101 @@ +#if os(iOS) || os(tvOS) || os(watchOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +/// `ImageDiffAttachmentGenerator` is a utility for visual snapshot testing that highlights and reports +/// differences between two images, usually representing the expected (reference) and actual (diffable) output +/// from a UI or graphics test. +/// +/// This generator compares images at the pixel level and, if differences are detected beyond the specified +/// thresholds, produces both textual descriptions of the differences and attachments that visualize them +/// (such as a difference image and annotated copies of the input images). +/// +/// - Parameters: +/// - precision: The strictness of the pixel-wise comparison. A value of `1.0` requires identical pixels, +/// while lower values allow for small variations. +/// - perceptualPrecision: The threshold for color difference when comparing images perceptually. This allows +/// for tolerant or "fuzzy" comparisons that are less strict about exact color matching, useful +/// when minor rendering differences are acceptable. +/// +/// On difference detection, the generator outputs a `DiffAttachment` which includes: +/// - A message describing the type and severity of the difference. +/// - Attachments: Visual representations of the reference image, the diffable image (or a placeholder if empty), +/// and a difference image highlighting discrepancies. +/// +/// This type is commonly used in snapshot and UI regression testing suites to make visual regressions +/// easy to spot and diagnose during continuous integration or local development. +public struct ImageDiffAttachmentGenerator: DiffAttachmentGenerator { + + private let precision: Float + private let perceptualPrecision: Float + + /// Initializes a new `ImageDiffAttachmentGenerator` with the specified comparison thresholds. + /// + /// - Parameters: + /// - precision: The pixel-wise comparison threshold. A value of `1.0` requires exact pixel matches, + /// while lower values allow minor variations, making the comparison less strict. + /// - perceptualPrecision: The color difference threshold for perceptual (fuzzy) image comparisons. + /// Lower values make the comparison more tolerant of small color variations, which + /// is useful for ignoring minor rendering differences that are not visually significant. + /// + /// Use this initializer to specify how strict or lenient the image difference detection should be, + /// enabling customized snapshot test sensitivity. + public init( + precision: Float, + perceptualPrecision: Float + ) { + self.precision = precision + self.perceptualPrecision = perceptualPrecision + } + + /// Compares two images and generates a `DiffAttachment` if significant visual differences are detected. + /// + /// This function performs a pixel-wise and perceptual comparison between a reference image and a diffable (test) image. + /// If the differences between the two images exceed the configured `precision` or `perceptualPrecision` thresholds, + /// the function produces a descriptive message and attachments, including: + /// - The original reference image. + /// - The diffable (test) image or, if it is empty, a placeholder. + /// - A difference image that highlights detected discrepancies. + /// + /// Use this function in visual regression or snapshot testing to identify and visualize unintended UI changes. + /// + /// - Parameters: + /// - reference: The known-correct (reference) image to compare against. + /// - diffable: The image under test, to be compared to the reference. + /// - Returns: A `DiffAttachment` containing a textual difference summary and visual attachments if a significant + /// difference is found; otherwise, returns `nil` if the images are considered equivalent. + public func callAsFunction( + from reference: ImageBytes, + with diffable: ImageBytes + ) -> DiffAttachment? { + performOnMainThread { + guard + let message = reference.rawValue.compare( + diffable.rawValue, + precision: precision, + perceptualPrecision: perceptualPrecision + ) + else { return nil } + + let difference = reference.rawValue.substract(diffable.rawValue) + var oldAttachment = SnapshotAttachment(image: reference.rawValue) + oldAttachment?.name = "reference" + let isEmptyImage = diffable.rawValue.size == .zero + var newAttachment = SnapshotAttachment( + image: isEmptyImage ? SDKImage.empty : diffable.rawValue + ) + newAttachment?.name = "failure" + var differenceAttachment = SnapshotAttachment(image: difference) + differenceAttachment?.name = "difference" + + return DiffAttachment( + message: message, + attachments: [oldAttachment, newAttachment, differenceAttachment].compactMap(\.self) + ) + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Diff/Defaults/StringDiffAttachmentGenerator.swift b/Sources/XCSnapshotTesting/Diff/Defaults/StringDiffAttachmentGenerator.swift new file mode 100644 index 000000000..71614bb9b --- /dev/null +++ b/Sources/XCSnapshotTesting/Diff/Defaults/StringDiffAttachmentGenerator.swift @@ -0,0 +1,79 @@ +import Foundation + +/// A diff attachment generator that compares two UTF-8 strings line-by-line and produces a patch-style text diff +/// as an `SnapshotAttachment`. Calculates differences between collections using the Longest Common +/// Subsequence (LCS) algorithm. +/// +/// `StringDiffAttachmentGenerator` implements the `DiffAttachmentGenerator` protocol, generating +/// a unified diff between a reference and a diffable string, each represented as a `StringBytes` value. +/// If the contents are identical, it returns `nil`. Otherwise, it produces a patch-like message +/// and an attachment with UTI "public.patch-file" that can be used to present rich diffs in test failures. +/// +/// Typical usage is through `DiffAttachmentGenerator.lines`, which instantiates this type. +/// +/// - Warning: The generator operates on lines, splitting both reference and diffable values on newlines, +/// and therefore only highlights differences at the line level. It does not perform word- or character-level diffs. +/// +/// - Note: This is particularly useful for snapshot and golden file tests where readable, actionable diffs +/// are important to developers. +public struct StringDiffAttachmentGenerator: DiffAttachmentGenerator { + + /// Creates a new instance of `StringDiffAttachmentGenerator`. + /// + /// Use this initializer to construct a diff generator that produces unified + /// patch-style attachments for line-by-line differences between two strings. + public init() {} + + /// Compares two UTF-8 string values line-by-line and generates a unified patch-style diff attachment if differences are found. + /// + /// This function splits both the `reference` and `diffable` string values into lines, + /// computes their differences using the Longest Common Subsequence (LCS) algorithm, + /// and constructs a patch-formatted message representing the changes. If the strings are identical, + /// the function returns `nil`. Otherwise, it returns a `DiffAttachment` containing a summary message and + /// a patch file attachment suitable for rich diff presentation. + /// + /// - Parameters: + /// - reference: The baseline value, typically representing the expected or golden UTF-8 string. + /// - diffable: The actual UTF-8 string to compare against the reference. + /// - Returns: A `DiffAttachment` with a patch-style message and attachment, or `nil` if no differences exist. + public func callAsFunction( + from reference: StringBytes, + with diffable: StringBytes + ) -> DiffAttachment? { + guard reference.rawValue != diffable.rawValue else { + return nil + } + + let hunks = reference.rawValue + .split(separator: "\n", omittingEmptySubsequences: false) + .map(String.init) + .diffing( + diffable.rawValue + .split(separator: "\n", omittingEmptySubsequences: false) + .map(String.init) + ) + .groupping() + + let failure = + hunks + .flatMap { [$0.patchMarker] + $0.lines } + .joined(separator: "\n") + + let attachment = SnapshotAttachment( + data: Data(failure.utf8), + uniformTypeIdentifier: "public.patch-file" + ) + + return DiffAttachment( + message: failure, + attachments: [attachment] + ) + } +} + +extension DiffAttachmentGenerator where Self == StringDiffAttachmentGenerator { + /// A line-diffing strategy for UTF-8 text. + public static var lines: Self { + StringDiffAttachmentGenerator() + } +} diff --git a/Sources/XCSnapshotTesting/Diff/DiffAttachmentGenerator.swift b/Sources/XCSnapshotTesting/Diff/DiffAttachmentGenerator.swift new file mode 100644 index 000000000..f01887793 --- /dev/null +++ b/Sources/XCSnapshotTesting/Diff/DiffAttachmentGenerator.swift @@ -0,0 +1,118 @@ +import Foundation + +/// Container for messages and attachments generated during snapshot comparisons. +/// +/// `DiffAttachment` provides a structured way to report differences discovered during snapshot testing. +/// It encapsulates both a textual message describing what was found (for example, "2% pixel mismatch") +/// and any visual assets (such as images, diffs, or annotated screenshots) that help illustrate the +/// discrepancies. This enables richer, more actionable feedback in snapshot test results, making it +/// easier to understand and investigate test failures. +/// +/// Typical uses include: +/// - Reporting pixel-level differences in image snapshots. +/// - Attaching diffed images or highlight overlays for visual comparison. +/// - Supplying serialized data (as files or text) showing the before and after state. +/// +/// Attachments are platform-dependent and may be rendered or linked in test reports, depending on +/// tooling support. +/// +/// Example usage: +/// ```swift +/// let attachment = DiffAttachment( +/// message: "Found color shift in bottom-right quadrant", +/// attachments: [SnapshotAttachment(image: diffImage)] +/// ) +/// ``` +public struct DiffAttachment: Sendable { + + /// Message describing the outcome of the snapshot comparison, summarizing key detected differences. + /// + /// This textual message provides actionable context for test failures, such as: + /// - The nature or extent of detected changes (e.g., "8% pixel mismatch in bottom-right region"). + /// - Additional hints or suggestions for investigation. + /// - Summaries of numerical, visual, or structural differences. + /// + /// The message should be concise yet descriptive, enabling developers to quickly understand + /// what changed and why the test failed, even without reviewing the attached visual artifacts. + /// + /// Example: "Image differs: 1,304 pixels modified (3% of total)." + public let message: String + + /// Collection of visual attachments illustrating the differences. + /// + /// This array contains instances of `SnapshotAttachment` that visually represent the discrepancies + /// between the reference and test values. Typical attachments may include images highlighting + /// regions that differ, diff overlays, annotated screenshots, or other files that provide + /// additional context for the detected changes. + /// + /// Attachments support richer test reporting by enabling quick visual inspection of what + /// changed, helping developers understand and investigate snapshot test failures more efficiently. + /// + /// Example: `[SnapshotAttachment(image: diffImage), SnapshotAttachment(image: croppedFailureRegion)]` + public let attachments: [SnapshotAttachment] + + /// Initializes a new `DiffAttachment` with a descriptive message and a collection of visual attachments. + /// + /// - Parameters: + /// - message: A concise, human-readable description summarizing the key differences found during the comparison. This message should help developers quickly understand the nature or extent of the discrepancies. + /// - attachments: An array of `SnapshotAttachment` instances that visually represent the detected differences (such as diff images, overlays, or annotated screenshots). These attachments provide additional context to aid investigation of test failures. + public init(message: String, attachments: [SnapshotAttachment]) { + self.message = message + self.attachments = attachments + } +} + +/// A protocol for generating detailed difference reports between two values during snapshot testing. +/// +/// `DiffAttachmentGenerator` enables the creation of both textual summaries and visual artifacts +/// when comparing a stored reference value (e.g., a previously recorded snapshot) to a newly produced value. +/// Conformers implement logic to highlight and explain significant discrepancies, aiding in the diagnosis +/// of test failures. +/// +/// Typical usages include generating: +/// - Human-readable messages summarizing differences (e.g., percentage of pixels mismatched). +/// - Visual attachments such as diff images, overlays, or annotated comparisons for richer test reports. +/// +/// Implementations should return `nil` when the two values are considered equivalent within the comparison criteria, +/// ensuring attachments are only created for meaningful changes. +/// +/// ## Example +/// ```swift +/// struct ImageDiffGenerator: DiffAttachmentGenerator { +/// func callAsFunction(from reference: ImageBytes, with diffable: ImageBytes) -> DiffAttachment? { +/// // Produce diff image, compare bytes, etc. +/// } +/// } +/// ``` +/// +/// - Note: The generator must be safe for concurrent use (`Sendable`) and should avoid blocking operations. +public protocol DiffAttachmentGenerator: Sendable { + + /// The type of values to compare during snapshot testing. + /// + /// `Value` represents the data being subjected to comparison by the generator. This can be any type + /// that is `Sendable`, such as images, serialized data, or complex domain-specific structures. + /// Conformers specify the actual type used for their comparison logic (e.g., `ImageBytes`, `UIView`, etc.). + /// + /// Implementations use `Value` to define the kinds of values their diffing logic handles. The generator + /// will analyze two instances of this type—the reference (such as a previously-approved snapshot) and + /// the new value (such as fresh test output)—and report differences via a `DiffAttachment`. + /// + /// - Note: `Value` must conform to `Sendable` to ensure thread-safe use during parallelized test execution. + associatedtype Value: Sendable + + /// Compares a reference value and a newly produced value, generating a detailed difference report if discrepancies are found. + /// + /// Implementers should analyze the two values and, if significant differences exist, return a `DiffAttachment` that summarizes the key findings and provides visual context (such as diff images or annotated overlays). If the values are considered equivalent according to the generator's criteria (for example, pixel-for-pixel identical for image data), the function must return `nil`, indicating no actionable differences. + /// + /// - Parameters: + /// - reference: The stored reference value, typically representing an approved or baseline snapshot. + /// - diffable: The new value to compare against the reference, produced during the current test run. + /// - Returns: A `DiffAttachment` encapsulating a human-readable message and any relevant visual attachments if significant differences are found, or `nil` if the values are considered equivalent. + /// + /// - Note: This method should be fast and thread-safe. It must not block on I/O or lengthy computations, and must be safe for concurrent use across multiple test invocations. + func callAsFunction( + from reference: Value, + with diffable: Value + ) -> DiffAttachment? +} diff --git a/Sources/XCSnapshotTesting/Diff/DiffHunk.swift b/Sources/XCSnapshotTesting/Diff/DiffHunk.swift new file mode 100644 index 000000000..9aad97277 --- /dev/null +++ b/Sources/XCSnapshotTesting/Diff/DiffHunk.swift @@ -0,0 +1,53 @@ +import Foundation + +// MARK: - Formatted Change Representation +/// Represents a group of changes (hunk) in patch format. +struct DiffHunk { + /// Start index in the first collection. + let firstStart: Int + + /// Number of lines in the first collection. + let firstLength: Int + + /// Start index in the second collection. + let secondStart: Int + + /// Number of lines in the second collection. + let secondLength: Int + + /// Formatted lines with change indicators. + let lines: [String] + + /// Generates the hunk header in patch format. + var patchMarker: String { + let firstMarker = "−\(firstStart + 1),\(firstLength)" + let secondMarker = "+\(secondStart + 1),\(secondLength)" + return "@@ \(firstMarker) \(secondMarker) @@" + } + + /// Combines two hunks into one. + static func + (lhs: DiffHunk, rhs: DiffHunk) -> DiffHunk { + DiffHunk( + firstStart: lhs.firstStart, + firstLength: lhs.firstLength + rhs.firstLength, + secondStart: lhs.secondStart, + secondLength: lhs.secondLength + rhs.secondLength, + lines: lhs.lines + rhs.lines + ) + } + + /// Default initializer with default values. + init( + firstStart: Int = 0, + firstLength: Int = 0, + secondStart: Int = 0, + secondLength: Int = 0, + lines: [String] = [] + ) { + self.firstStart = firstStart + self.firstLength = firstLength + self.secondStart = secondStart + self.secondLength = secondLength + self.lines = lines + } +} diff --git a/Sources/XCSnapshotTesting/Diff/DiffTool.swift b/Sources/XCSnapshotTesting/Diff/DiffTool.swift new file mode 100644 index 000000000..fdb68ad2b --- /dev/null +++ b/Sources/XCSnapshotTesting/Diff/DiffTool.swift @@ -0,0 +1,162 @@ +import Foundation + +/// A formatter for generating snapshot comparison messages in the console or for use with external diff tools. +/// +/// `DiffTool` provides a flexible way to format output displayed in the Xcode console when a snapshot comparison fails. +/// This output may consist of: +/// - Human-readable error messages (e.g., `.default`) +/// - Shell commands to launch external comparison tools (e.g., `.ksdiff` for Kaleidoscope) +/// +/// The generated output is **not automatically executed** by the library. Developers or continuous integration systems +/// may process it manually, such as by copying and pasting commands into a terminal or automating with scripts. +/// +/// ## Usage +/// You can use one of the built-in presets, such as `.default` or `.ksdiff`, or provide a custom formatter, +/// either as a closure or string literal. For example: +/// +/// ```swift +/// let customTool: DiffTool = { ref, actual in +/// "diff \"\(ref)\" \"\(actual)\"" +/// } +/// +/// let literalTool: DiffTool = "open -a Kaleidoscope" +/// ``` +/// +/// To configure the diff tool for tests, use `withTestingEnvironment(diffTool: ...)` or specify it through Swift Testing traits. +/// +/// ## Thread Safety +/// `DiffTool` is `Sendable` and safe to use from concurrent test runners. +/// +/// ## Configuration via Swift Testing Traits +/// The diff tool can also be configured using Swift Testing traits, allowing you to set it at a project or target level +/// without modifying individual test files. +/// +/// ## See Also +/// - ``withTestingEnvironment(record:diffTool:maxConcurrentTests:platform:operation:file:line:)`` +/// - ``DiffTool/ksdiff`` +/// - ``DiffTool/default`` +/// +/// - Note: The formatting closure is always given two absolute file paths: the reference (current/expected) file, and the failed (actual/current) file. +public struct DiffTool: Sendable, ExpressibleByStringLiteral { + + /// Formats output for [Kaleidoscope](http://kaleidoscope.app). + /// + /// Generates a shell command that can be executed externally to open Kaleidoscope, + /// comparing the two file paths provided. + /// + /// - Example output: + /// ```bash + /// ksdiff "/path/reference-file.png" "/path/failed-file.png" + /// ``` + /// + /// - WARNING: Requires Kaleidoscope to be installed and the `ksdiff` command-line tool to be available. + /// - Parameters: + /// - currentFilePath: The path to the reference (expected) file. + /// - failedFilePath: The path to the failed (actual) file. + /// - Returns: A shell command string that can be executed to launch Kaleidoscope for file comparison. + public static let ksdiff = Self { + "ksdiff \"\($0)\" \"\($1)\"" + } + + /// Default format (human-readable error in console). + /// + /// Generates a message guiding developers to configure an advanced diff tool for more interactive file comparison. + /// + /// - Output Example: + /// ```plaintext + /// @− + /// "/path/to/reference.png" + /// @+ + /// "/path/to/failed.png" + /// + /// To configure output for a custom diff tool, use 'withTestingEnvironment'. For example: + /// + /// withTestingEnvironment(diffTool: .ksdiff) { + /// // ... + /// } + /// ``` + /// + /// - Use Cases: + /// - Suitable for CI environments or when no external diff tool is available. + /// - Provides clear next steps for developers to configure more advanced comparison tools. + /// - SeeAlso: ``DiffTool/ksdiff``, ``withTestingEnvironment(record:diffTool:maxConcurrentTests:platform:operation:file:line:)`` + public static let `default` = Self { + """ + @− + "\($0)" + @+ + "\($1)" + + To configure output for a custom diff tool, use 'withTestingEnvironment'. For example: + + withTestingEnvironment(diffTool: .ksdiff) { + // ... + } + """ + } + + private var tool: @Sendable (_ currentFilePath: String, _ failedFilePath: String) -> String + + /// Initializes a new `DiffTool` with a custom formatting closure. + /// + /// Use this initializer to define how snapshot difference output is generated. The closure receives the absolute + /// file paths of the reference ("current") and failed ("actual") files, and returns a string that will be shown + /// in the console or passed to external processes. + /// + /// Example usage: + /// ```swift + /// let customDiff = DiffTool { ref, actual in + /// "diff \"\(ref)\" \"\(actual)\"" + /// } + /// ``` + /// + /// - Parameter tool: + /// A closure that formats the output for failed snapshot comparisons. + /// - `currentFilePath`: The absolute path to the reference (expected) file. + /// - `failedFilePath`: The absolute path to the failed (actual) file. + /// - Returns: The formatted string to display or process. + /// + /// - Note: The closure must be `Sendable` to ensure thread safety during concurrent test execution. + public init( + _ tool: @escaping @Sendable (_ currentFilePath: String, _ failedFilePath: String) -> String + ) { + self.tool = tool + } + + /// Initializes the tool from a string literal. + /// + /// - Parameter value: Text or command formatted with file paths. `$0` and `$1` are replaced by the reference and failed file paths, respectively. If `$0` or `$0` aren't provided, it'll be added at the end of string separated with space. + /// + /// - Example: `DiffTool("open -a Kaleidoscope $0 $1")` generates `open -a Kaleidoscope /path1 /path2`. + /// + /// - Note: The string is evaluated as a command to be executed externally. Ensure proper formatting for your shell or environment. + public init(stringLiteral value: StringLiteralType) { + self.tool = { + if value.contains("$0") || value.contains("$1") { + return + value + .replacingOccurrences(of: "$0", with: $0) + .replacingOccurrences(of: "$1", with: $1) + } else { + return "\(value) \($0) \($1)" + } + } + } + + /// Generates the comparison output. + /// + /// - Parameters: + /// - currentFilePath: Path to the reference file. + /// - failedFilePath: Path to the compared file. + /// - Returns: A string representing the formatted comparison output, which may include: + /// - Human-readable error messages (e.g., for `.default`) + /// - Shell commands for external diff tools (e.g., `.ksdiff`) + /// - Custom-format strings defined by the user + /// + /// - Note: The output is intended for display in the Xcode console or Terminal. It can be + /// manually executed or processed by scripts, but the library itself does not + /// execute the generated commands. + public func callAsFunction(currentFilePath: String, failedFilePath: String) -> String { + self.tool(currentFilePath, failedFilePath) + } +} diff --git a/Sources/XCSnapshotTesting/Diff/Difference.swift b/Sources/XCSnapshotTesting/Diff/Difference.swift new file mode 100644 index 000000000..a52394aba --- /dev/null +++ b/Sources/XCSnapshotTesting/Diff/Difference.swift @@ -0,0 +1,189 @@ +import Foundation + +// MARK: - Difference Structure +/// Represents a difference between two collections of elements. +struct Difference { + /// Origin of elements in the comparison + enum Origin { + case first // Element unique to the first collection + case second // Element unique to the second collection + case common // Element present in both collections + } + + /// Elements involved in the difference + let elements: [Element] + /// Origin of elements (first collection, second collection, or common) + let origin: Origin +} + +extension Array where Element: Hashable { + + // MARK: - Main Comparison Function + /// Calculates differences between collections using the Longest Common Subsequence (LCS) algorithm. + /// - Parameters: + /// - first: First collection to compare + /// - second: Second collection to compare + /// - Returns: List of identified differences + func diffing(_ other: [Element]) -> [Difference] { + // 1. Maps element indices from first collection + var elementIndices = [Element: [Int]]() + for (index, element) in enumerated() { + elementIndices[element, default: []].append(index) + } + + // 2. Finds Longest Common Subsequence (LCS) + var longestSubsequence = ( + overlap: [Int: Int](), // Overlap table + firstIndex: 0, // Start index in first collection + secondIndex: 0, // Start index in second collection + length: 0 // Subsequence length + ) + + // Iterates through second collection to find matches + for pair in other.enumerated() { + guard let indices = elementIndices[pair.element] else { continue } + + for firstIndex in indices { + let currentLength = (longestSubsequence.overlap[firstIndex - 1] ?? 0) + 1 + var newOverlap = longestSubsequence.overlap + newOverlap[firstIndex] = currentLength + + // Updates longest subsequence found + if currentLength > longestSubsequence.length { + longestSubsequence.overlap = newOverlap + longestSubsequence.firstIndex = firstIndex - currentLength + 1 + longestSubsequence.secondIndex = pair.offset - currentLength + 1 + longestSubsequence.length = currentLength + } + } + } + + // 3. No common subsequence case + guard longestSubsequence.length > 0 else { + return [ + Difference(elements: self, origin: .first), + Difference(elements: other, origin: .second), + ].filter { !$0.elements.isEmpty } + } + + // 4. Splits collections into parts for recursive analysis + let (firstPart, secondPart) = ( + Array(self.prefix(upTo: longestSubsequence.firstIndex)), + Array(other.prefix(upTo: longestSubsequence.secondIndex)) + ) + + let (firstRemainder, secondRemainder) = ( + Array(self.suffix(from: longestSubsequence.firstIndex + longestSubsequence.length)), + Array(other.suffix(from: longestSubsequence.secondIndex + longestSubsequence.length)) + ) + + // 5. Combines results from analyzed parts recursively + return firstPart.diffing(secondPart) + + [ + Difference( + elements: Array( + self[ + longestSubsequence.firstIndex..] { + + func groupping(context: Int = 4) -> [DiffHunk] { + let figureSpace = "\u{2007}" // Figure space (for alignment) + + // Processes each difference and groups into hunks + let (finalHunk, hunks) = reduce(into: (current: DiffHunk(), hunks: [DiffHunk]())) { + state, + diff in + + let count = diff.elements.count + + switch diff.origin { + // Case: Common elements with large context + case .common where count > context * 2: + let prefixLines = diff.elements.prefix(context).map(addPrefix(figureSpace)) + let suffixLines = diff.elements.suffix(context).map(addPrefix(figureSpace)) + + let newHunk = + state.current + + DiffHunk( + firstLength: context, + secondLength: context, + lines: prefixLines + ) + + state.current = DiffHunk( + firstStart: state.current.firstStart + state.current.firstLength + count - context, + firstLength: context, + secondStart: state.current.secondStart + state.current.secondLength + count - context, + secondLength: context, + lines: suffixLines + ) + + // Adds previous hunk if it contains changes + if newHunk.lines.contains(where: { $0.hasPrefix("−") || $0.hasPrefix("+") }) { + state.hunks.append(newHunk) + } + + // Case: Common elements with empty hunk + case .common where state.current.lines.isEmpty: + let suffixLines = diff.elements.suffix(context).map(addPrefix(figureSpace)) + state.current = + state.current + + DiffHunk( + firstStart: count - suffixLines.count, + firstLength: suffixLines.count, + secondStart: count - suffixLines.count, + secondLength: suffixLines.count, + lines: suffixLines + ) + + // Case: Normal common elements + case .common: + let lines = diff.elements.map(addPrefix(figureSpace)) + state.current = + state.current + + DiffHunk( + firstLength: count, + secondLength: count, + lines: lines + ) + + // Case: Removals (first collection elements) + case .first: + state.current = + state.current + + DiffHunk( + firstLength: count, + lines: diff.elements.map(addPrefix("−")) + ) + + // Case: Additions (second collection elements) + case .second: + state.current = + state.current + + DiffHunk( + secondLength: count, + lines: diff.elements.map(addPrefix("+")) + ) + } + } + + // Returns accumulated hunks + final hunk (if valid) + return finalHunk.lines.isEmpty ? hunks : hunks + [finalHunk] + } +} + +// MARK: - Contextual Change Grouping +/// Helper function to add line prefixes +private func addPrefix(_ prefix: String) -> (String) -> String { + { "\(prefix)\($0)\($0.hasSuffix(" ") ? "¬" : "")" } +} diff --git a/Sources/XCSnapshotTesting/Environment/Defaults/DiffToolEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/Defaults/DiffToolEnvironmentKey.swift new file mode 100644 index 000000000..256d6bc9e --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/Defaults/DiffToolEnvironmentKey.swift @@ -0,0 +1,46 @@ +import Foundation + +private struct DiffToolEnvironmentKey: SnapshotEnvironmentKey { + + static var defaultValue: DiffTool { + TestingSystem.shared.environment?.diffTool ?? .default + } +} + +extension SnapshotEnvironmentValues { + + /// The `diffTool` property provides access to the current diff tool configuration within the snapshot testing environment. + /// + /// This property allows you to specify or retrieve the tool used for visual comparisons when snapshot tests fail. + /// It can be accessed globally through `SnapshotEnvironment.current.diffTool`. + /// + /// The value defaults to `.default`, which provides a basic textual comparison output in the Xcode console. + /// You can change this value using: + /// - `withTestingEnvironment(diffTool:operation:)` + /// - Swift Testing framework attributes + /// + /// Available diff tools include: + /// - `.default`: Basic console output with instructions for configuring advanced diff tools + /// - `.ksdiff`: Launches Kaleidoscope for visual comparison (requires Kaleidoscope installation) + /// + /// ```swift + /// // Accessing the current diff tool + /// let currentTool = SnapshotEnvironment.current.diffTool + /// + /// // Configuring a custom diff tool + /// withTestingEnvironment { + /// $0.diffTool = .ksdiff + /// } operation: { + /// // Your testing code here + /// } + /// ``` + /// + /// - Note: The diff tool is only used when snapshot comparisons fail and a visual diff is needed. + /// - SeeAlso: + /// - ``DiffTool`` + /// - ``withTestingEnvironment(record:diffTool:maxConcurrentTests:platform:operation:file:line:)`` + public var diffTool: DiffTool { + get { self[DiffToolEnvironmentKey.self] } + set { self[DiffToolEnvironmentKey.self] = newValue } + } +} diff --git a/Sources/XCSnapshotTesting/Environment/Defaults/DisableInconsistentTraitsCheckEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/Defaults/DisableInconsistentTraitsCheckEnvironmentKey.swift new file mode 100644 index 000000000..8f95c3948 --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/Defaults/DisableInconsistentTraitsCheckEnvironmentKey.swift @@ -0,0 +1,13 @@ +import Foundation + +private struct DisableInconsistentTraitsCheckerEnvironmentKey: SnapshotEnvironmentKey { + static let defaultValue = false +} + +extension SnapshotEnvironmentValues { + + var disableInconsistentTraitsChecker: Bool { + get { self[DisableInconsistentTraitsCheckerEnvironmentKey.self] } + set { self[DisableInconsistentTraitsCheckerEnvironmentKey.self] = newValue } + } +} diff --git a/Sources/XCSnapshotTesting/Environment/Defaults/IncludeMajorPlatformVersionInPathEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/Defaults/IncludeMajorPlatformVersionInPathEnvironmentKey.swift new file mode 100644 index 000000000..7f02dca0a --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/Defaults/IncludeMajorPlatformVersionInPathEnvironmentKey.swift @@ -0,0 +1,75 @@ +import Foundation + +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#elseif os(watchOS) +import WatchKit +#endif + +private struct IncludeMajorPlatformVersionInPathEnvironmentKey: SnapshotEnvironmentKey { + static let defaultValue = false +} + +extension SnapshotEnvironmentValues { + + /// A Boolean value that determines whether the major platform version is included in the path during snapshot testing. + /// + /// When `true`, the snapshot paths will include the major version of the platform (e.g., "v14" for iOS 14). + /// This is useful for ensuring snapshots are version-specific and avoid conflicts between different platform versions. + /// + /// - Note: The value is retrieved from the `SnapshotEnvironmentValues`: + /// - `SnapshotEnvironment.current.includeMajorPlatformVersionInPath` + /// - Or set within a testing environment closure: + /// + /// withTestingEnvironment { + /// $0.includeMajorPlatformVersionInPath = true + /// } operation: { ... } + /// + /// Platform version retrieval is handled differently across platforms: + /// - **macOS**: Parses the OS version, handling macOS 10.x specially. + /// - **iOS/tvOS/visionOS**: Uses `UIDevice.current.systemVersion` on the main thread. + /// - **watchOS**: Uses `WKInterfaceDevice.current().systemVersion` on the main thread. + /// + /// The final version string is formatted as "v\{MajorVersion}". + /// + /// ```swift + /// // Enable versioned paths + /// withTestingEnvironment { + /// $0.includeMajorPlatformVersionInPath = true + /// } operation: { + /// // Your testing code here + /// } + /// ``` + public var includeMajorPlatformVersionInPath: Bool { + get { self[IncludeMajorPlatformVersionInPathEnvironmentKey.self] } + set { self[IncludeMajorPlatformVersionInPathEnvironmentKey.self] = newValue } + } + + var platformVersion: String? { + guard includeMajorPlatformVersionInPath, !platform.isEmpty else { + return nil + } + + let majorVersion: String? + #if os(macOS) + if ProcessInfo.processInfo.operatingSystemVersion.majorVersion == 10 { + let systemVersion = ProcessInfo.processInfo.operatingSystemVersion + majorVersion = "\(systemVersion.majorVersion).\(systemVersion.minorVersion)" + } else { + majorVersion = String(ProcessInfo.processInfo.operatingSystemVersion.majorVersion) + } + #elseif os(iOS) || os(tvOS) || os(visionOS) + majorVersion = performOnMainThread { + UIDevice.current.systemVersion.split(separator: ".").first.map(String.init) + } + #elseif os(watchOS) + majorVersion = performOnMainThread { + WKInterfaceDevice.current().systemVersion.split(separator: ".").first.map(String.init) + } + #else + majorVersion = String(ProcessInfo.processInfo.operatingSystemVersion.majorVersion) + #endif + + return majorVersion.map { "v\($0)" } + } +} diff --git a/Sources/XCSnapshotTesting/Environment/Defaults/MaxConcurrentTestsEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/Defaults/MaxConcurrentTestsEnvironmentKey.swift new file mode 100644 index 000000000..6c2f2a30e --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/Defaults/MaxConcurrentTestsEnvironmentKey.swift @@ -0,0 +1,42 @@ +import Foundation + +private struct MaxConcurrentTestsEnvironmentKey: SnapshotEnvironmentKey { + + static var defaultValue: Int { + TestingSystem.shared.environment?.maxConcurrentTests ?? 3 + } +} + +extension SnapshotEnvironmentValues { + + /// The `maxConcurrentTests` property defines the maximum number of concurrent tests allowed during UI testing. + /// + /// This property helps prevent device overload and potential capture errors caused by stress on UIKit, SwiftUI, and AppKit frameworks. + /// It limits the number of `UIWindow` (iOS, tvOS, visionOS) or `NSWindow` (macOS) instances allocated simultaneously during testing. + /// + /// The value can be accessed via `SnapshotEnvironment.current.maxConcurrentTests`. + /// + /// Default value is `3`. You can modify this value through: + /// - `withTestingEnvironment(maxConcurrentTests:operation:)` + /// - Swift Testing framework attributes + /// + /// ```swift + /// // Increase concurrent tests limit + /// withTestingEnvironment { + /// $0.maxConcurrentTests = 5 + /// } operation: { + /// // Your testing code here + /// } + /// ``` + /// + /// - Note: This property is specifically applicable to UI tests and helps maintain testing stability on resource-constrained devices. + /// - SeeAlso: + /// - ``withTestingEnvironment(record:diffTool:maxConcurrentTests:platform:operation:file:line:)`` + public var maxConcurrentTests: Int { + get { self[MaxConcurrentTestsEnvironmentKey.self] } + set { + precondition(newValue >= 1) + self[MaxConcurrentTestsEnvironmentKey.self] = newValue + } + } +} diff --git a/Sources/XCSnapshotTesting/Environment/Defaults/PlatformEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/Defaults/PlatformEnvironmentKey.swift new file mode 100644 index 000000000..afb22f5d1 --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/Defaults/PlatformEnvironmentKey.swift @@ -0,0 +1,79 @@ +import Foundation + +#if os(iOS) +import UIKit +#endif + +private struct PlatformEnvironmentKey: SnapshotEnvironmentKey { + + static var defaultValue: String { + TestingSystem.shared.environment?.platform ?? operatingSystemName() + } + + private static func operatingSystemName() -> String { + #if os(macOS) + return "macOS" + #elseif os(iOS) + #if targetEnvironment(macCatalyst) + return "macCatalyst" + #else + return performOnMainThread { + if UIDevice.current.userInterfaceIdiom == .pad { + return "iPadOS" + } else { + return "iOS" + } + } + #endif + #elseif os(tvOS) + return "tvOS" + #elseif os(watchOS) + return "watchOS" + #elseif os(visionOS) + return "visionOS" + #elseif os(Android) + return "android" + #elseif os(Windows) + return "windows" + #elseif os(Linux) + return "linux" + #elseif os(WASI) + return "wasi" + #else + return "unknown" + #endif + } +} + +extension SnapshotEnvironmentValues { + + /// The platform name used in snapshot URLs to distinguish test outputs. + /// + /// This property helps differentiate snapshots across platforms with different UI frameworks (like UIKit and SwiftUI on iOS vs. AppKit on macOS). + /// It ensures that platform-specific UI layouts do not interfere with each other during testing. + /// + /// The value defaults to the current platform (e.g., "iOS", "macOS") unless explicitly configured through: + /// - `withTestingEnvironment(platform:operation:)` + /// - Swift Testing framework traits + /// + /// Setting this to an empty string will make snapshots share the same output path without platform-specific distinction. + /// + /// Accessed via `SnapshotEnvironment.current.platform`. + /// + /// ```swift + /// // Customize platform name for snapshots + /// withTestingEnvironment { + /// $0.platform = "iOS-Simulator" + /// } operation: { + /// // Your testing code here + /// } + /// ``` + /// + /// - Note: This is particularly useful for UI snapshot testing where different platforms may have different default layouts and behaviors. + /// - SeeAlso: + /// - ``withTestingEnvironment(record:diffTool:maxConcurrentTests:platform:operation:file:line:)`` + public var platform: String { + get { self[PlatformEnvironmentKey.self] } + set { self[PlatformEnvironmentKey.self] = newValue } + } +} diff --git a/Sources/XCSnapshotTesting/Environment/Defaults/RecordEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/Defaults/RecordEnvironmentKey.swift new file mode 100644 index 000000000..1d7fa7686 --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/Defaults/RecordEnvironmentKey.swift @@ -0,0 +1,21 @@ +import Foundation + +private struct RecordEnvironmentKey: SnapshotEnvironmentKey { + + static var defaultValue: RecordMode { + TestingSystem.shared.environment?.recordMode ?? .missing + } +} + +extension SnapshotEnvironmentValues { + + /// The current record mode for snapshot testing. + /// + /// This key is used to store and retrieve the ``RecordMode`` value within the environment + /// of a snapshot test. The default value is determined by the testing system's environment, + /// falling back to `.missing` if no environment is available. + public var recordMode: RecordMode { + get { self[RecordEnvironmentKey.self] } + set { self[RecordEnvironmentKey.self] = newValue } + } +} diff --git a/Sources/XCSnapshotTesting/Environment/Defaults/TraitsEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/Defaults/TraitsEnvironmentKey.swift new file mode 100644 index 000000000..ed2ceec02 --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/Defaults/TraitsEnvironmentKey.swift @@ -0,0 +1,37 @@ +import Foundation + +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct TraitsEnvironmentKey: SnapshotEnvironmentKey { + static let defaultValue = Traits() +} + +extension SnapshotEnvironmentValues { + + /// Provides access to the `Traits` configuration for snapshot testing. + /// + /// This property allows you to customize the appearance and behavior of + /// UI elements during snapshot testing by specifying traits like + /// accessibility features, color schemes, or device characteristics. + /// + /// Example: + /// ```swift + /// // Configure traits for snapshot testing + /// withTestingEnvironment { + /// $0.traits = .init(preferredContentSizeCategory: .extraLarge) + /// } operation: { + /// // Your testing code here + /// } + /// ``` + /// + /// - Note: Traits can be combined to simulate various UI conditions during testing. + /// - SeeAlso: + /// - ``Traits`` + /// - ``withTestingEnvironment(_:operation:file:line:)`` + public var traits: Traits { + get { self[TraitsEnvironmentKey.self] } + set { self[TraitsEnvironmentKey.self] = newValue } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Environment/Defaults/WebViewToleranceEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/Defaults/WebViewToleranceEnvironmentKey.swift new file mode 100644 index 000000000..4d7822a8a --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/Defaults/WebViewToleranceEnvironmentKey.swift @@ -0,0 +1,34 @@ +import Foundation + +#if os(iOS) || os(visionOS) || os(macOS) || os(visionOS) +private struct WebViewToleranceEnvironmentKey: SnapshotEnvironmentKey { + static let defaultValue: TimeInterval = 2.5 +} + +extension SnapshotEnvironmentValues { + + /// The maximum time (in seconds) to wait for a web view to load before taking a snapshot. + /// + /// This property configures the timeout duration for web view loading during snapshot operations. + /// It helps prevent tests from failing due to network latency or complex web content loading. + /// + /// - Default: 2.5 seconds + /// - Available on: iOS, iPadOS, macOS, visionOS + /// + /// ```swift + /// // Increase web view loading timeout + /// withTestingEnvironment { + /// $0.webViewTolerance = 5.0 + /// } operation: { + /// // Your web view testing code here + /// } + /// ``` + /// + /// - Note: This setting is particularly useful when testing views containing `WKWebView` or similar web-rendering components. + /// - SeeAlso: ``withTestingEnvironment(_:operation:file:line:)`` + public var webViewTolerance: TimeInterval { + get { self[WebViewToleranceEnvironmentKey.self] } + set { self[WebViewToleranceEnvironmentKey.self] = newValue } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Environment/SnapshotEnvironment.swift b/Sources/XCSnapshotTesting/Environment/SnapshotEnvironment.swift new file mode 100644 index 000000000..344e9fb68 --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/SnapshotEnvironment.swift @@ -0,0 +1,67 @@ +import Foundation + +/// A dynamic environment container for accessing snapshot testing configuration values. +/// +/// `SnapshotEnvironment` provides a convenient way to access configuration values using Swift's `@dynamicMemberLookup` feature. +/// It allows you to retrieve values from the underlying `SnapshotEnvironmentValues` using dynamic key path lookups. +/// +/// ```swift +/// struct MyEnvironmentKey: SnapshotEnvironmentKey { +/// typealias Value = Int +/// static let defaultValue = 42 +/// } +/// +/// extension SnapshotEnvironmentValues { +/// var myEnvironment: Int { +/// get { self[MyEnvironmentKey.self] } +/// set { self[MyEnvironmentKey.self] = newValue } +/// } +/// } +/// +/// let value = SnapshotEnvironment.current.myEnvironment // results 42 +/// ``` +@dynamicMemberLookup +public struct SnapshotEnvironment: Sendable { + + /// The current snapshot environment instance available in the testing context. + public static let current = SnapshotEnvironment() + + fileprivate init() {} + + /// Provides dynamic member lookup access to values stored in `SnapshotEnvironmentValues`. + /// + /// This subscript allows you to retrieve configuration values from the snapshot testing environment + /// using key paths to properties defined in `SnapshotEnvironmentValues`. + /// + /// - Parameter keyPath: A key path to the value in `SnapshotEnvironmentValues`. + /// - Returns: The value associated with the given key path. + /// + /// Example usage: + /// ```swift + /// let currentDiffTool = SnapshotEnvironment.current.diffTool + /// let currentRecordMode = SnapshotEnvironment.current.recordMode + /// ``` + /// + /// - Note: This subscript provides type-safe access to environment values and is made possible + /// by Swift's `@dynamicMemberLookup` feature. + public subscript(dynamicMember keyPath: KeyPath) -> Value { + (SnapshotEnvironmentValues.current ?? SnapshotEnvironmentValues())[keyPath: keyPath] + } + + /// Accesses configuration values stored in `SnapshotEnvironmentValues` using keys that conform to `SnapshotEnvironmentKey`. + /// + /// This subscript allows you to retrieve values from the snapshot testing environment configuration. + /// Each value is associated with a specific key type that conforms to `SnapshotEnvironmentKey`. + /// + /// - Parameter key: The type of key identifying the configuration value to access. + /// - Returns: The value associated with the provided key. + /// + /// Example usage: + /// ```swift + /// let diffTool = SnapshotEnvironment.current[DiffToolKey.self] + /// print(diffTool(currentFilePath: "file://old.png", failedFilePath: "file://new.png")) + /// ``` + public subscript(_ key: Key.Type) -> Key.Value { + (SnapshotEnvironmentValues.current ?? SnapshotEnvironmentValues())[key] + } +} diff --git a/Sources/XCSnapshotTesting/Environment/SnapshotEnvironmentKey.swift b/Sources/XCSnapshotTesting/Environment/SnapshotEnvironmentKey.swift new file mode 100644 index 000000000..a00a86e57 --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/SnapshotEnvironmentKey.swift @@ -0,0 +1,20 @@ +import Foundation + +/// A protocol for defining keys that can be used with `SnapshotEnvironmentValues`. +/// +/// Conform to this protocol to create custom keys for storing values in the snapshot testing environment. +/// Each key must specify an associated value type and provide a default value. +/// +/// ```swift +/// struct MyEnvironmentKey: SnapshotEnvironmentKey { +/// typealias Value = Int +/// static let defaultValue = 42 +/// } +/// ``` +public protocol SnapshotEnvironmentKey { + /// The type of value associated with the key. + associatedtype Value: Sendable + + /// The default value for the key if no value has been explicitly set. + static var defaultValue: Value { get } +} diff --git a/Sources/XCSnapshotTesting/Environment/SnapshotEnvironmentValues.swift b/Sources/XCSnapshotTesting/Environment/SnapshotEnvironmentValues.swift new file mode 100644 index 000000000..bfdec28eb --- /dev/null +++ b/Sources/XCSnapshotTesting/Environment/SnapshotEnvironmentValues.swift @@ -0,0 +1,312 @@ +import Foundation + +/// A collection of values that configure the snapshot testing environment. +/// +/// `SnapshotEnvironmentValues` provides a type-safe way to store and retrieve configuration values for snapshot testing. +/// It uses a subscript syntax with keys conforming to `SnapshotEnvironmentKey` to access and modify environment settings. +/// +/// ```swift +/// struct MyEnvironmentKey: SnapshotEnvironmentKey { +/// typealias Value = Int +/// static let defaultValue = 42 +/// } +/// +/// try await withTestingEnvironment { +/// $0[MyEnvironmentKey.self] = 100 +/// } operation: { +/// print(SnapshotEnvironment.current[MyEnvironmentKey.self]) // prints 100 +/// } +/// ``` +public struct SnapshotEnvironmentValues: Sendable { + + @TaskLocal static var current: SnapshotEnvironmentValues? + + private var values: [ObjectIdentifier: Sendable] = [:] + + init() {} + + /// Accesses or sets a configuration value associated with a specific key. + /// + /// This subscript provides type-safe access to configuration values stored in `SnapshotEnvironmentValues`. + /// It uses keys conforming to `SnapshotEnvironmentKey` to retrieve or modify values. + /// + /// - Parameter key: The type of key identifying the configuration value. + /// - Returns: The stored value for the provided key. If no value is set, returns the key's default value. + public subscript(_ key: Key.Type) -> Key.Value { + get { + values[ObjectIdentifier(key)] as? Key.Value ?? Key.defaultValue + } + set { + values[ObjectIdentifier(key)] = newValue + } + } +} + +/// Temporarily modifies the testing environment for the duration of the specified operation. +/// +/// This function allows you to safely mutate the testing environment within a defined scope. After the operation completes, the environment is restored to its original state. +/// +/// - Parameters: +/// - mutating: A closure that takes a mutable reference to `SnapshotEnvironmentValues` and modifies it as needed. +/// - operation: The asynchronous operation to perform within the modified environment. +/// - isolation: An optional actor used to isolate the operation. Defaults to the current isolation context. +/// - file: The file name for diagnostic purposes. Defaults to the current file. +/// - line: The line number for diagnostic purposes. Defaults to the current line. +/// +/// - Returns: The result of the asynchronous operation. +/// +/// - Throws: Any error thrown by the operation closure. +/// +/// - Note: The changes made to the environment are only in effect for the duration of the operation. +public func withTestingEnvironment( + _ mutating: @Sendable (inout SnapshotEnvironmentValues) -> Void, + operation: () async throws -> R, + isolation: isolated Actor? = #isolation, + file: String = #file, + line: UInt = #line +) async rethrows -> R { + var argumentValues = SnapshotEnvironmentValues.current ?? SnapshotEnvironmentValues() + mutating(&argumentValues) + return try await SnapshotEnvironmentValues.$current.withValue( + argumentValues, + operation: operation, + isolation: isolation, + file: file, + line: line + ) +} + +/// Temporarily modifies the testing environment for the duration of the specified operation. +/// +/// This function allows you to safely mutate the testing environment within a defined scope. After the operation completes, the environment is restored to its original state. +/// +/// - Parameters: +/// - mutating: A closure that takes a mutable reference to `SnapshotEnvironmentValues` and modifies it as needed. +/// - operation: The synchronous operation to perform within the modified environment. +/// - file: The file name for diagnostic purposes. Defaults to the current file. +/// - line: The line number for diagnostic purposes. Defaults to the current line. +/// +/// - Returns: The result of the synchronous operation. +/// +/// - Throws: Any error thrown by the operation closure. +/// +/// - Note: The changes made to the environment are only in effect for the duration of the operation. +public func withTestingEnvironment( + _ mutating: @Sendable (inout SnapshotEnvironmentValues) -> Void, + operation: () throws -> R, + file: String = #file, + line: UInt = #line +) rethrows -> R { + var argumentValues = SnapshotEnvironmentValues.current ?? SnapshotEnvironmentValues() + mutating(&argumentValues) + return try SnapshotEnvironmentValues.$current.withValue( + argumentValues, + operation: operation, + file: file, + line: line + ) +} + +/// Temporarily modifies the testing environment with specified parameters and executes an asynchronous operation within this modified context. +/// +/// This function allows granular control over the testing environment for the duration of the specified asynchronous operation. It combines direct parameter customization (`record`, `diffTool`, etc.) with a closure for additional environment modifications. +/// +/// - Parameters: +/// - record: Optionally sets the recording mode for the operation. +/// - diffTool: Optionally specifies a custom diff tool for the operation. +/// - maxConcurrentTests: Optionally limits the number of concurrent tests. +/// - platform: Optionally overrides the platform identifier for snapshot paths. +/// - mutating: A closure that can further modify the `SnapshotEnvironmentValues`. +/// - operation: The asynchronous operation to perform within the modified environment. +/// - isolation: An optional actor used to isolate the operation. Defaults to the current isolation context. +/// - file: The file name for diagnostic purposes. Defaults to the current file. +/// - line: The line number for diagnostic purposes. Defaults to the current line. +/// +/// - Returns: The result of the asynchronous operation. +/// - Throws: Any error thrown by the operation closure. +/// +/// - Note: The environment changes are only effective for the duration of the operation. +public func withTestingEnvironment( + record: RecordMode? = nil, + diffTool: DiffTool? = nil, + maxConcurrentTests: Int? = nil, + platform: String? = nil, + environment mutating: (@Sendable (inout SnapshotEnvironmentValues) -> Void), + operation: () async throws -> R, + isolation: isolated Actor? = #isolation, + file: String = #file, + line: UInt = #line +) async rethrows -> R { + try await withTestingEnvironment( + { + mutatingEnvironmentValues( + record: record, + diffTool: diffTool, + maxConcurrentTests: maxConcurrentTests, + platform: platform, + mutating: &$0 + ) + mutating(&$0) + }, + operation: operation, + isolation: isolation, + file: file, + line: line + ) +} + +/// Temporarily modifies the testing environment with specified parameters and executes an asynchronous operation within this modified context. +/// +/// This function allows quick configuration of common testing environment parameters for the duration of the specified asynchronous operation. +/// +/// - Parameters: +/// - record: Optionally sets the recording mode for the operation. +/// - diffTool: Optionally specifies a custom diff tool for the operation. +/// - maxConcurrentTests: Optionally limits the number of concurrent tests. +/// - platform: Optionally overrides the platform identifier for snapshot paths. +/// - operation: The asynchronous operation to perform within the modified environment. +/// - isolation: An optional actor used to isolate the operation. Defaults to the current isolation context. +/// - file: The file name for diagnostic purposes. Defaults to the current file. +/// - line: The line number for diagnostic purposes. Defaults to the current line. +/// +/// - Returns: The result of the asynchronous operation. +/// - Throws: Any error thrown by the operation closure. +/// +/// - Note: The environment changes are only effective for the duration of the operation. +public func withTestingEnvironment( + record: RecordMode? = nil, + diffTool: DiffTool? = nil, + maxConcurrentTests: Int? = nil, + platform: String? = nil, + operation: () async throws -> R, + isolation: isolated Actor? = #isolation, + file: String = #file, + line: UInt = #line +) async rethrows -> R { + try await withTestingEnvironment( + { + mutatingEnvironmentValues( + record: record, + diffTool: diffTool, + maxConcurrentTests: maxConcurrentTests, + platform: platform, + mutating: &$0 + ) + }, + operation: operation, + isolation: isolation, + file: file, + line: line + ) +} + +/// Temporarily modifies the testing environment with specified parameters and executes a synchronous operation within this modified context. +/// +/// This function allows granular control over the testing environment for the duration of the specified synchronous operation. It combines direct parameter customization (`record`, `diffTool`, etc.) with a closure for additional environment modifications. +/// +/// - Parameters: +/// - record: Optionally sets the recording mode for the operation. +/// - diffTool: Optionally specifies a custom diff tool for the operation. +/// - maxConcurrentTests: Optionally limits the number of concurrent tests. +/// - platform: Optionally overrides the platform identifier for snapshot paths. +/// - mutating: A closure that can further modify the `SnapshotEnvironmentValues`. +/// - operation: The synchronous operation to perform within the modified environment. +/// - file: The file name for diagnostic purposes. Defaults to the current file. +/// - line: The line number for diagnostic purposes. Defaults to the current line. +/// +/// - Returns: The result of the synchronous operation. +/// - Throws: Any error thrown by the operation closure. +/// +/// - Note: The environment changes are only effective for the duration of the operation. +public func withTestingEnvironment( + record: RecordMode? = nil, + diffTool: DiffTool? = nil, + maxConcurrentTests: Int? = nil, + platform: String? = nil, + environment mutating: (@Sendable (inout SnapshotEnvironmentValues) -> Void), + operation: () throws -> R, + file: String = #file, + line: UInt = #line +) rethrows -> R { + try withTestingEnvironment( + { + mutatingEnvironmentValues( + record: record, + diffTool: diffTool, + maxConcurrentTests: maxConcurrentTests, + platform: platform, + mutating: &$0 + ) + mutating(&$0) + }, + operation: operation, + file: file, + line: line + ) +} + +/// Temporarily modifies the testing environment with specified parameters and executes a synchronous operation within this modified context. +/// +/// This function allows quick configuration of common testing environment parameters for the duration of the specified synchronous operation. +/// +/// - Parameters: +/// - record: Optionally sets the recording mode for the operation. +/// - diffTool: Optionally specifies a custom diff tool for the operation. +/// - maxConcurrentTests: Optionally limits the number of concurrent tests. +/// - platform: Optionally overrides the platform identifier for snapshot paths. +/// - operation: The synchronous operation to perform within the modified environment. +/// - file: The file name for diagnostic purposes. Defaults to the current file. +/// - line: The line number for diagnostic purposes. Defaults to the current line. +/// +/// - Returns: The result of the synchronous operation. +/// - Throws: Any error thrown by the operation closure. +/// +/// - Note: The environment changes are only effective for the duration of the operation. +public func withTestingEnvironment( + record: RecordMode? = nil, + diffTool: DiffTool? = nil, + maxConcurrentTests: Int? = nil, + platform: String? = nil, + operation: () throws -> R, + file: String = #file, + line: UInt = #line +) rethrows -> R { + try withTestingEnvironment( + { + mutatingEnvironmentValues( + record: record, + diffTool: diffTool, + maxConcurrentTests: maxConcurrentTests, + platform: platform, + mutating: &$0 + ) + }, + operation: operation, + file: file, + line: line + ) +} + +private func mutatingEnvironmentValues( + record: RecordMode?, + diffTool: DiffTool?, + maxConcurrentTests: Int?, + platform: String?, + mutating environment: inout SnapshotEnvironmentValues +) { + if let record { + environment.recordMode = record + } + + if let diffTool { + environment.diffTool = diffTool + } + + if let maxConcurrentTests { + environment.maxConcurrentTests = maxConcurrentTests + } + + if let platform { + environment.platform = platform + } +} diff --git a/Sources/XCSnapshotTesting/Exports.swift b/Sources/XCSnapshotTesting/Exports.swift new file mode 100644 index 000000000..212156ccf --- /dev/null +++ b/Sources/XCSnapshotTesting/Exports.swift @@ -0,0 +1,3 @@ +#if !os(visionOS) +@_exported import _SnapshotTesting +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/CGImage+Extension.swift b/Sources/XCSnapshotTesting/Extensions/CGImage+Extension.swift new file mode 100644 index 000000000..cec2b9eeb --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/CGImage+Extension.swift @@ -0,0 +1,25 @@ +#if os(iOS) || os(tvOS) || os(macOS) || os(watchOS) || os(visionOS) +import CoreGraphics + +extension CGImage { + + func context(with data: UnsafeMutableRawPointer? = nil) -> CGContext? { + let bytesPerRow = self.width * ImageContext.bytesPerPixel + guard + let colorSpace = ImageContext.colorSpace, + let context = CGContext( + data: data, + width: self.width, + height: self.height, + bitsPerComponent: ImageContext.bitsPerComponent, + bytesPerRow: bytesPerRow, + space: colorSpace, + bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue + ) + else { return nil } + + context.draw(self, in: CGRect(x: 0, y: 0, width: self.width, height: self.height)) + return context + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/CGSize+Extension.swift b/Sources/XCSnapshotTesting/Extensions/CGSize+Extension.swift new file mode 100644 index 000000000..53ce68543 --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/CGSize+Extension.swift @@ -0,0 +1,92 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(macOS) || os(visionOS) +import CoreGraphics + +extension CGSize { + + func scaleThatFits(_ size: CGSize) -> CGFloat { + if size.width <= width && size.height <= height { + return 1 + } + + let scaleWidth = width / size.width + let scaleHeight = height / size.height + + return min(scaleWidth, scaleHeight) + } + + func scaleToFit(_ size: CGSize) -> CGSize { + let scale = scaleThatFits(size) + + return CGSize( + width: size.width * scale, + height: size.height * scale + ) + } +} + +extension CGRect { + + func scale(by scale: CGFloat) -> CGRect { + guard scale != 1 else { + return self + } + + let center = CGPoint(x: midX, y: midY) + + let scaledSize = CGSize( + width: width * scale, + height: height * scale + ) + + return .init( + x: center.x - scaledSize.width / 2, + y: center.y - scaledSize.height / 2, + width: scaledSize.width, + height: scaledSize.height + ) + } +} + +extension CGSize { + + /// 440 x 956 + static let screen6_9 = CGSize(width: 440, height: 956) + + /// 430 x 932 + static let screen6_7v2 = CGSize(width: 430, height: 932) + + /// 428 x 926 + static let screen6_7v1 = CGSize(width: 428, height: 926) + + /// 414 x 896 + static let screen6_5 = CGSize(width: 414, height: 896) + + /// 402 x 874 + static let screen6_3 = CGSize(width: 402, height: 874) + + /// 414 x 896 + static let screen6_1v3 = CGSize(width: 414, height: 896) + + /// 393 x 852 + static let screen6_1v2 = CGSize(width: 393, height: 852) + + /// 390 x 844 + static let screen6_1v1 = CGSize(width: 390, height: 844) + + /// 375 x 812 + static let screen5_8 = CGSize(width: 375, height: 812) + + /// 414 x 736 + static let screen5_5 = CGSize(width: 414, height: 736) + + /// 375 x 812 + static let screen5_4 = CGSize(width: 375, height: 812) + + /// 375 x 667 + static let screen4_7 = CGSize(width: 375, height: 667) + + func reflected() -> CGSize { + .init(width: height, height: width) + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/CIImage+Extension.swift b/Sources/XCSnapshotTesting/Extensions/CIImage+Extension.swift new file mode 100644 index 000000000..c10acf18d --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/CIImage+Extension.swift @@ -0,0 +1,201 @@ +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) +import Accelerate.vImage +import CoreImage.CIKernel +import MetalPerformanceShaders +@preconcurrency import Metal.MTLDevice + +extension CIImage { + + func perceptuallyCompare( + _ newValue: CIImage, + pixelPrecision: Float, + perceptualPrecision: Float + ) -> String? { + // Calculate the deltaE values. Each pixel is a value between 0-100. + // 0 means no difference, 100 means completely opposite. + let deltaOutputImage = self.applyingLabDeltaE(newValue) + // Setting the working color space and output color space to NSNull disables color management. This is appropriate when the output + // of the operations is computational instead of an image intended to be displayed. + let context = CIContext(options: [.workingColorSpace: NSNull(), .outputColorSpace: NSNull()]) + let deltaThreshold = (1 - perceptualPrecision) * 100 + let actualPixelPrecision: Float + var maximumDeltaE: Float = 0 + + // Metal is supported by all iOS/tvOS devices (2013 models or later) and Macs (2012 models or later). + // Older devices do not support iOS/tvOS 13 and macOS 10.15 which are the minimum versions of swift-snapshot-testing. + // However, some virtualized hardware do not have GPUs and therefore do not support Metal. + // In this case, macOS falls back to a CPU-based OpenGL ES renderer that silently fails when a Metal command is issued. + // We need to check for Metal device support and fallback to CPU based vImage buffer iteration. + if ThresholdImageProcessorKernel.isSupported { + // Fast path - Metal processing + guard + let thresholdOutputImage = try? deltaOutputImage.applyingThreshold(deltaThreshold), + let averagePixel = thresholdOutputImage.applyingAreaAverage().renderSingleValue( + in: context + ) + else { + return "Newly-taken snapshot's data could not be processed." + } + actualPixelPrecision = 1 - averagePixel + if actualPixelPrecision < pixelPrecision { + maximumDeltaE = deltaOutputImage.applyingAreaMaximum().renderSingleValue(in: context) ?? 0 + } + } else { + // Slow path - CPU based vImage buffer iteration + guard let buffer = deltaOutputImage.render(in: context) else { + return "Newly-taken snapshot could not be processed." + } + defer { buffer.free() } + var failingPixelCount: Int = 0 + // rowBytes must be a multiple of 8, so vImage_Buffer pads the end of each row with bytes to meet the multiple of 0 requirement. + // We must do 2D iteration of the vImage_Buffer in order to avoid loading the padding garbage bytes at the end of each row. + // + // NB: We are purposely using a verbose 'while' loop instead of a 'for in' loop. When the + // compiler doesn't have optimizations enabled, like in test targets, a `while` loop is + // significantly faster than a `for` loop for iterating through the elements of a memory + // buffer. Details can be found in [SR-6983](https://github.com/apple/swift/issues/49531) + let componentStride = MemoryLayout.stride + var line = 0 + while line < buffer.height { + defer { line += 1 } + let lineOffset = buffer.rowBytes * line + var column = 0 + while column < buffer.width { + defer { column += 1 } + let byteOffset = lineOffset + column * componentStride + let deltaE = buffer.data.load(fromByteOffset: byteOffset, as: Float.self) + if deltaE > deltaThreshold { + failingPixelCount += 1 + if deltaE > maximumDeltaE { + maximumDeltaE = deltaE + } + } + } + } + let failingPixelPercent = + Float(failingPixelCount) + / Float(deltaOutputImage.extent.width * deltaOutputImage.extent.height) + actualPixelPrecision = 1 - failingPixelPercent + } + + guard actualPixelPrecision < pixelPrecision else { return nil } + // The actual perceptual precision is the perceptual precision of the pixel with the highest DeltaE. + // DeltaE is in a 0-100 scale, so we need to divide by 100 to transform it to a percentage. + let minimumPerceptualPrecision = 1 - min(maximumDeltaE / 100, 1) + return """ + The percentage of pixels that match \(actualPixelPrecision) is less than required \(pixelPrecision) + The lowest perceptual color precision \(minimumPerceptualPrecision) is less than required \(perceptualPrecision) + """ + } +} + +extension CIImage { + + fileprivate func applyingLabDeltaE(_ other: CIImage) -> CIImage { + applyingFilter("CILabDeltaE", parameters: ["inputImage2": other]) + } + + fileprivate func applyingThreshold(_ threshold: Float) throws -> CIImage { + try ThresholdImageProcessorKernel.apply( + withExtent: extent, + inputs: [self], + arguments: [ThresholdImageProcessorKernel.inputThresholdKey: threshold] + ) + } + + fileprivate func applyingAreaAverage() -> CIImage { + applyingFilter("CIAreaAverage", parameters: [kCIInputExtentKey: extent]) + } + + fileprivate func applyingAreaMaximum() -> CIImage { + applyingFilter("CIAreaMaximum", parameters: [kCIInputExtentKey: extent]) + } + + fileprivate func renderSingleValue(in context: CIContext) -> Float? { + guard let buffer = render(in: context) else { return nil } + defer { buffer.free() } + return buffer.data.load(fromByteOffset: 0, as: Float.self) + } + + fileprivate func render(in context: CIContext, format: CIFormat = CIFormat.Rh) -> vImage_Buffer? { + // Some hardware configurations (virtualized CPU renderers) do not support 32-bit float output formats, + // so use a compatible 16-bit float format and convert the output value to 32-bit floats. + guard + var buffer16 = try? vImage_Buffer( + width: Int(extent.width), + height: Int(extent.height), + bitsPerPixel: 16 + ) + else { return nil } + defer { buffer16.free() } + context.render( + self, + toBitmap: buffer16.data, + rowBytes: buffer16.rowBytes, + bounds: extent, + format: format, + colorSpace: nil + ) + guard + var buffer32 = try? vImage_Buffer( + width: Int(buffer16.width), + height: Int(buffer16.height), + bitsPerPixel: 32 + ), + vImageConvert_Planar16FtoPlanarF(&buffer16, &buffer32, 0) == kvImageNoError + else { return nil } + return buffer32 + } +} + +// Copied from https://developer.apple.com/documentation/coreimage/ciimageprocessorkernel +private final class ThresholdImageProcessorKernel: CIImageProcessorKernel { + static let inputThresholdKey = "thresholdValue" + static let device = MTLCreateSystemDefaultDevice() + + static var isSupported: Bool { + guard let device = device else { + return false + } + + #if targetEnvironment(simulator) + guard #available(iOS 14.0, tvOS 14.0, *) else { + // The MPSSupportsMTLDevice method throws an exception on iOS/tvOS simulators < 14.0 + return false + } + #endif + + return MPSSupportsMTLDevice(device) + } + + override class func process( + with inputs: [CIImageProcessorInput]?, + arguments: [String: Any]?, + output: CIImageProcessorOutput + ) throws { + guard + let device = device, + let commandBuffer = output.metalCommandBuffer, + let input = inputs?.first, + let sourceTexture = input.metalTexture, + let destinationTexture = output.metalTexture, + let thresholdValue = arguments?[inputThresholdKey] as? Float + else { + return + } + + let threshold = MPSImageThresholdBinary( + device: device, + thresholdValue: thresholdValue, + maximumValue: 1.0, + linearGrayColorTransform: nil + ) + + threshold.encode( + commandBuffer: commandBuffer, + sourceTexture: sourceTexture, + destinationTexture: destinationTexture + ) + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/NSLayoutConstraint+Extension.swift b/Sources/XCSnapshotTesting/Extensions/NSLayoutConstraint+Extension.swift new file mode 100644 index 000000000..a7fabf451 --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/NSLayoutConstraint+Extension.swift @@ -0,0 +1,26 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#elseif os(macOS) +import AppKit +#endif + +#if os(tvOS) || os(macOS) || os(iOS) || os(visionOS) +extension NSLayoutConstraint { + + func storing(in constraints: inout [NSLayoutConstraint]) -> NSLayoutConstraint { + constraints.append(self) + return self + } +} + +extension NSLayoutConstraint { + + static func activate( + _ constraints: [NSLayoutConstraint], + storingAt store: inout [NSLayoutConstraint] + ) { + activate(constraints) + store.append(contentsOf: constraints) + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/ProcessInfo+Extension.swift b/Sources/XCSnapshotTesting/Extensions/ProcessInfo+Extension.swift new file mode 100644 index 000000000..26cb92555 --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/ProcessInfo+Extension.swift @@ -0,0 +1,19 @@ +import Foundation + +extension ProcessInfo { + + static var artifactsDirectory: URL { + let env = ProcessInfo.processInfo.environment + + return URL( + fileURLWithPath: env["SNAPSHOT_ARTIFACTS"] ?? NSTemporaryDirectory(), + isDirectory: true + ) + } + + static var isXcode: Bool { + ProcessInfo.processInfo.environment.keys.contains( + "__XCODE_BUILT_PRODUCTS_DIR_PATHS" + ) + } +} diff --git a/Sources/XCSnapshotTesting/Extensions/String+Extension.swift b/Sources/XCSnapshotTesting/Extensions/String+Extension.swift new file mode 100644 index 000000000..d07a42c44 --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/String+Extension.swift @@ -0,0 +1,53 @@ +import Foundation + +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +import CoreServices +import UniformTypeIdentifiers +#endif + +extension String { + + func sanitizingPointersReferences() -> String { + replacingOccurrences( + of: ":?\\s*0x[\\da-f]+(\\s*)", + with: "$1", + options: .regularExpression + ) + } + + func sanitizingPathComponent() -> String { + // see for ressoning on charachrer sets https://superuser.com/a/358861 + let invalidCharacters = CharacterSet(charactersIn: "\\/:*?\"<>|") + .union(.newlines) + .union(.illegalCharacters) + .union(.controlCharacters) + + return + self + // Only applies to functions without parameters + .replacingOccurrences(of: "()", with: "") + .components(separatedBy: invalidCharacters) + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + .joined(separator: "") + } + + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) + func uniformTypeIdentifier() -> String? { + #if os(visionOS) + return UTType(filenameExtension: self)?.identifier + #else + if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) { + return UTType(filenameExtension: self)?.identifier + } + + let unmanagedString = UTTypeCreatePreferredIdentifierForTag( + kUTTagClassFilenameExtension as CFString, + self as CFString, + nil + ) + + return unmanagedString?.takeRetainedValue() as String? + #endif + } + #endif +} diff --git a/Sources/XCSnapshotTesting/Extensions/Task+Extension.swift b/Sources/XCSnapshotTesting/Extensions/Task+Extension.swift new file mode 100644 index 000000000..9be92f426 --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/Task+Extension.swift @@ -0,0 +1,49 @@ +import Foundation + +extension Task where Failure == Error { + + public static func timeout( + _ timeout: TimeInterval, + execute closure: @Sendable @escaping () async throws -> Success + ) async throws -> Success { + try await withUnsafeThrowingContinuation { continuation in + let regularTask = Task { + let result: Result + + do { + result = .success(try await closure()) + } catch { + result = .failure(error) + } + + do { + try Task.checkCancellation() + } catch { + continuation.resume(throwing: error) + return + } + + continuation.resume(with: result) + } + + let timeoutTask = Task { + try await Task.sleep( + nanoseconds: UInt64(timeout) * 1_000_000_000 + ) + continuation.resume(throwing: TaskTimeout()) + } + + Task { + _ = try? await timeoutTask.value + timeoutTask.cancel() + } + + Task { + _ = await regularTask.value + timeoutTask.cancel() + } + } + } +} + +public struct TaskTimeout: Error {} diff --git a/Sources/XCSnapshotTesting/Extensions/UIApplication+Extension.swift b/Sources/XCSnapshotTesting/Extensions/UIApplication+Extension.swift new file mode 100644 index 000000000..361fca2f4 --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/UIApplication+Extension.swift @@ -0,0 +1,81 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) +@MainActor +extension SDKApplication { + + static var sharedIfAvailable: SDKApplication? { + let sharedSelector = NSSelectorFromString("sharedApplication") + guard SDKApplication.responds(to: sharedSelector) else { + return nil + } + + let shared = SDKApplication.perform(sharedSelector) + return shared?.takeUnretainedValue() as! SDKApplication? + } + + #if os(iOS) || os(tvOS) || os(visionOS) + func windowScenes(for role: UISceneSession.Role) -> [UIWindowScene] { + connectedScenes.lazy + .filter { $0.session.role == role } + .compactMap { $0 as? UIWindowScene } + } + #endif +} + +#if os(iOS) || os(tvOS) || os(visionOS) +extension [UIWindowScene] { + + @MainActor + var keyWindows: [SDKWindow] { + self.lazy + .filter { $0.session.role == .windowApplication } + .reduce([]) { + $0 + $1.windows.filter(\.isKeyWindow) + } + } +} +#endif + +@MainActor +private var kUIApplicationLock = 0 + +@MainActor +extension SDKApplication { + + private var lock: AsyncLock { + if let lock = objc_getAssociatedObject(self, &kUIApplicationLock) as? AsyncLock { + return lock + } + + let lock = AsyncLock() + objc_setAssociatedObject(self, &kUIApplicationLock, lock, .OBJC_ASSOCIATION_RETAIN) + return lock + } + + fileprivate func withLock( + _ body: @Sendable () async throws -> Value + ) async throws -> Value { + try await lock.withLock(body) + } +} + +extension AsyncSnapshot { + + func withLock() -> AsyncSnapshot< + Input, Output + > where Executor == Async { + map { executor in + Async(Input.self) { application in + try await application.withLock { + try await executor(application) + } + } + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/UIImage+Extension.swift b/Sources/XCSnapshotTesting/Extensions/UIImage+Extension.swift new file mode 100644 index 000000000..106c19bf7 --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/UIImage+Extension.swift @@ -0,0 +1,199 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) +typealias SDKImage = UIKit.UIImage +typealias SDKLabel = UIKit.UILabel +typealias SDKView = UIKit.UIView +typealias SDKViewController = UIKit.UIViewController +typealias SDKApplication = UIKit.UIApplication +typealias SDKWindow = UIKit.UIWindow +#elseif os(watchOS) +typealias SDKImage = UIKit.UIImage +#elseif os(macOS) +typealias SDKImage = AppKit.NSImage +typealias SDKLabel = AppKit.NSText +typealias SDKView = AppKit.NSView +typealias SDKViewController = AppKit.NSViewController +typealias SDKApplication = AppKit.NSApplication +typealias SDKWindow = AppKit.NSWindow +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) || os(macOS) +extension SDKImage { + + #if os(macOS) + var scale: CGFloat { + 1.0 + } + + var cgImage: CGImage? { + guard + let pngData = pngData(), + let dataProvider = CGDataProvider(data: pngData as CFData) + else { return nil } + + return CGImage( + pngDataProviderSource: dataProvider, + decode: nil, + shouldInterpolate: true, + intent: .defaultIntent + ) + } + + func pngData() -> Data? { + performOnMainThread { + guard + let bitmapRep = NSBitmapImageRep( + bitmapDataPlanes: nil, + pixelsWide: Int(size.width), + pixelsHigh: Int(size.height), + bitsPerSample: 8, + samplesPerPixel: 4, + hasAlpha: true, + isPlanar: false, + colorSpaceName: .calibratedRGB, + bytesPerRow: 0, + bitsPerPixel: 0 + ), + let context = NSGraphicsContext(bitmapImageRep: bitmapRep) + else { return nil } + + NSGraphicsContext.saveGraphicsState() + NSGraphicsContext.current = context + draw(in: NSRect(origin: .zero, size: size)) + NSGraphicsContext.restoreGraphicsState() + + return bitmapRep.representation(using: .png, properties: [:]) + } + } + #endif + + /// Used when the image size has no width or no height to generated the default empty image + @MainActor + static var empty: SDKImage { + #if os(iOS) || os(tvOS) || os(macOS) + let label = SDKLabel(frame: CGRect(x: 0, y: 0, width: 400, height: 80)) + let text = + "Error: No image could be generated for this view as its size was zero. Please set an explicit size in the test." + label.backgroundColor = .red + #if os(macOS) + label.string = text + label.alignment = .center + label.isVerticallyResizable = true + #else + label.text = text + label.textAlignment = .center + label.numberOfLines = 3 + #endif + return label.asImage() + #else + return SDKImage() + #endif + } + + @MainActor + func substract(_ image: SDKImage) -> SDKImage { + #if os(macOS) + guard let lhsImage = cgImage, let rhsImage = image.cgImage else { + return SDKImage() + } + + let oldCiImage = CIImage(cgImage: lhsImage) + let newCiImage = CIImage(cgImage: rhsImage) + let differenceFilter = CIFilter(name: "CIDifferenceBlendMode")! + differenceFilter.setValue(oldCiImage, forKey: kCIInputImageKey) + differenceFilter.setValue(newCiImage, forKey: kCIInputBackgroundImageKey) + let maxSize = CGSize( + width: max(size.width, image.size.width), + height: max(size.height, image.size.height) + ) + let rep = NSCIImageRep(ciImage: differenceFilter.outputImage!) + let difference = NSImage(size: maxSize) + difference.addRepresentation(rep) + return difference + #else + let width = max(self.size.width, image.size.width) + let height = max(self.size.height, image.size.height) + let scale = max(self.scale, image.scale) + let size = CGSize(width: width, height: height) + UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), true, scale) + image.draw(in: .init(origin: .zero, size: size)) + self.draw(in: .init(origin: .zero, size: size), blendMode: .difference, alpha: 1) + let differenceImage = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + return differenceImage + #endif + } + + @MainActor + func compare(_ newValue: SDKImage, precision: Float, perceptualPrecision: Float) -> String? { + guard let oldCgImage = self.cgImage else { + return "Reference image could not be loaded." + } + guard let newCgImage = newValue.cgImage else { + return "Newly-taken snapshot could not be loaded." + } + guard newCgImage.width != 0, newCgImage.height != 0 else { + return "Newly-taken snapshot is empty." + } + guard oldCgImage.width == newCgImage.width, oldCgImage.height == newCgImage.height else { + return "Newly-taken snapshot@\(newValue.size) does not match reference@\(self.size)." + } + let pixelCount = oldCgImage.width * oldCgImage.height + let byteCount = ImageContext.bytesPerPixel * pixelCount + var oldBytes = [UInt8](repeating: 0, count: byteCount) + guard let oldData = oldCgImage.context(with: &oldBytes)?.data else { + return "Reference image's data could not be loaded." + } + if let newContext = newCgImage.context(), let newData = newContext.data { + if memcmp(oldData, newData, byteCount) == 0 { return nil } + } + var newerBytes = [UInt8](repeating: 0, count: byteCount) + guard + let pngData = newValue.pngData(), + let newerCgImage = SDKImage(data: pngData)?.cgImage, + let newerContext = newerCgImage.context(with: &newerBytes), + let newerData = newerContext.data + else { + return "Newly-taken snapshot's data could not be loaded." + } + if memcmp(oldData, newerData, byteCount) == 0 { return nil } + if precision >= 1, perceptualPrecision >= 1 { + return "Newly-taken snapshot does not match reference." + } + #if os(iOS) || os(tvOS) || os(macOS) + if perceptualPrecision < 1, #available(iOS 11.0, tvOS 11.0, *) { + return CIImage(cgImage: oldCgImage).perceptuallyCompare( + CIImage(cgImage: newCgImage), + pixelPrecision: precision, + perceptualPrecision: perceptualPrecision + ) + } + #endif + + let byteCountThreshold = Int((1 - precision) * Float(byteCount)) + var differentByteCount = 0 + // NB: We are purposely using a verbose 'while' loop instead of a 'for in' loop. When the + // compiler doesn't have optimizations enabled, like in test targets, a `while` loop is + // significantly faster than a `for` loop for iterating through the elements of a memory + // buffer. Details can be found in [SR-6983](https://github.com/apple/swift/issues/49531) + var index = 0 + while index < byteCount { + defer { index += 1 } + if oldBytes[index] != newerBytes[index] { + differentByteCount += 1 + } + } + if differentByteCount > byteCountThreshold { + let actualPrecision = 1 - Float(differentByteCount) / Float(byteCount) + return "Actual image precision \(actualPrecision) is less than required \(precision)" + } + + return nil + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/UIView+Extension.swift b/Sources/XCSnapshotTesting/Extensions/UIView+Extension.swift new file mode 100644 index 000000000..50dbd56db --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/UIView+Extension.swift @@ -0,0 +1,175 @@ +#if os(macOS) +import AppKit +#elseif os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#endif + +#if os(macOS) || os(iOS) || os(visionOS) +import WebKit +#endif + +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) + +@MainActor +extension SDKView { + + func asImage() -> SDKImage { + #if os(macOS) + guard let rep = bitmapImageRepForCachingDisplay(in: bounds) else { + return SDKImage() + } + + cacheDisplay(in: bounds, to: rep) + + let image = SDKImage(size: bounds.size) + image.addRepresentation(rep) + + return image + #else + let renderer = UIGraphicsImageRenderer(bounds: bounds) + return renderer.image { rendererContext in + layer.render(in: rendererContext.cgContext) + } + #endif + } + + func withController() -> SDKViewController { + UIViewHostingController(self) + } + + func waitLoadingStateIfNeeded(tolerance: TimeInterval) async { + #if os(iOS) || os(macOS) || os(visionOS) + if let webView = self as? WKWebView { + try? await webView.waitLoadingState(tolerance: tolerance) + return + } + + for subview in subviews { + await subview.waitLoadingStateIfNeeded(tolerance: tolerance) + } + #endif + } + + func recursiveNeedsLayout() { + guard window != nil else { + return + } + + invalidateIntrinsicContentSize() + #if os(macOS) + needsUpdateConstraints = true + needsLayout = true + #else + setNeedsUpdateConstraints() + setNeedsLayout() + #endif + + switch self { + #if os(macOS) + #else + case is UITableView, is UICollectionView: + break + #endif + default: + for view in subviews { + view.recursiveNeedsLayout() + } + } + } +} + +@MainActor +private class UIViewHostingController: SDKViewController { + + private let contentView: SDKView + + init(_ contentView: SDKView) { + self.contentView = contentView + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + nil + } + + override func loadView() { + view = contentView + } +} + +@MainActor +private var kUIViewLock = 0 + +@MainActor +extension SDKView { + + private var lock: AsyncLock { + if let lock = objc_getAssociatedObject(self, &kUIViewLock) as? AsyncLock { + return lock + } + + let lock = AsyncLock() + objc_setAssociatedObject(self, &kUIViewLock, lock, .OBJC_ASSOCIATION_RETAIN) + return lock + } + + fileprivate func withLock( + _ body: @Sendable () async throws -> Value + ) async throws -> Value { + try await lock.withLock(body) + } +} + +extension Snapshot { + + func withLock() -> AsyncSnapshot + where Executor == Async { + map { executor in + Async(Input.self) { view in + try await view.withLock { + try await executor(view) + } + } + } + } +} +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) +@MainActor +private var kUIViewTraits = 0 + +extension SDKView { + + private var traits: Traits? { + get { objc_getAssociatedObject(self, &kUIViewTraits) as? Traits } + set { + objc_setAssociatedObject( + self, + &kUIViewTraits, + newValue, + .OBJC_ASSOCIATION_RETAIN + ) + } + } + + func inconsistentTraitsChecker(for traits: Traits) { + defer { self.traits = traits } + self.traits?.inconsistentTraitsChecker(self, to: traits) + } +} + +extension Snapshot { + + func inconsistentTraitsChecker( + _ traits: Traits + ) -> AsyncSnapshot where Executor == Async { + map { executor in + Async(Input.self) { @MainActor in + $0.inconsistentTraitsChecker(for: traits) + return try await executor($0) + } + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/UIViewController+Extension.swift b/Sources/XCSnapshotTesting/Extensions/UIViewController+Extension.swift new file mode 100644 index 000000000..422359f3b --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/UIViewController+Extension.swift @@ -0,0 +1,84 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) +@MainActor +private var kUIViewControllerLock = 0 + +@MainActor +extension SDKViewController { + + private var lock: AsyncLock { + if let lock = objc_getAssociatedObject(self, &kUIViewControllerLock) as? AsyncLock { + return lock + } + + let lock = AsyncLock() + objc_setAssociatedObject(self, &kUIViewControllerLock, lock, .OBJC_ASSOCIATION_RETAIN) + return lock + } + + fileprivate func withLock( + _ body: @Sendable () async throws -> Value + ) async throws -> Value { + try await lock.withLock(body) + } +} + +extension Snapshot { + + func withLock() -> AsyncSnapshot< + Input, Output + > where Executor == Async { + map { executor in + Async(Input.self) { view in + try await view.withLock { + try await executor(view) + } + } + } + } +} +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) +@MainActor +private var kUIViewControllerTraits = 0 + +extension SDKViewController { + + private var traits: Traits? { + get { objc_getAssociatedObject(self, &kUIViewControllerTraits) as? Traits } + set { + objc_setAssociatedObject( + self, + &kUIViewControllerTraits, + newValue, + .OBJC_ASSOCIATION_RETAIN + ) + } + } + + func inconsistentTraitsChecker(for traits: Traits) { + defer { self.traits = traits } + self.traits?.inconsistentTraitsChecker(self, to: traits) + } +} + +extension Snapshot { + + func inconsistentTraitsChecker( + _ traits: Traits + ) -> AsyncSnapshot where Executor == Async { + map { executor in + Async(Input.self) { @MainActor in + $0.inconsistentTraitsChecker(for: traits) + return try await executor($0) + } + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/UIWindow+Extension.swift b/Sources/XCSnapshotTesting/Extensions/UIWindow+Extension.swift new file mode 100644 index 000000000..b71a738c9 --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/UIWindow+Extension.swift @@ -0,0 +1,64 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) +@MainActor +extension SDKWindow { + + @discardableResult + func removeRootViewController() -> SDKViewController? { + #if os(macOS) + if let contentViewController { + for presentedViewController in contentViewController.presentedViewControllers ?? [] { + contentViewController.dismiss(presentedViewController) + } + + contentViewController.view.removeFromSuperview() + self.contentViewController = nil + return contentViewController + } + #else + if let rootViewController { + // Allow the view controller to be deallocated + rootViewController.dismiss(animated: false) { + // Remove the root view in case its still showing + rootViewController.view.removeFromSuperview() + } + + self.rootViewController = nil + + for subview in subviews { + subview.removeFromSuperview() + } + + return rootViewController + } + #endif + return nil + } + + @discardableResult + func switchRoot( + _ viewController: SDKViewController + ) -> SDKViewController? { + #if os(macOS) + let previousRootViewController = removeRootViewController() + contentViewController = viewController + #else + let previousRootViewController = removeRootViewController() + rootViewController = viewController + + #if !os(tvOS) && !os(visionOS) + viewController.setNeedsStatusBarAppearanceUpdate() + #endif + setNeedsLayout() + layoutIfNeeded() + safeAreaInsetsDidChange() + #endif + return previousRootViewController + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Extensions/WKWebView+Extension.swift b/Sources/XCSnapshotTesting/Extensions/WKWebView+Extension.swift new file mode 100644 index 000000000..6e3ca1d9e --- /dev/null +++ b/Sources/XCSnapshotTesting/Extensions/WKWebView+Extension.swift @@ -0,0 +1,12 @@ +#if os(iOS) || os(macOS) || os(visionOS) +import WebKit + +extension WKWebView { + + func waitLoadingState(tolerance: TimeInterval) async throws { + repeat { + try await Task.sleep(nanoseconds: UInt64(tolerance * 1_000_000_000)) + } while isLoading + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/Snapshot+Any.swift b/Sources/XCSnapshotTesting/Methods/Snapshot+Any.swift new file mode 100644 index 000000000..bd24e7355 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/Snapshot+Any.swift @@ -0,0 +1,68 @@ +import Foundation + +extension SyncSnapshot where Output == StringBytes { + /// A snapshot strategy that captures a value's textual description from `String`'s + /// `init(describing:)` initializer. + /// + /// ``` swift + /// try assert(of: user, as: .description) + /// ``` + /// + /// Records: + /// + /// ``` + /// User(bio: "Blobbed around the world.", id: 1, name: "Blobby") + /// ``` + public static var description: SyncSnapshot { + IdentitySyncSnapshot.lines.pullback { + String(describing: $0) + } + } +} + +extension SyncSnapshot where Output == StringBytes { + /// A snapshot strategy for comparing any structure based on their JSON representation. + /// + /// This strategy serializes the input value into a JSON-formatted string using + /// `JSONSerialization` with the following options: + /// - `.prettyPrinted` for human-readable formatting + /// - `.sortedKeys` to ensure consistent key ordering + /// - `.fragmentsAllowed` for partial JSON outputs + /// + /// The result is a `StringBytes` snapshot containing the JSON-encoded data. + /// + /// - Available on: macOS 10.13+, watchOS 4.0+, tvOS 11.0+ + /// - Example: `assert(of: user, as: .json)` + public static var json: SyncSnapshot { + let options: JSONSerialization.WritingOptions = [ + .prettyPrinted, + .sortedKeys, + .fragmentsAllowed, + ] + + let snapshot = IdentitySyncSnapshot.lines.pullback { (data: Input) in + try String( + decoding: JSONSerialization.data( + withJSONObject: data, + options: options + ), + as: UTF8.self + ) + } + + return .init( + pathExtension: "json", + attachmentGenerator: snapshot.attachmentGenerator, + executor: snapshot.executor + ) + } +} + +private let snapshotDateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" + formatter.calendar = Calendar(identifier: .gregorian) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(abbreviation: "UTC") + return formatter +}() diff --git a/Sources/XCSnapshotTesting/Methods/Snapshot+CaseIterable.swift b/Sources/XCSnapshotTesting/Methods/Snapshot+CaseIterable.swift new file mode 100644 index 000000000..6ec1dd540 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/Snapshot+CaseIterable.swift @@ -0,0 +1,71 @@ +extension SyncSnapshot where Input: CaseIterable & Sendable, Output == StringBytes, Input.AllCases: Sendable { + /// A strategy for snapshot the output for every input of a function. The format of the + /// snapshot is a comma-separated value (CSV) file that shows the mapping of inputs to outputs. + /// + /// - Parameter witness: A snapshot value on the output of the function to be snapshot. + /// - Returns: A snapshot strategy on functions `(Value) -> A` that feeds every possible input + /// into the function and records the output into a CSV file. + /// + /// ```swift + /// enum Direction: String, CaseIterable { + /// case up, down, left, right + /// var rotatedLeft: Direction { + /// switch self { + /// case .up: return .left + /// case .down: return .right + /// case .left: return .down + /// case .right: return .up + /// } + /// } + /// } + /// + /// try assert( + /// of: \Direction.rotatedLeft, + /// as: .func(into: .description) + /// ) + /// ``` + /// + /// Records: + /// + /// ```csv + /// "up","left" + /// "down","right" + /// "left","down" + /// "right","up" + /// ``` + public static func `func`( + into witness: SyncSnapshot + ) -> SyncSnapshot<@Sendable (Input) -> A, Output> { + let snapshot = IdentitySyncSnapshot.lines.map { executor in + executor.pullback { (f: @escaping @Sendable (Input) -> A, continuation) in + Input.allCases.map { input in + Sync { (f: @escaping @Sendable (Input) -> A, continuation) in + witness.executor(f(input)) { result in + switch result { + case .success(let output): + continuation.resume(returning: (input, output)) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } + .sequence() + .map { + $0 + .map { "\"\($0)\",\"\($1)\"" } + .joined(separator: "\n") + } + .callAsFunction(f) { + continuation.resume(with: $0) + } + } + } + + return .init( + pathExtension: "csv", + attachmentGenerator: snapshot.attachmentGenerator, + executor: snapshot.executor + ) + } +} diff --git a/Sources/XCSnapshotTesting/Methods/Snapshot+Encodable.swift b/Sources/XCSnapshotTesting/Methods/Snapshot+Encodable.swift new file mode 100644 index 000000000..098cd6271 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/Snapshot+Encodable.swift @@ -0,0 +1,91 @@ +import Foundation + +extension SyncSnapshot where Input: Encodable & Sendable, Output == StringBytes { + /// A snapshot strategy for comparing encodable structures based on their JSON representation. + /// + /// ```swift + /// try assert(of: user, as: .json) + /// ``` + /// + /// Records: + /// + /// ```json + /// { + /// "bio" : "Blobbed around the world.", + /// "id" : 1, + /// "name" : "Blobby" + /// } + /// ``` + public static var json: SyncSnapshot { + let encoder = JSONEncoder() + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + return .json(encoder) + } + + /// A snapshot strategy for comparing encodable structures based on their JSON representation. + /// + /// - Parameter encoder: A JSON encoder. + public static func json(_ encoder: JSONEncoder) -> SyncSnapshot { + let snapshot = IdentitySyncSnapshot.lines.pullback { (encodable: Input) in + try String( + decoding: encoder.encode(encodable), + as: UTF8.self + ) + } + + return .init( + pathExtension: "json", + attachmentGenerator: snapshot.attachmentGenerator, + executor: snapshot.executor + ) + } + + /// A snapshot strategy for comparing encodable structures based on their property list + /// representation. + /// + /// ```swift + /// try assert(of: user, as: .plist) + /// ``` + /// + /// Records: + /// + /// ```xml + /// + /// + /// + /// + /// bio + /// Blobbed around the world. + /// id + /// 1 + /// name + /// Blobby + /// + /// + /// ``` + public static var plist: SyncSnapshot { + let encoder = Foundation.PropertyListEncoder() + encoder.outputFormat = .xml + return .plist(encoder) + } + + /// A snapshot strategy for comparing encodable structures based on their property list + /// representation. + /// + /// - Parameter encoder: A property list encoder. + public static func plist(_ encoder: Foundation.PropertyListEncoder) -> SyncSnapshot { + let snapshot = IdentitySyncSnapshot.lines.pullback { (encodable: Input) in + try String( + decoding: encoder.encode(encodable), + as: UTF8.self + ) + } + + return .init( + pathExtension: "plist", + attachmentGenerator: snapshot.attachmentGenerator, + executor: snapshot.executor + ) + } +} diff --git a/Sources/XCSnapshotTesting/Methods/Snapshot+URLRequest.swift b/Sources/XCSnapshotTesting/Methods/Snapshot+URLRequest.swift new file mode 100644 index 000000000..4b2616c9d --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/Snapshot+URLRequest.swift @@ -0,0 +1,136 @@ +#if !os(WASI) +import Foundation + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +extension SyncSnapshot where Input == URLRequest, Output == StringBytes { + /// A snapshot strategy for comparing requests based on raw equality. + /// + /// ``` swift + /// try assert(of: request, as: .raw) + /// ``` + /// + /// Records: + /// + /// ``` + /// POST http://localhost:8080/account + /// Cookie: pf_session={"userId":"1"} + /// + /// email=blob%40pointfree.co&name=Blob + /// ``` + public static var raw: SyncSnapshot { + .raw(pretty: false) + } + + /// A snapshot strategy for comparing requests based on raw equality. + /// + /// - Parameter pretty: Attempts to pretty print the body of the request (supports JSON). + public static func raw(pretty: Bool) -> SyncSnapshot { + IdentitySyncSnapshot.lines.pullback { (request: URLRequest) in + let method = + "\(request.httpMethod ?? "GET") \(request.url?.sortingQueryItems()?.absoluteString ?? "(null)")" + + let headers = (request.allHTTPHeaderFields ?? [:]) + .map { key, value in "\(key): \(value)" } + .sorted() + + let body: [String] + do { + if pretty, #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) { + body = + try request.httpBody + .map { try JSONSerialization.jsonObject(with: $0, options: []) } + .map { + try JSONSerialization.data( + withJSONObject: $0, + options: [.prettyPrinted, .sortedKeys] + ) + } + .map { + ["\n\(String(decoding: $0, as: UTF8.self))"] + } ?? [] + } else { + throw NSError(domain: "co.pointfree.Never", code: 1, userInfo: nil) + } + } catch { + body = + request.httpBody.map { + ["\n\(String(decoding: $0, as: UTF8.self))"] + } ?? [] + } + + return ([method] + headers + body).joined(separator: "\n") + } + } + + /// A snapshot strategy for comparing requests based on a cURL representation. + /// + // ``` swift + // assert(of: request, as: .curl) + // ``` + // + // Records: + // + // ``` + // curl \ + // --request POST \ + // --header "Accept: text/html" \ + // --data 'pricing[billing]=monthly&pricing[lane]=individual' \ + // "https://www.pointfree.co/subscribe" + // ``` + public static var curl: SyncSnapshot { + IdentitySyncSnapshot.lines.pullback { (request: URLRequest) in + var components = ["curl"] + + // HTTP Method + let httpMethod = request.httpMethod! + switch httpMethod { + case "GET": break + case "HEAD": components.append("--head") + default: components.append("--request \(httpMethod)") + } + + // Headers + if let headers = request.allHTTPHeaderFields { + for field in headers.keys.sorted() where field != "Cookie" { + let escapedValue = headers[field]!.replacingOccurrences(of: "\"", with: "\\\"") + components.append("--header \"\(field): \(escapedValue)\"") + } + } + + // Body + if let httpBodyData = request.httpBody, + let httpBody = String(data: httpBodyData, encoding: .utf8) + { + var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"") + escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"") + + components.append("--data \"\(escapedBody)\"") + } + + // Cookies + if let cookie = request.allHTTPHeaderFields?["Cookie"] { + let escapedValue = cookie.replacingOccurrences(of: "\"", with: "\\\"") + components.append("--cookie \"\(escapedValue)\"") + } + + // URL + components.append("\"\(request.url!.sortingQueryItems()!.absoluteString)\"") + + return components.joined(separator: " \\\n\t") + } + } +} + +extension URL { + + fileprivate func sortingQueryItems() -> URL? { + var components = URLComponents(url: self, resolvingAgainstBaseURL: false) + let sortedQueryItems = components?.queryItems?.sorted { $0.name < $1.name } + components?.queryItems = sortedQueryItems + return components?.url + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Core/DeviceOrientation.swift b/Sources/XCSnapshotTesting/Methods/UI/Core/DeviceOrientation.swift new file mode 100644 index 000000000..e3e58a708 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Core/DeviceOrientation.swift @@ -0,0 +1,73 @@ +import Foundation + +/// Represents the orientation of a device, either landscape or portrait. +/// +/// - Note: This type is `Sendable` to allow safe sharing across concurrent operations. +public enum DeviceOrientation: Sendable { + + /// A landscape orientation with an extended layout ratio. + public static let landscape: Self = .landscape(.extended) + + /// A portrait orientation with an extended layout ratio. + public static let portrait: Self = .portrait(.extended) + + /// A device orientation in landscape mode, with a specific layout ratio. + case landscape(DeviceLayoutRatio) + + /// A device orientation in portrait mode, with a specific layout ratio. + case portrait(DeviceLayoutRatio) +} + +/// Represents a device's layout ratio, with predefined values for compact, medium, regular, and extended. +/// The raw value is a Double between 0 and 1, representing the ratio. +/// - SeeAlso: DeviceOrientation +/// +/// - Note: This type is `Sendable` to allow safe sharing across concurrent operations. +public struct DeviceLayoutRatio: Sendable, RawRepresentable, ExpressibleByFloatLiteral, Hashable, Comparable { + + /// A compact layout ratio (1/3). + public static let compact: Self = .init(rawValue: 1 / 3) + + /// A medium layout ratio (0.5). + public static let medium: Self = .init(rawValue: 0.5) + + /// A regular layout ratio (2/3). + public static let regular: Self = .init(rawValue: 2 / 3) + + /// An extended layout ratio (1). + public static let extended: Self = .init(rawValue: 1) + + /// The raw value representing the layout ratio, between 0 and 1. + public let rawValue: Double + + /// Initializes a `DeviceLayoutRatio` with a given raw value. + /// - Parameter rawValue: A Double between 0 and 1. + public init(rawValue: Double) { + precondition(rawValue >= 0 && rawValue <= 1, "Raw value must be between 0 and 1") + self.rawValue = rawValue + } + + /// Initializes a `DeviceLayoutRatio` with a float literal. + /// - Parameter value: The float value to use as the raw value. + public init(floatLiteral value: Double) { + self.init(rawValue: value) + } + + /// Compares two `DeviceLayoutRatio` instances. + /// - Parameters: + /// - lhs: The left-hand side instance. + /// - rhs: The right-hand side instance. + /// - Returns: `true` if lhs is less than rhs, otherwise `false`. + public static func < (lhs: Self, rhs: Self) -> Bool { + lhs.rawValue < rhs.rawValue + } + + /// Compares two `DeviceLayoutRatio` instances. + /// - Parameters: + /// - lhs: The left-hand side instance. + /// - rhs: The right-hand side instance. + /// - Returns: `true` if lhs is greater than rhs, otherwise `false`. + public static func > (lhs: Self, rhs: Self) -> Bool { + lhs.rawValue > rhs.rawValue + } +} diff --git a/Sources/XCSnapshotTesting/Methods/UI/Core/ImageContext.swift b/Sources/XCSnapshotTesting/Methods/UI/Core/ImageContext.swift new file mode 100644 index 000000000..d5b85dee4 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Core/ImageContext.swift @@ -0,0 +1,9 @@ +#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) +import CoreGraphics + +enum ImageContext { + static let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) + static let bitsPerComponent = 8 + static let bytesPerPixel = 4 +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Core/LayoutConfiguration.swift b/Sources/XCSnapshotTesting/Methods/UI/Core/LayoutConfiguration.swift new file mode 100644 index 000000000..375bafc23 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Core/LayoutConfiguration.swift @@ -0,0 +1,884 @@ +#if os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) || os(macOS) +/// Layout configuration for rendering elements in snapshot tests. +/// +/// `LayoutConfiguration` defines properties like safe area margins, element size, and device traits +/// (e.g., orientation, screen size) to simulate different display scenarios. +/// +/// This struct helps create consistent rendering conditions for snapshot tests by specifying layout properties +/// that mimic different devices and orientations. However, it's important to note that this configuration +/// only simulates visual conditions and does not actually execute tests on the specified platform. Due to +/// inherent differences in layout engines across platforms (iOS, macOS, tvOS), test results should be +/// validated on the target platform to account for framework-specific behaviors (UIKit, SwiftUI, AppKit). +/// +/// - Note: This struct encapsulates screen size, safe area insets, and device-specific traits to enable +/// responsive UI layouts across iOS, iPadOS, and tvOS. It provides a way to standardize testing conditions +/// while acknowledging that final visual verification should occur on the actual target platform. +/// - SeeAlso: `LayoutConfiguration.iPadPro12_9`, `LayoutConfiguration.iPhone13`, `LayoutConfiguration.tv` +public struct LayoutConfiguration: Sendable { + + // MARK: - Internal static methods + + #if os(macOS) + static func resolve(_ snapshotLayout: SnapshotLayout) -> LayoutConfiguration { + switch snapshotLayout { + case .sizeThatFits: + return .init(safeArea: .init(), size: nil) + case .fixed(let width, let height): + let size = CGSize(width: width, height: height) + return .init(safeArea: .init(), size: size) + } + } + #elseif os(iOS) || os(tvOS) || os(visionOS) + static func resolve( + _ snapshotLayout: SnapshotLayout, + with traits: Traits + ) -> LayoutConfiguration { + switch snapshotLayout { + case .device(let deviceConfig): + return .init( + safeArea: deviceConfig.safeArea, + size: deviceConfig.size, + traits: deviceConfig.traits.merging(traits) + ) + case .sizeThatFits: + return .init(safeArea: .zero, size: nil, traits: traits) + case .fixed(let width, let height): + let size = CGSize(width: width, height: height) + return .init(safeArea: .zero, size: size, traits: traits) + } + } + #else + static func resolve(_ snapshotLayout: SnapshotLayout) -> LayoutConfiguration { + switch snapshotLayout { + case .sizeThatFits: + return .init(size: nil) + case .fixed(let width, let height): + let size = CGSize(width: width, height: height) + return .init(size: size) + } + } + #endif + + // MARK: - Public properties + + #if os(macOS) + /// Margins for safe area layout (e.g., device notches or status bars). + /// + /// Default value: `.zero` (no additional margins). + public let safeArea: NSEdgeInsets + #elseif !os(watchOS) + /// Margins for safe area layout (e.g., device notches or status bars). + /// + /// Default value: `.zero` (no additional margins). + public let safeArea: UIEdgeInsets + #endif + + /// Size of the element to render. + /// + /// When `nil`, the element uses its intrinsic content size. + public let size: CGSize? + + #if os(iOS) || os(tvOS) || os(visionOS) + /// Collection of UI traits like orientation, device size, and dark mode. + /// + /// Default value: `Traits()` (system default values). + public let traits: Traits + #endif + + // MARK: - Inits + #if os(macOS) + /// Initializes a `LayoutConfiguration` with the specified safe area and size. + /// + /// - Parameters: + /// - safeArea: The edge insets representing the safe area of the layout. + /// - size: The dimensions of the layout's viewing area. + public init( + safeArea: NSEdgeInsets = .init(), + size: CGSize? = nil + ) { + self.safeArea = safeArea + self.size = size + } + #elseif os(iOS) || os(tvOS) || os(visionOS) + /// Initializes a layout configuration with default or custom values. + /// + /// - Parameters: + /// - safeArea: Margins for safe area (e.g., iPhone notch spacing). + /// - size: Desired element size (optional). + /// - traits: UI characteristics (e.g., portrait/landscape orientation). + public init( + safeArea: UIEdgeInsets = .zero, + size: CGSize? = nil, + traits: Traits = .init() + ) { + self.safeArea = safeArea + self.size = size + self.traits = traits + } + #else + /// Initializes a layout configuration with default values. + /// + /// - Parameters: + /// - size: Desired element size (optional). + public init( + size: CGSize? = nil + ) { + self.size = size + } + #endif +} + +#if os(iOS) || os(tvOS) || os(visionOS) + +extension LayoutConfiguration { + + fileprivate struct EdgeInsets { + + static var zero: Self { + .init( + top: .zero, + left: .zero, + bottom: .zero, + right: .zero + ) + } + + let top: CGFloat? + let left: CGFloat? + let bottom: CGFloat? + let right: CGFloat? + + init( + top: CGFloat? = nil, + left: CGFloat? = nil, + bottom: CGFloat? = nil, + right: CGFloat? = nil + ) { + self.top = top + self.left = left + self.bottom = bottom + self.right = right + } + } +} + +// MARK: - iPhone 16 +extension LayoutConfiguration { + + public static let iPhone16ProMax: LayoutConfiguration = .iPhone16ProMax(.portrait) + + public static func iPhone16ProMax(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_9, + portraitSafeArea: EdgeInsets(top: 62, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone16ProMax + ) + } + + public static let iPhone16Pro: LayoutConfiguration = .iPhone16Pro(.portrait) + + public static func iPhone16Pro(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_3, + portraitSafeArea: EdgeInsets(top: 62, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone16Pro + ) + } + + public static let iPhone16Plus: LayoutConfiguration = .iPhone16Plus(.portrait) + + public static func iPhone16Plus(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_7v2, + portraitSafeArea: EdgeInsets(top: 59, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone16Plus + ) + } + + public static let iPhone16: LayoutConfiguration = .iPhone16(.portrait) + + public static func iPhone16(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v2, + portraitSafeArea: EdgeInsets(top: 59, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone16 + ) + } +} + +// MARK: - iPhone 15 +extension LayoutConfiguration { + + public static let iPhone15ProMax: LayoutConfiguration = .iPhone15ProMax(.portrait) + + public static func iPhone15ProMax(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_7v2, + portraitSafeArea: EdgeInsets(top: 59, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone15ProMax + ) + } + + public static let iPhone15Pro: LayoutConfiguration = .iPhone15Pro(.portrait) + + public static func iPhone15Pro(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v2, + portraitSafeArea: EdgeInsets(top: 59, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone15Pro + ) + } + + public static let iPhone15Plus: LayoutConfiguration = .iPhone15Plus(.portrait) + + public static func iPhone15Plus(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_7v2, + portraitSafeArea: EdgeInsets(top: 59, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone15Plus + ) + } + + public static let iPhone15: LayoutConfiguration = .iPhone15(.portrait) + + public static func iPhone15(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v2, + portraitSafeArea: EdgeInsets(top: 59, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone15 + ) + } +} + +// MARK: - iPhone 14 +extension LayoutConfiguration { + + public static let iPhone14ProMax: LayoutConfiguration = .iPhone14ProMax(.portrait) + + public static func iPhone14ProMax(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_7v2, + portraitSafeArea: EdgeInsets(top: 59, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone14ProMax + ) + } + + public static let iPhone14Pro: LayoutConfiguration = .iPhone14Pro(.portrait) + + public static func iPhone14Pro(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v2, + portraitSafeArea: EdgeInsets(top: 59, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone14Pro + ) + } + + public static let iPhone14Plus: LayoutConfiguration = .iPhone14Plus(.portrait) + + public static func iPhone14Plus(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_7v1, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone14Plus + ) + } + + public static let iPhone14: LayoutConfiguration = .iPhone14(.portrait) + + public static func iPhone14(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v1, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone14 + ) + } +} + +// MARK: - iPhone 13 +extension LayoutConfiguration { + + public static let iPhone13ProMax: LayoutConfiguration = .iPhone13ProMax(.portrait) + + public static func iPhone13ProMax(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_7v1, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone13ProMax + ) + } + + public static let iPhone13Pro: LayoutConfiguration = .iPhone13Pro(.portrait) + + public static func iPhone13Pro(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v1, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone13Pro + ) + } + + public static let iPhone13: LayoutConfiguration = .iPhone13(.portrait) + + public static func iPhone13(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v1, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone13 + ) + } + + public static let iPhone13Mini: LayoutConfiguration = .iPhone13Mini(.portrait) + + public static func iPhone13Mini(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen5_4, + portraitSafeArea: EdgeInsets(top: 50, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone13Mini + ) + } +} + +// MARK: - iPhone 12 +extension LayoutConfiguration { + + public static let iPhone12ProMax: LayoutConfiguration = .iPhone12ProMax(.portrait) + + public static func iPhone12ProMax(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_7v1, + portraitSafeArea: EdgeInsets(top: 50, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone12ProMax + ) + } + + public static let iPhone12Pro: LayoutConfiguration = .iPhone12Pro(.portrait) + + public static func iPhone12Pro(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v1, + portraitSafeArea: EdgeInsets(top: 50, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone12Pro + ) + } + + public static let iPhone12: LayoutConfiguration = .iPhone12(.portrait) + + public static func iPhone12(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v1, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone12 + ) + } + + public static let iPhone12Mini: LayoutConfiguration = .iPhone12Mini(.portrait) + + public static func iPhone12Mini(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen5_4, + portraitSafeArea: EdgeInsets(top: 50, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone12Mini + ) + } +} + +// MARK: - iPhone 11 +extension LayoutConfiguration { + + public static let iPhone11ProMax: LayoutConfiguration = .iPhone11ProMax(.portrait) + + public static func iPhone11ProMax(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_5, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone11ProMax + ) + } + + public static let iPhone11Pro: LayoutConfiguration = .iPhone11Pro(.portrait) + + public static func iPhone11Pro(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen5_8, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone11Pro + ) + } + + public static let iPhone11: LayoutConfiguration = .iPhone11(.portrait) + + public static func iPhone11(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v3, + portraitSafeArea: EdgeInsets(top: 47, bottom: 34, right: 0), + orientation: orientation, + displayScale: 2, + deviceInterfaceSizeClass: .iPhone11ProMax + ) + } +} + +// MARK: - iPhone X +extension LayoutConfiguration { + + public static let iPhoneXR: LayoutConfiguration = .iPhoneXR(.portrait) + + public static func iPhoneXR(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_1v3, + portraitSafeArea: EdgeInsets(top: 44, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhoneXR + ) + } + + public static let iPhoneXSMax: LayoutConfiguration = .iPhoneXSMax(.portrait) + + public static func iPhoneXSMax(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen6_5, + portraitSafeArea: EdgeInsets(top: 44, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhoneXSMax + ) + } + + public static let iPhoneXS: LayoutConfiguration = .iPhoneXS(.portrait) + + public static func iPhoneXS(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen5_8, + portraitSafeArea: EdgeInsets(top: 44, bottom: 34, right: 0), + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhoneXS + ) + } + + public static let iPhoneX: LayoutConfiguration = .iPhoneX(.portrait) + + public static func iPhoneX(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen5_8, + portraitSafeArea: EdgeInsets(top: 44, bottom: 34, right: 0), + orientation: orientation, + displayScale: 2, + deviceInterfaceSizeClass: .iPhoneX + ) + } +} + +// MARK: - iPhone 8 +extension LayoutConfiguration { + + public static let iPhone8: LayoutConfiguration = .iPhone8(.portrait) + + public static func iPhone8(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen4_7, + portraitSafeArea: EdgeInsets(top: 20), + landscapeSafeArea: EdgeInsets.zero, + orientation: orientation, + displayScale: 2, + deviceInterfaceSizeClass: .iPhone8 + ) + } + + public static let iPhone8Plus: LayoutConfiguration = .iPhone8Plus(.portrait) + + public static func iPhone8Plus(_ orientation: DeviceOrientation) -> LayoutConfiguration { + iPhone( + size: .screen5_5, + portraitSafeArea: EdgeInsets(top: 20), + landscapeSafeArea: EdgeInsets.zero, + orientation: orientation, + displayScale: 3, + deviceInterfaceSizeClass: .iPhone8Plus + ) + } +} + +// MARK: - iPhone SE +extension LayoutConfiguration { + + public static let iPhoneSE: LayoutConfiguration = .iPhoneSE(.portrait) + + public static func iPhoneSE( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPhone( + size: .screen4_7, + portraitSafeArea: EdgeInsets(top: 20), + landscapeSafeArea: .zero, + orientation: orientation, + displayScale: 2, + deviceInterfaceSizeClass: .iPhoneSE + ) + } +} + +// MARK: - iPhone +extension LayoutConfiguration { + + fileprivate static func iPhone( + size: CGSize, + portraitSafeArea: EdgeInsets, + landscapeSafeArea: EdgeInsets = .init(bottom: 21), + orientation: DeviceOrientation, + displayScale: CGFloat, + deviceInterfaceSizeClass: DeviceDynamicInterfaceSizeClass + ) -> LayoutConfiguration { + let safeArea: UIEdgeInsets + let screenSize: CGSize + + switch orientation { + case .portrait(let ratio): + precondition(ratio == .extended) + + safeArea = .init( + top: portraitSafeArea.top ?? .zero, + left: portraitSafeArea.left ?? .zero, + bottom: portraitSafeArea.bottom ?? .zero, + right: portraitSafeArea.right ?? .zero + ) + screenSize = size + case .landscape(let ratio): + precondition(ratio == .extended) + + safeArea = UIEdgeInsets( + top: landscapeSafeArea.top ?? .zero, + left: landscapeSafeArea.left ?? portraitSafeArea.top ?? .zero, + bottom: landscapeSafeArea.bottom ?? 21, + right: landscapeSafeArea.right ?? portraitSafeArea.top ?? .zero + ) + screenSize = size.reflected() + } + + return .init( + safeArea: safeArea, + size: screenSize, + traits: .iOS( + displayScale: displayScale, + size: screenSize, + deviceInterfaceSizeClass: deviceInterfaceSizeClass + ) + ) + } +} + +// MARK: - iPad Pro +extension LayoutConfiguration { + + public static let iPadPro12_9 = iPadPro12_9(.landscape) + + public static func iPadPro12_9( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 1_024, height: 1_366), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPadPro11 = iPadPro11(.landscape) + + public static func iPadPro11( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 834, height: 1_194), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPadPro10_5 = iPadPro10_5(.landscape) + + public static func iPadPro10_5( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 834, height: 1_194), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPadPro9_7 = iPadPro9_7(.landscape) + + public static func iPadPro9_7( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 768, height: 1_024), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } +} + +// MARK: - iPad Air +extension LayoutConfiguration { + + public static let iPadAir13 = iPadAir13(.landscape) + + public static func iPadAir13( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 1_024, height: 1_366), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPadAir11 = iPadAir11(.landscape) + + public static func iPadAir11( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 820, height: 1_180), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPadAir10_9 = iPadAir10_9(.landscape) + + public static func iPadAir10_9( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 820, height: 1_180), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPadAir10_5 = iPadAir10_5(.landscape) + + public static func iPadAir10_5( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 820, height: 1_180), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPadAir9_7 = iPadAir9_7(.landscape) + + public static func iPadAir9_7( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 768, height: 1_024), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } +} + +// MARK: - iPad +extension LayoutConfiguration { + + public static let iPad11 = iPad11(.landscape) + + public static func iPad11( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 820, height: 1_180), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPad10_2 = iPad10_2(.landscape) + + public static func iPad10_2( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 810, height: 1_080), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPad9_7 = iPad9_7(.landscape) + + public static func iPad9_7( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 768, height: 1_024), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } +} + +// MARK: - iPad Mini +extension LayoutConfiguration { + + public static let iPadMini8_3 = iPadMini8_3(.landscape) + + public static func iPadMini8_3( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 744, height: 1_133), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } + + public static let iPadMini7_9 = iPadMini7_9(.landscape) + + public static func iPadMini7_9( + _ orientation: DeviceOrientation + ) -> LayoutConfiguration { + iPad( + orientation: orientation, + size: CGSize(width: 768, height: 1_024), + displayScale: 2, + deviceInterfaceSizeClass: .iPadOS + ) + } +} + +// MARK: - iPad Method +extension LayoutConfiguration { + + fileprivate static func iPad( + orientation: DeviceOrientation, + size: CGSize, + displayScale: CGFloat, + deviceInterfaceSizeClass: DeviceDynamicInterfaceSizeClass + ) -> LayoutConfiguration { + var deviceSize: CGSize + let deviceRatio: DeviceLayoutRatio + + switch orientation { + case .landscape(let ratio): + deviceRatio = ratio + deviceSize = size.reflected() + case .portrait(let ratio): + deviceRatio = ratio + deviceSize = size + } + + deviceSize.width *= deviceRatio.rawValue + + return LayoutConfiguration( + safeArea: UIEdgeInsets(top: 24, left: .zero, bottom: 20, right: .zero), + size: deviceSize, + traits: .iPadOS( + displayScale: displayScale, + size: deviceSize, + deviceInterfaceSizeClass: deviceInterfaceSizeClass + ) + ) + } +} + +extension LayoutConfiguration { + + public static let tv = tvOS( + displayScale: 1, + deviceInterfaceSizeClass: .tvOS + ) + + public static let tv4K = tvOS( + displayScale: 2, + deviceInterfaceSizeClass: .tvOS + ) + + private static func tvOS( + displayScale: CGFloat, + deviceInterfaceSizeClass: DeviceDynamicInterfaceSizeClass + ) -> LayoutConfiguration { + let size = CGSize(width: 1920, height: 1080) + + return LayoutConfiguration( + safeArea: .init(top: 60, left: 80, bottom: 60, right: 80), + size: size, + traits: .tvOS( + displayScale: displayScale, + size: size, + deviceInterfaceSizeClass: deviceInterfaceSizeClass + ) + ) + } +} +#endif +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Core/SizeListener.swift b/Sources/XCSnapshotTesting/Methods/UI/Core/SizeListener.swift new file mode 100644 index 000000000..dbdc6a0e6 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Core/SizeListener.swift @@ -0,0 +1,139 @@ +#if canImport(SwiftUI) +import SwiftUI + +#if os(iOS) || os(tvOS) || os(watchOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) +@MainActor +protocol SizeListenerDelegate: AnyObject { + + func viewDidUpdateSize(_ id: ObjectIdentifier, size: CGSize) +} + +@MainActor +class SizeListener { + + var id: ObjectIdentifier { + ObjectIdentifier(self) + } + + weak var delegate: SizeListenerDelegate? { + willSet { updateSize(size) } + } + + private(set) var size: CGSize = .zero + fileprivate weak var owningView: SDKView? + + init() {} + + fileprivate func updateSize(_ size: CGSize) { + guard self.size != size else { + return + } + + self.size = size + delegate?.viewDidUpdateSize(id, size: size) + } + + func dispose() { + owningView?.removeFromSuperview() + } +} + +// MARK: - UIView Extensions + +@MainActor +private class UIViewSizeListener: SDKView { + + let listener: SizeListener + + init(listener: SizeListener) { + self.listener = listener + super.init(frame: .zero) + listener.owningView = self + } + + required init?(coder: NSCoder) { + nil + } + + #if os(macOS) + override func layout() { + super.layout() + guard window != nil, let superview else { + return + } + + listener.updateSize(superview.bounds.size) + } + #else + override func layoutSubviews() { + super.layoutSubviews() + listener.updateSize(bounds.size) + } + #endif +} + +@MainActor +extension SDKView { + + func addSizeListener(_ listener: SizeListener) { + let view = UIViewSizeListener(listener: listener) + view.translatesAutoresizingMaskIntoConstraints = false + #if os(macOS) + addSubview(view, positioned: .below, relativeTo: subviews.first) + #else + insertSubview(view, at: .zero) + #endif + if #available(macOS 11, *) { + NSLayoutConstraint.activate([ + view.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor), + view.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor), + view.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor), + view.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor), + ]) + } else { + NSLayoutConstraint.activate([ + view.topAnchor.constraint(equalTo: topAnchor), + view.bottomAnchor.constraint(equalTo: bottomAnchor), + view.leadingAnchor.constraint(equalTo: leadingAnchor), + view.trailingAnchor.constraint(equalTo: trailingAnchor), + ]) + } + } +} + +// MARK: - SwiftUI Extensions + +private struct ViewSizeListener: ViewModifier { + + let listener: SizeListener + + func body(content: Content) -> some View { + content + .background( + GeometryReader { proxy -> Color in + let size = proxy.size + + Task { @MainActor in + listener.updateSize(size) + } + + return Color.black.opacity(.zero) + } + ) + } +} + +extension View { + + func sizeListener(_ listener: SizeListener) -> some View { + modifier(ViewSizeListener(listener: listener)) + } +} +#endif +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotLayout.swift b/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotLayout.swift new file mode 100644 index 000000000..d3e79695a --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotLayout.swift @@ -0,0 +1,39 @@ +#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) +import CoreGraphics + +/// Defines how a UI component's layout is configured during snapshot testing. +public enum SnapshotLayout { + + #if os(iOS) || os(tvOS) || os(visionOS) + /// Renders the component using a specific device configuration. + /// + /// - Parameter configuration: Layout configuration defining safe area margins, size, + /// and UI traits (e.g., orientation). + /// + /// Example: + /// ```swift + /// let layout = .device(.iPhone15ProMax) + /// ``` + case device(LayoutConfiguration) + #endif + + /// Renders the component with an explicit fixed size. + /// + /// Useful for ensuring test consistency across devices or configurations. + /// + /// - Parameters: + /// - width: Width in points. + /// - height: Height in points. + /// + /// Example: + /// ```swift + /// let layout = .fixed(width: 375, height: 812) // iPhone 12 dimensions + /// ``` + case fixed(width: CGFloat, height: CGFloat) + + /// Renders the component using its natural intrinsic content size. + /// + /// Ideal for content-adaptive components like labels or dynamic collections. + case sizeThatFits +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotUIController.swift b/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotUIController.swift new file mode 100644 index 000000000..92779d8e1 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotUIController.swift @@ -0,0 +1,472 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +import SwiftUI +import SceneKit +import SpriteKit +#elseif os(macOS) +@preconcurrency import AppKit +import SwiftUI +import SceneKit +import SpriteKit +#endif + +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) +@MainActor +class SnapshotUIController: SDKViewController { + + // MARK: - Internal properties + + #if os(iOS) || os(tvOS) || os(visionOS) + override var shouldAutomaticallyForwardAppearanceMethods: Bool { + true + } + #endif + + var configuration: LayoutConfiguration { + snapshotView.configuration + } + + private let snapshotView: SnapshotView + + // MARK: - Private properties + + private var childConstraints = [NSLayoutConstraint]() + private let childController: SDKViewController + private let childSizeListener: SizeListener + + private var isWaitingSnapshotSignal = false + private let snapshotSignal = AsyncSignal() + + // MARK: - Inits + + init(_ view: SDKView, with configuration: LayoutConfiguration) { + let sizeListener = SizeListener() + view.addSizeListener(sizeListener) + self.childController = view.withController() + self.childSizeListener = sizeListener + self.snapshotView = .init(configuration: configuration) + super.init(nibName: nil, bundle: nil) + } + + init(_ viewController: SDKViewController, with configuration: LayoutConfiguration) { + let sizeListener = SizeListener() + viewController.view.addSizeListener(sizeListener) + self.childController = viewController + self.childSizeListener = sizeListener + self.snapshotView = .init(configuration: configuration) + super.init(nibName: nil, bundle: nil) + } + + init(_ content: Content, with configuration: LayoutConfiguration) { + func size(_ keyPath: KeyPath) -> CGFloat? { + guard let size = configuration.size else { + return nil + } + + let value = size[keyPath: keyPath] + return value == .zero ? nil : value + } + + let sizeListener = SizeListener() + let rootView = + content + .frame(width: size(\.width), height: size(\.height)) + .sizeListener(sizeListener) + #if os(macOS) + let viewController = NSHostingController(rootView: rootView) + #else + let viewController = UIHostingController(rootView: rootView) + #endif + self.childController = viewController + self.childSizeListener = sizeListener + self.snapshotView = .init(configuration: configuration) + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + nil + } + + // MARK: - Super methods + + override func loadView() { + view = snapshotView + snapshotView.translatesAutoresizingMaskIntoConstraints = false + } + + override func viewDidLoad() { + super.viewDidLoad() + attachChild() + #if !os(macOS) + configuration.traits.commit(in: self) + #endif + } + + #if os(macOS) + override func viewDidLayout() { + super.viewDidLayout() + if isWaitingSnapshotSignal { + isWaitingSnapshotSignal = false + + Task { + await snapshotSignal.signal() + } + } + } + + override func viewWillAppear() { + super.viewWillAppear() + + let trackingArea = NSTrackingArea( + rect: view.bounds, + options: [.mouseEnteredAndExited, .activeInKeyWindow], + owner: view + ) + + view.addTrackingArea(trackingArea) + } + + override func viewWillDisappear() { + super.viewWillDisappear() + + for trackingArea in view.trackingAreas { + view.removeTrackingArea(trackingArea) + } + } + #else + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + if isWaitingSnapshotSignal { + isWaitingSnapshotSignal = false + + Task { + await snapshotSignal.signal() + } + } + } + #if os(iOS) + override func shouldAutomaticallyForwardRotationMethods() -> Bool { + true + } + #endif + #endif + + // MARK: - Internal methods + + func layoutIfNeeded() { + #if os(macOS) + let view = childController.view + #else + let view: UIView = childController.view ?? view + #endif + + let size = view.frame.size + if size.height == .zero || size.width == .zero { + #if os(macOS) + view.needsLayout = true + view.layoutSubtreeIfNeeded() + #else + view.setNeedsLayout() + view.layoutIfNeeded() + #endif + } + } + + func snapshot() async throws -> SDKImage { + #if !os(macOS) + let traits = configuration.traits + #endif + + isWaitingSnapshotSignal = true + + #if os(macOS) + childController.view.needsLayout = true + view.needsLayout = true + view.layoutSubtreeIfNeeded() + #else + view.recursiveNeedsLayout() + view.layoutIfNeeded() + #endif + + try await snapshotSignal.wait() + await snapshotSignal.lock() + + if let sceneView = childController.view as? SCNView { + return sceneView.snapshot() + } + + if let skView = childController.view as? SKView, + let scene = skView.scene, + let image = skView.texture(from: scene)?.cgImage() + { + #if os(macOS) + return .init( + cgImage: image, + size: CGSize( + width: image.width, + height: image.height + ) + ) + #else + return .init(cgImage: image) + #endif + } + + #if os(macOS) + return try snapshot(view) + #else + return try snapshot(view, with: traits) + #endif + } + + enum DescriptorMethod: String { + #if os(macOS) + case subtreeDescription = "_subtreeDescription" + #else + case hierarchy = "_printHierarchy" + case recursiveDescription = "recursiveDescription" + #endif + + @MainActor + fileprivate func callAsFunction(_ viewController: SDKViewController) -> String { + let reference: NSObject + + switch self { + #if os(macOS) + case .subtreeDescription: + reference = viewController.view + #else + case .hierarchy: + reference = viewController + case .recursiveDescription: + reference = viewController.view + #endif + } + + return + (reference + .perform(Selector(rawValue)) + .retain() + .takeUnretainedValue() as! String).sanitizingPointersReferences() + } + } + + func descriptor(_ method: DescriptorMethod) async throws -> String { + isWaitingSnapshotSignal = true + + #if os(macOS) + childController.view.needsLayout = true + view.needsLayout = true + view.layoutSubtreeIfNeeded() + #else + childController.view.setNeedsLayout() + view.setNeedsLayout() + view.layoutIfNeeded() + #endif + + try await snapshotSignal.wait() + await snapshotSignal.lock() + + return method(childController) + } + + func detachChild() { + defer { snapshotView.dispose() } + + NSLayoutConstraint.deactivate(childConstraints) + #if !os(macOS) + childController.additionalSafeAreaInsets = .zero + #endif + + #if !os(macOS) + childController.willMove(toParent: nil) + #endif + childController.view.removeFromSuperview() + childController.removeFromParent() + } + + // MARK: - Private methods + + private func attachChild() { + addChild(childController) + snapshotView.add(childController.view, with: childSizeListener) + #if !os(macOS) + childController.didMove(toParent: self) + #endif + + let heightAnchor = childController.view.heightAnchor.constraint( + equalTo: view.heightAnchor, + multiplier: 1 + ) + + let widthAnchor = childController.view.widthAnchor.constraint( + equalTo: view.widthAnchor, + multiplier: 1 + ) + + #if os(macOS) + heightAnchor.priority = .fittingSizeCompression + widthAnchor.priority = .fittingSizeCompression + #else + heightAnchor.priority = .fittingSizeLevel + widthAnchor.priority = .fittingSizeLevel + #endif + + NSLayoutConstraint.activate( + [ + heightAnchor, + widthAnchor, + ], + storingAt: &childConstraints + ) + + setupSizeConstraints() + + #if !os(macOS) + childController.additionalSafeAreaInsets = configuration.safeArea + #endif + } + + private func setupSizeConstraints() { + let size = configuration.size ?? .zero + + if size.height > .zero { + childController.view.setContentHuggingPriority(.defaultLow, for: .vertical) + childController.view.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) + + let heightAnchor = childController.view.heightAnchor.constraint( + equalToConstant: size.height + ) + + heightAnchor.priority = .required + + NSLayoutConstraint.activate( + [ + heightAnchor + ], + storingAt: &childConstraints + ) + } else { + childController.view.setContentHuggingPriority(.required, for: .vertical) + childController.view.setContentCompressionResistancePriority(.required, for: .vertical) + } + + if size.width > .zero { + childController.view.setContentHuggingPriority(.defaultLow, for: .horizontal) + childController.view.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + + let widthAnchor = childController.view.widthAnchor.constraint( + equalToConstant: size.width + ) + + widthAnchor.priority = .required + + NSLayoutConstraint.activate( + [ + widthAnchor + ], + storingAt: &childConstraints + ) + } else { + childController.view.setContentHuggingPriority(.required, for: .horizontal) + childController.view.setContentCompressionResistancePriority(.required, for: .horizontal) + } + } + + #if os(macOS) + private func snapshot(_ view: SDKView) throws -> SDKImage { + let bounds = snapshotView.calculateContentFrame() + guard let rep = view.bitmapImageRepForCachingDisplay(in: bounds) else { + throw RenderingError() + } + + view.cacheDisplay(in: bounds, to: rep) + + let snapshot = NSImage(size: rep.size) + snapshot.addRepresentation(rep) + return snapshot + } + #else + private func snapshot( + _ view: SDKView, + with traits: Traits + ) throws -> SDKImage { + let bounds = snapshotView.calculateContentFrame() + + let format = UIGraphicsImageRendererFormat( + for: traits() + ) + + #if os(visionOS) + format.scale = traits.displayScale + #else + format.scale = view.window?.screen.scale ?? traits.displayScale + #endif + + let renderer = UIGraphicsImageRenderer( + bounds: bounds, + format: format + ) + + return renderer.image { + view.layer.render(in: $0.cgContext) + } + } + #endif +} +#endif + +#if os(macOS) +import CoreGraphics +func CGContextCreateBitmapContext(size: CGSize, opaque: Bool, scale: CGFloat) -> CGContext? { + var scale = scale + + if scale == .zero { + // Match `UIGraphicsBeginImageContextWithOptions`, reset to the scale factor of the device’s main screen if scale is 0. + scale = NSScreen.main?.backingScaleFactor ?? 1 + } + + let width = ceil(size.width * scale) + let height = ceil(size.height * scale) + + guard width >= 1, height >= 1 else { + return nil + } + + guard let space = NSScreen.main?.colorSpace?.cgColorSpace else { + return nil + } + // kCGImageAlphaNone is not supported in CGBitmapContextCreate. + // Check #3330 for more detail about why this bitmap is choosen. + // From v5.17.0, use runtime detection of bitmap info instead of hardcode. + // However, macOS's runtime detection will also call this function, cause recursive, so still hardcode here + let bitmapInfo: CGBitmapInfo + if !opaque { + // [NSImage imageWithSize:flipped:drawingHandler:] returns float(16-bits) RGBA8888 on alpha image, which we don't need + bitmapInfo = [ + .byteOrderDefault, .init(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue), + ] + } else { + bitmapInfo = [.byteOrderDefault, .init(rawValue: CGImageAlphaInfo.noneSkipLast.rawValue)] + } + + guard + let context = CGContext( + data: nil, + width: Int(width), + height: Int(height), + bitsPerComponent: 8, + bytesPerRow: .zero, + space: space, + bitmapInfo: bitmapInfo.rawValue + ) + else { return nil } + + context.scaleBy(x: scale, y: scale) + + return context +} + +struct RenderingError: Error {} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotView.swift b/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotView.swift new file mode 100644 index 000000000..5793b9422 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Core/SnapshotView.swift @@ -0,0 +1,172 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#elseif os(macOS) +import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) || os(macOS) +class SnapshotView: SDKView { + + let configuration: LayoutConfiguration + + private var sizableViews: [ObjectIdentifier: (SDKView, SizeListener)] = [:] + + init(configuration: LayoutConfiguration) { + self.configuration = configuration + super.init(frame: .zero) + #if os(macOS) + wantsLayer = true + #endif + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + #if os(macOS) + override func layout() { + super.layout() + self.updateTransformations() + } + #else + override func safeAreaInsetsDidChange() { + super.safeAreaInsetsDidChange() + self.updateTransformations() + } + + override func layoutSubviews() { + super.layoutSubviews() + self.updateTransformations() + } + #endif + + func dispose() { + defer { sizableViews = [:] } + + for (transformableView, sizeListener) in sizableViews.values { + sizeListener.dispose() + transformableView.removeFromSuperview() + } + } + + func add(_ view: SDKView, with sizeListener: SizeListener) { + defer { sizeListener.delegate = self } + + let transformableView = SDKView() + let containerView = SDKView() + + view.translatesAutoresizingMaskIntoConstraints = false + transformableView.translatesAutoresizingMaskIntoConstraints = false + containerView.translatesAutoresizingMaskIntoConstraints = false + + containerView.addSubview(view) + transformableView.addSubview(containerView) + + super.addSubview(transformableView) + + NSLayoutConstraint.activate([ + transformableView.topAnchor.constraint(equalTo: topAnchor), + transformableView.leadingAnchor.constraint(equalTo: leadingAnchor), + transformableView.bottomAnchor.constraint(equalTo: bottomAnchor), + transformableView.trailingAnchor.constraint(equalTo: trailingAnchor), + ]) + + NSLayoutConstraint.activate([ + view.centerXAnchor.constraint(equalTo: transformableView.centerXAnchor), + view.centerYAnchor.constraint(equalTo: transformableView.centerYAnchor), + ]) + + NSLayoutConstraint.activate([ + view.topAnchor.constraint(equalTo: containerView.topAnchor), + view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), + view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), + view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), + ]) + + sizableViews[sizeListener.id] = (transformableView, sizeListener) + } + + func calculateContentFrame() -> CGRect { + var contentSize = sizableViews.values.reduce(CGSize.zero) { + CGSize( + width: max($0.width, $1.1.size.width), + height: max($0.height, $1.1.size.height) + ) + } + + let safeArea = configuration.safeArea + + contentSize.width += safeArea.left + safeArea.right + contentSize.height += safeArea.top + safeArea.bottom + + let scale = self.scale(for: contentSize) + + contentSize.width *= scale + contentSize.height *= scale + + return CGRect( + x: bounds.midX - contentSize.width / 2, + y: bounds.midY - contentSize.height / 2, + width: contentSize.width, + height: contentSize.height + ) + } + + private func updateTransformations() { + for (transformableView, sizeListener) in sizableViews.values { + downscale(transformableView, with: sizeListener.size) + } + } + + private func downscale(_ transformableView: SDKView, with size: CGSize) { + let safeArea = configuration.safeArea + let scale = scale( + for: CGSize( + width: size.width + safeArea.left + safeArea.right, + height: size.height + safeArea.top + safeArea.bottom + ) + ) + #if os(macOS) + self.layer?.contentsScale = scale + #else + transformableView.transform = CGAffineTransform(scaleX: scale, y: scale) + #endif + transformableView.recursiveNeedsLayout() + #if os(macOS) + needsLayout = true + #else + transformableView.layoutIfNeeded() + #endif + } + + private func scale(for size: CGSize) -> CGFloat { + guard frame.size.height > .zero && frame.size.width > .zero else { + return 1 + } + + let proposedSize: CGSize + + if #available(macOS 11, *) { + proposedSize = CGSize( + width: frame.size.width - (safeAreaInsets.left + safeAreaInsets.right), + height: frame.size.height - (safeAreaInsets.top + safeAreaInsets.bottom) + ) + } else { + proposedSize = frame.size + } + + return proposedSize.scaleThatFits(size) + } +} + +extension SnapshotView: SizeListenerDelegate { + + func viewDidUpdateSize(_ id: ObjectIdentifier, size: CGSize) { + guard let (transformableView, _) = sizableViews[id] else { + return + } + + downscale(transformableView, with: size) + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Core/ViewOperationPayload.swift b/Sources/XCSnapshotTesting/Methods/UI/Core/ViewOperationPayload.swift new file mode 100644 index 000000000..412a3b70e --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Core/ViewOperationPayload.swift @@ -0,0 +1,109 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) +@MainActor +struct ViewOperationPayload { + let previousRootViewController: SDKViewController? + let window: SDKWindow + let input: SnapshotUIController +} + +extension Async where Output == SnapshotUIController { + + func connectToWindow( + _ configuration: SnapshotWindowConfiguration + ) -> Async< + Input, ViewOperationPayload + > { + map { @MainActor in + ViewOperationPayload( + previousRootViewController: configuration.window.switchRoot($0), + window: configuration.window, + input: $0 + ) + } + } +} + +extension Async where Output == ViewOperationPayload { + + func waitLoadingStateIfNeeded(tolerance: TimeInterval) -> Async { + map { + await $0.input.view.waitLoadingStateIfNeeded(tolerance: tolerance) + return $0 + } + } + + func layoutIfNeeded() -> Async { + map { @MainActor in + $0.input.layoutIfNeeded() + return $0 + } + } + + func snapshot( + _ executor: Sync + ) -> Async { + map { @MainActor payload in + let image = try await executor( + payload.input.snapshot() + ) + + payload.window.removeRootViewController() + + #if os(macOS) + payload.window.contentViewController = payload.previousRootViewController + #else + payload.window.rootViewController = payload.previousRootViewController + #endif + + payload.input.detachChild() + + if !payload.window.isKeyWindow { + #if os(macOS) + payload.window.close() + #else + payload.window.isHidden = true + #endif + } + + return image + } + } + + func descriptor( + _ executor: Sync, + method: SnapshotUIController.DescriptorMethod + ) -> Async { + map { @MainActor payload in + let string = try await executor( + payload.input.descriptor(method) + ) + + payload.window.removeRootViewController() + + #if os(macOS) + payload.window.contentViewController = payload.previousRootViewController + #else + payload.window.rootViewController = payload.previousRootViewController + #endif + + payload.input.detachChild() + + if !payload.window.isKeyWindow { + #if os(macOS) + payload.window.close() + #else + payload.window.isHidden = true + #endif + } + + return string + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+CALayer.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+CALayer.swift new file mode 100644 index 000000000..c4f6ad6da --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+CALayer.swift @@ -0,0 +1,83 @@ +#if os(macOS) +import AppKit +import Cocoa +@preconcurrency import QuartzCore + +extension SyncSnapshot where Input: CALayer, Output == ImageBytes { + /// A snapshot strategy for comparing layers based on pixel equality. + /// + /// ``` swift + /// // Match reference perfectly. + /// assert(of: layer, as: .image) + /// + /// // Allow for a 1% pixel difference. + /// assert(of: layer, as: .image(precision: 0.99)) + /// ``` + public static var image: SyncSnapshot { + .image(precision: 1) + } + + /// A snapshot strategy for comparing layers 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. + public static func image( + precision: Float, + perceptualPrecision: Float = 1 + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ).pullback { layer in + let image = NSImage(size: layer.bounds.size) + image.lockFocus() + let context = NSGraphicsContext.current!.cgContext + layer.setNeedsLayout() + layer.layoutIfNeeded() + layer.render(in: context) + image.unlockFocus() + return image + } + } +} +#elseif os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +extension SyncSnapshot where Input: CALayer, Output == ImageBytes { + /// A snapshot strategy for comparing layers based on pixel equality. + public static var image: SyncSnapshot { + .image() + } + + /// A snapshot strategy for comparing layers 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. + /// - traits: A trait collection override. + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + traits: UITraitCollection = .init() + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ).pullback { layer in + let renderer = UIGraphicsImageRenderer(bounds: layer.bounds, format: .init(for: traits)) + return renderer.image { ctx in + layer.setNeedsLayout() + layer.layoutIfNeeded() + layer.render(in: ctx.cgContext) + } + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+CGPath.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+CGPath.swift new file mode 100644 index 000000000..97ae8f9c2 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+CGPath.swift @@ -0,0 +1,181 @@ +#if os(macOS) +import AppKit +import Cocoa +@preconcurrency import CoreGraphics +#elseif os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#endif + +#if os(macOS) +extension SyncSnapshot where Input: CGPath, Output == ImageBytes { + /// A snapshot strategy for comparing bezier paths based on pixel equality. + public static var image: SyncSnapshot { + .image() + } + + /// A snapshot strategy for comparing bezier paths based on pixel equality. + /// + /// ``` swift + /// // Match reference perfectly. + /// assert(of: path, as: .image) + /// + /// // Allow for a 1% pixel difference. + /// assert(of: path, as: .image(precision: 0.99)) + /// ``` + /// + /// - 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. + /// - drawingMode: The mode used to render the path, defined by `CGPathDrawingMode`. Determines whether the path is stroked, filled, or both. + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + drawingMode: CGPathDrawingMode = .eoFill + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ).pullback { path in + let bounds = path.boundingBoxOfPath + var transform = CGAffineTransform( + translationX: -bounds.origin.x, + y: -bounds.origin.y + ) + + let path = path.copy(using: &transform)! + + let image = NSImage(size: bounds.size, flipped: false) { destRect in + guard let context = NSGraphicsContext.current else { + return false + } + + context.imageInterpolation = .high + context.cgContext.addPath(path) + context.cgContext.drawPath(using: drawingMode) + return true + } + + return image + } + } +} +#elseif os(iOS) || os(tvOS) || os(visionOS) +extension SyncSnapshot where Input: CGPath, Output == ImageBytes { + /// A snapshot strategy for comparing bezier paths based on pixel equality. + public static var image: SyncSnapshot { + .image() + } + + /// A snapshot strategy for comparing bezier paths 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. + /// - scale: The scale factor for the rendered image. + /// - drawingMode: The mode used to render the path, defined by `CGPathDrawingMode`. Determines whether the path is stroked, filled, or both. + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + scale: CGFloat = 1, + drawingMode: CGPathDrawingMode = .eoFill + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ).pullback { path in + let bounds = path.boundingBoxOfPath + let format: UIGraphicsImageRendererFormat + if #available(iOS 11.0, tvOS 11.0, *) { + format = UIGraphicsImageRendererFormat.preferred() + } else { + format = UIGraphicsImageRendererFormat.default() + } + format.scale = scale + let renderer = UIGraphicsImageRenderer(bounds: bounds, format: format) + return renderer.image { ctx in + let cgContext = ctx.cgContext + cgContext.addPath(path) + cgContext.drawPath(using: drawingMode) + } + } + } +} +#endif + +#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) +extension SyncSnapshot where Input: CGPath, Output == StringBytes { + /// A snapshot strategy for comparing bezier paths based on element descriptions. + public static var elementsDescription: SyncSnapshot { + .elementsDescription(numberFormatter: defaultNumberFormatter) + } + + /// A snapshot strategy for comparing bezier paths based on element descriptions. + /// + /// - Parameter numberFormatter: The number formatter used for formatting points. + public static func elementsDescription( + numberFormatter: NumberFormatter + ) -> SyncSnapshot< + Input, Output + > { + let namesByType: [CGPathElementType: String] = [ + .moveToPoint: "MoveTo", + .addLineToPoint: "LineTo", + .addQuadCurveToPoint: "QuadCurveTo", + .addCurveToPoint: "CurveTo", + .closeSubpath: "Close", + ] + + let numberOfPointsByType: [CGPathElementType: Int] = [ + .moveToPoint: 1, + .addLineToPoint: 1, + .addQuadCurveToPoint: 2, + .addCurveToPoint: 3, + .closeSubpath: 0, + ] + + return IdentitySyncSnapshot.lines.pullback { path in + var string: String = "" + + path.applyWithBlock { elementPointer in + let element = elementPointer.pointee + let name = namesByType[element.type] ?? "Unknown" + + if element.type == .moveToPoint && !string.isEmpty { + string += "\n" + } + + string += name + + if let numberOfPoints = numberOfPointsByType[element.type] { + let points = UnsafeBufferPointer(start: element.points, count: numberOfPoints) + string += + " " + + points.map { point in + let x = numberFormatter.string(from: point.x as NSNumber)! + let y = numberFormatter.string(from: point.y as NSNumber)! + return "(\(x), \(y))" + }.joined(separator: " ") + } + + string += "\n" + } + + return string + } + } +} + +private let defaultNumberFormatter: NumberFormatter = { + let numberFormatter = NumberFormatter() + numberFormatter.decimalSeparator = "." + numberFormatter.minimumFractionDigits = 1 + numberFormatter.maximumFractionDigits = 3 + return numberFormatter +}() +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+NSBezierPath.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+NSBezierPath.swift new file mode 100644 index 000000000..305d39938 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+NSBezierPath.swift @@ -0,0 +1,130 @@ +#if os(macOS) +@preconcurrency import AppKit +import Cocoa + +extension SyncSnapshot where Input: NSBezierPath, Output == ImageBytes { + /// A snapshot strategy for comparing bezier paths based on pixel equality. + public static var image: SyncSnapshot { + .image() + } + + /// A snapshot strategy for comparing bezier paths based on pixel equality. + /// + ///``` swift + /// // Match reference perfectly. + /// assert(of: path, as: .image) + /// + /// // Allow for a 1% pixel difference. + /// assert(of: path, as: .image(precision: 0.99)) + /// ``` + /// + /// - 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. + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1 + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ).map { + $0.pullback { path in + // Move path info frame: + let bounds = path.bounds + let transform = AffineTransform( + translationByX: -bounds.origin.x, + byY: -bounds.origin.y + ) + path.transform(using: transform) + + let image = NSImage(size: bounds.size, flipped: false) { destRect in + guard let context = NSGraphicsContext.current else { + return false + } + + context.imageInterpolation = .high + path.fill() + return true + } + + return image + } + } + } +} + +extension SyncSnapshot where Input: NSBezierPath, Output == StringBytes { + /// A snapshot strategy for comparing bezier paths based on pixel equality. + @available(macOS 11.0, *) + public static var elementsDescription: SyncSnapshot { + .elementsDescription(numberFormatter: defaultNumberFormatter) + } + + /// A snapshot strategy for comparing bezier paths based on pixel equality. + /// + /// - Parameter numberFormatter: The number formatter used for formatting points. + @available(macOS 11.0, *) + public static func elementsDescription( + numberFormatter: NumberFormatter + ) -> SyncSnapshot< + Input, Output + > { + let namesByType: [NSBezierPath.ElementType: String] = [ + .moveTo: "MoveTo", + .lineTo: "LineTo", + .curveTo: "CurveTo", + .closePath: "Close", + ] + + let numberOfPointsByType: [NSBezierPath.ElementType: Int] = [ + .moveTo: 1, + .lineTo: 1, + .curveTo: 3, + .closePath: 0, + ] + + return IdentitySyncSnapshot.lines.pullback { path in + var string: String = "" + + var elementPoints = [CGPoint](repeating: .zero, count: 3) + for elementIndex in 0.. { + .image() + } + + /// Creates a custom image snapshot configuration for `NSView`. + /// + /// This configuration allows you to capture `NSView` instances with custom settings for + /// layout, comparison precision, and other visual traits. It renders the view in isolation, + /// making it ideal for component testing. + /// + /// - Parameters: + /// - precision: Pixel tolerance for comparison (1 = perfect match, lower values allow more variation). + /// - perceptualPrecision: Tolerance for color and tonal differences (values closer to 1 require more exact matches). + /// - layout: Specifies how the view should be sized during rendering (e.g., specific device simulation). + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `NSApplication` instance to render the windows. + /// + /// - Example: + /// ```swift + /// let view = NSView() + /// view.backgroundColor = .red + /// view.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + /// + /// try await assert( + /// of: view, + /// as: .image( + /// layout: .device(.iPhone15Pro), + /// precision: 0.98 + /// ) + /// ) + /// ``` + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + layout: SnapshotLayout = .sizeThatFits, + delay: Double = .zero, + application: NSApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve(layout) + + return IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ) + .withWindow( + sessionRole: .windowApplication, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + .snapshot(executor) + } + ) + #if !os(macOS) + .inconsistentTraitsChecker(config.traits) + #endif + .withLock() + } +} + +extension AsyncSnapshot where Input: NSView, Output == StringBytes { + + /// A snapshot strategy for comparing views based on a recursive description of their properties + /// and hierarchies. + /// + /// This strategy captures a text-based representation of the view's hierarchy, including property values like frames, opacity, and layer information. + /// It's useful for verifying the structural integrity of complex views without relying on visual image comparisons. + /// + /// Example usage: + /// ```swift + /// let view = NSView() + /// view.backgroundColor = .red + /// view.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + /// + /// try await assert(of: view, as: .recursiveDescription) + /// ``` + /// + /// Recorded snapshot: + /// + /// ``` + /// > + /// ``` + public static var recursiveDescription: AsyncSnapshot { + .recursiveDescription() + } + + /// Creates a custom snapshot configuration for comparing views based on a recursive description of their properties and hierarchies. + /// + /// - Parameters: + /// - layout: Specifies how the view should be sized during rendering. + /// - delay: Delay before capturing the description (useful for waiting for animations or dynamic content). + /// - application: The `NSApplication` instance to render the windows. + /// + /// Example usage with custom layout: + /// ```swift + /// let view = NSView() + /// view.backgroundColor = .blue + /// view.frame = CGRect(x: 0, y: 0, width: 200, height: 300) + /// + /// try await assert( + /// of: view, + /// as: .recursiveDescription( + /// layout: .fixed(width: 200, height: 300) + /// ) + /// ) + /// ``` + public static func recursiveDescription( + layout: SnapshotLayout = .sizeThatFits, + delay: Double = .zero, + application: NSApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve(layout) + + return IdentitySyncSnapshot.lines + .withWindow( + sessionRole: .windowApplication, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + .descriptor(executor, method: .subtreeDescription) + } + ) + #if !os(macOS) + .inconsistentTraitsChecker(config.traits) + #endif + .withLock() + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+NSViewController.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+NSViewController.swift new file mode 100644 index 000000000..bd90e1743 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+NSViewController.swift @@ -0,0 +1,152 @@ +#if os(macOS) +import AppKit +import Cocoa + +extension AsyncSnapshot where Input: NSViewController & Sendable, Output == ImageBytes { + + /// Default configuration for capturing `NSViewController` as image snapshots. + /// + /// This configuration provides a basic setup for capturing view controllers with default settings. + /// It renders the view controller's view with default sizing and comparison precision. + /// + /// - Note: This configuration is suitable for simple view controllers where custom sizing or + /// comparison settings are not required. + public static var image: AsyncSnapshot { + .image() + } + + /// Creates a custom image snapshot configuration for `NSViewController`. + /// + /// This configuration allows you to capture `NSViewController` instances with custom settings for + /// layout, comparison precision, and other visual traits. It renders the view controller's view + /// in isolation, making it ideal for screen testing. + /// + /// - Parameters: + /// - precision: Pixel tolerance for comparison (1 = perfect match, lower values allow more variation). + /// - perceptualPrecision: Tolerance for color and tonal differences (values closer to 1 require more exact matches). + /// - layout: Specifies how the view should be sized during rendering (e.g., specific device simulation). + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `NSApplication` instance to render the windows. + /// + /// Example usage: + /// ```swift + /// let viewController = NSViewController() + /// viewController.view.backgroundColor = .red + /// viewController.view.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + /// + /// try await assert( + /// of: viewController, + /// as: .image( + /// layout: .device(.iPhone15Pro), + /// precision: 0.98 + /// ) + /// ) + /// ``` + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + layout: SnapshotLayout = .sizeThatFits, + delay: Double = .zero, + application: NSApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve(layout) + + return IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ) + .withWindow( + sessionRole: .windowApplication, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + .snapshot(executor) + } + ) + #if !os(macOS) + .inconsistentTraitsChecker(config.traits) + #endif + .withLock() + } +} + +extension Snapshot where Input: NSViewController, Output == StringBytes { + + /// A snapshot strategy for comparing view controller views based on a recursive description of their properties and hierarchies. + /// + /// This strategy captures a text-based representation of the view hierarchy, including property values like frames, opacity, and layer information. + /// It's useful for verifying the structural integrity of complex views within view controllers without relying on visual image comparisons. + /// + /// Example usage: + /// ```swift + /// let viewController = NSViewController() + /// viewController.view.backgroundColor = .red + /// viewController.view.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + /// + /// try await assert(of: viewController, as: .recursiveDescription) + /// ``` + /// + /// Recorded snapshot: + /// + /// ``` + /// > + /// ``` + public static var recursiveDescription: AsyncSnapshot { + .recursiveDescription() + } + + /// Creates a custom snapshot configuration for comparing view controller views based on a recursive description of their properties and hierarchies. + /// + /// - Parameters: + /// - layout: Specifies how the view should be sized during rendering. + /// - delay: Delay before capturing the recursive description (useful for waiting for animations or dynamic content). + /// - application: The `NSApplication` instance to render the windows. + /// + /// Example usage with custom layout: + /// ```swift + /// let viewController = NSViewController() + /// viewController.view.backgroundColor = .green + /// viewController.view.frame = CGRect(x: 0, y: 0, width: 300, height: 400) + /// + /// try await assert( + /// of: viewController, + /// as: .recursiveDescription( + /// layout: .fixed(width: 300, height: 400) + /// ) + /// ) + /// ``` + public static func recursiveDescription( + layout: SnapshotLayout = .sizeThatFits, + delay: Double = .zero, + application: NSApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve(layout) + + return IdentitySyncSnapshot.lines + .withWindow( + sessionRole: .windowApplication, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + .descriptor(executor, method: .subtreeDescription) + } + ) + #if !os(macOS) + .inconsistentTraitsChecker(config.traits) + #endif + .withLock() + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+SceneKit.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+SceneKit.swift new file mode 100644 index 000000000..184837da2 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+SceneKit.swift @@ -0,0 +1,109 @@ +#if os(iOS) || os(macOS) || os(tvOS) || os(visionOS) +import SceneKit +#if os(macOS) +import Cocoa +#elseif os(iOS) || os(tvOS) +import UIKit +#endif + +#if os(macOS) +extension AsyncSnapshot where Input: SCNScene & Sendable, Output == ImageBytes { + /// A snapshot strategy for comparing SceneKit scenes 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. + /// - size: The size of the scene. + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `NSApplication` instance to render the windows. + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + size: CGSize, + delay: Double = .zero, + application: NSApplication? = nil + ) -> AsyncSnapshot { + .scnScene( + sessionRole: .windowApplication, + precision: precision, + perceptualPrecision: perceptualPrecision, + size: size, + delay: delay, + application: application + ) + } +} +#elseif os(iOS) || os(tvOS) || os(visionOS) +extension Snapshot where Input: SCNScene & Sendable, Output == ImageBytes { + /// A snapshot strategy for comparing SceneKit scenes based on pixel equality. + /// + /// - Parameters: + /// - sessionRole: Defines the role of the UI session (default is `.windowApplication`). + /// - 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. + /// - size: The size of the scene. + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `UIApplication` instance to render the windows. + public static func image( + sessionRole: UISceneSession.Role = .windowApplication, + precision: Float = 1, + perceptualPrecision: Float = 1, + size: CGSize, + delay: Double = .zero, + application: UIKit.UIApplication? = nil + ) -> AsyncSnapshot { + .scnScene( + sessionRole: sessionRole, + precision: precision, + perceptualPrecision: perceptualPrecision, + size: size, + delay: delay, + application: application + ) + } +} +#endif + +extension Snapshot where Input: SCNScene & Sendable, Output == ImageBytes { + + fileprivate static func scnScene( + sessionRole: UISceneSession.Role, + precision: Float, + perceptualPrecision: Float, + size: CGSize, + delay: Double = .zero, + application: SDKApplication? + ) -> AsyncSnapshot { + #if os(macOS) + let snapshot = AsyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision, + layout: .fixed(width: size.width, height: size.height), + delay: delay, + application: application + ) + #else + let snapshot = AsyncSnapshot.image( + sessionRole: sessionRole, + precision: precision, + perceptualPrecision: perceptualPrecision, + layout: .fixed(width: size.width, height: size.height), + delay: delay, + application: application + ) + #endif + + return snapshot.pullback { @MainActor scene in + let view = SCNView(frame: .init(origin: .zero, size: size)) + view.scene = scene + return view + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+SpriteKit.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+SpriteKit.swift new file mode 100644 index 000000000..926674ec8 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+SpriteKit.swift @@ -0,0 +1,108 @@ +#if os(iOS) || os(macOS) || os(tvOS) || os(visionOS) +import SpriteKit +#if os(macOS) +import Cocoa +#elseif os(iOS) || os(tvOS) +import UIKit +#endif + +#if os(macOS) +extension AsyncSnapshot where Input: SKScene & Sendable, Output == ImageBytes { + /// A snapshot strategy for comparing SpriteKit scenes 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. + /// - size: The size of the scene. + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `NSApplication` instance to render the windows. + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + size: CGSize, + delay: Double = .zero, + application: NSApplication? = nil + ) -> AsyncSnapshot { + .skScene( + sessionRole: .windowApplication, + precision: precision, + perceptualPrecision: perceptualPrecision, + size: size, + delay: delay, + application: application + ) + } +} +#elseif os(iOS) || os(tvOS) || os(visionOS) +extension AsyncSnapshot where Input: SKScene, Output == ImageBytes { + /// A snapshot strategy for comparing SpriteKit scenes based on pixel equality. + /// + /// - Parameters: + /// - sessionRole: The role of the UI session (default is `.windowApplication`, appropriate for most UI testing scenarios). + /// - 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. + /// - size: The size of the scene. + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `UIApplication` instance to render the windows. + public static func image( + sessionRole: UISceneSession.Role = .windowApplication, + precision: Float = 1, + perceptualPrecision: Float = 1, + size: CGSize, + delay: Double = .zero, + application: UIKit.UIApplication? = nil + ) -> AsyncSnapshot { + .skScene( + sessionRole: sessionRole, + precision: precision, + perceptualPrecision: perceptualPrecision, + size: size, + delay: delay, + application: application + ) + } +} +#endif + +extension AsyncSnapshot where Input: SKScene, Output == ImageBytes { + + fileprivate static func skScene( + sessionRole: UISceneSession.Role, + precision: Float, + perceptualPrecision: Float, + size: CGSize, + delay: Double, + application: SDKApplication? + ) -> AsyncSnapshot { + #if os(macOS) + let snapshot = AsyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision, + layout: .fixed(width: size.width, height: size.height), + delay: delay, + application: application + ) + #else + let snapshot = AsyncSnapshot.image( + sessionRole: sessionRole, + precision: precision, + perceptualPrecision: perceptualPrecision, + layout: .fixed(width: size.width, height: size.height), + delay: delay, + application: application + ) + #endif + return snapshot.pullback { @MainActor scene in + let view = SKView(frame: .init(origin: .zero, size: size)) + view.presentScene(scene) + return view + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIApplication.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIApplication.swift new file mode 100644 index 000000000..e72e4baad --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIApplication.swift @@ -0,0 +1,94 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) +extension Snapshot where Input: UIKit.UIApplication, Output == ImageBytes { + + /// Default configuration for capturing `UIApplication` as image snapshots. + /// + /// This configuration is particularly useful when used with any UI testing framework to capture the current UI state after performing user interactions. + /// It allows you to drive UI interactions using the UI testing framework functions and take snapshots of the rendered UI at strategic moments. + /// + /// Notes: + /// - Uses default values for precision (`precision: 1`) and other parameters. + /// - Captures the current screen rendered in `UIWindow`. + /// - Useful for end-to-end testing where you need to verify the visual state after a series of actions. + /// + /// Example usage with XCUITest (renamed on Xcode 16.3 as XCUIAutomation): + /// ```swift + /// let app = XCUIApplication() + /// app.launch() + /// + /// // Perform UI interactions + /// app.buttons["Login"].tap() + /// + /// // Capture snapshot of the current UI state + /// try assert(of: UIApplication.shared, as: .image) + /// ``` + public static var image: AsyncSnapshot { + .image() + } + + /// Creates a custom image snapshot configuration for `UIApplication`. + /// + /// This configuration is designed for use with any UI testing framework, allowing you to capture the current UI state after performing user interactions. + /// It captures the contents of the application's `UIWindow`, making it ideal for verifying visual changes after specific user actions or workflow steps. + /// + /// - Parameters: + /// - precision: Pixel tolerance for comparison (1 = perfect match, 0.95 = 5% variation allowed). + /// - perceptualPrecision: Color/tonal tolerance for perceptual comparison (values closer to 1 require more exact color matching). + /// - delay: Delay before capturing the image (useful for waiting for animations or network responses). + /// - sessionRole: The role of the UI session (default is `.windowApplication`, appropriate for most UI testing scenarios). + /// + /// Example usage with XCUITest (renamed on Xcode 16.3 as XCUIAutomation): + /// ```swift + /// let app = XCUIApplication() + /// app.launch() + /// + /// // Perform UI interactions + /// app.buttons["Submit"].tap() + /// + /// // Capture snapshot with custom precision + /// try assert( + /// of: UIApplication.shared, + /// as: .image( + /// precision: 0.98, + /// delay: 2.0 + /// ) + /// ) + /// ``` + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + delay: Double = .zero, + sessionRole: UISceneSession.Role = .windowApplication + ) -> AsyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ) + .withApplication(sessionRole: sessionRole) { window, executor in + Async(Input.self) { _ in window } + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + .map { @MainActor window in + let renderer = UIGraphicsImageRenderer( + bounds: window.bounds, + format: .init(for: window.traitCollection) + ) + + let image = try await executor( + renderer.image { + window.layer.render(in: $0.cgContext) + } + ) + + return image + } + } + .withLock() + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIBezierPath.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIBezierPath.swift new file mode 100644 index 000000000..1f5e83670 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIBezierPath.swift @@ -0,0 +1,65 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +@preconcurrency import UIKit + +extension SyncSnapshot where Input: UIBezierPath, Output == ImageBytes { + /// A snapshot strategy for comparing bezier paths based on pixel equality. + public static var image: SyncSnapshot { + .image() + } + + /// A snapshot strategy for comparing bezier paths 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. + /// - scale: The scale factor for the rendered image. + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + scale: CGFloat = 1 + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ).pullback { path in + let bounds = path.bounds + let format: UIGraphicsImageRendererFormat + if #available(iOS 11.0, tvOS 11.0, *) { + format = UIGraphicsImageRendererFormat.preferred() + } else { + format = UIGraphicsImageRendererFormat.default() + } + format.scale = scale + let renderer = UIGraphicsImageRenderer(bounds: bounds, format: format) + return renderer.image { ctx in + path.fill() + } + } + } +} + +extension SyncSnapshot where Input: UIBezierPath, Output == StringBytes { + /// A snapshot strategy for comparing bezier paths based on pixel equality. + public static var elementsDescription: SyncSnapshot { + SyncSnapshot.elementsDescription.pullback { + $0.cgPath + } + } + + /// A snapshot strategy for comparing bezier paths based on pixel equality. + /// + /// - Parameter numberFormatter: The number formatter used for formatting points. + public static func elementsDescription( + numberFormatter: NumberFormatter + ) -> SyncSnapshot< + Input, Output + > { + SyncSnapshot.elementsDescription( + numberFormatter: numberFormatter + ).pullback { path in path.cgPath } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIView.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIView.swift new file mode 100644 index 000000000..31ea018af --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIView.swift @@ -0,0 +1,169 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) +extension AsyncSnapshot where Input: UIKit.UIView, Output == ImageBytes { + + /// Default configuration for capturing `UIView` as image snapshots. + /// + /// This configuration provides a basic setup for capturing views with default settings. + /// It uses the view's intrinsic content size and default comparison precision. + /// + /// - Note: This configuration is suitable for simple views where custom sizing or + /// comparison settings are not required. + public static var image: AsyncSnapshot { + .image() + } + + /// Creates a custom image snapshot configuration for `UIView`. + /// + /// This configuration allows you to capture `UIView` instances with custom settings for + /// layout, comparison precision, and other visual traits. It renders the view in isolation, + /// making it ideal for component testing. + /// + /// - Parameters: + /// - sessionRole: Defines the role of the UI session (default is `.windowApplication`). + /// - precision: Pixel tolerance for comparison (1 = perfect match, lower values allow more variation). + /// - perceptualPrecision: Tolerance for color and tonal differences (values closer to 1 require more exact matches). + /// - layout: Specifies how the view should be sized during rendering (e.g., specific device simulation). + /// - traits: Collection of UI traits (e.g., accessibility features, display characteristics). + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `UIApplication` instance to render the windows. + /// + /// - Example: + /// ```swift + /// let view = UIView() + /// view.backgroundColor = .red + /// view.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + /// + /// try await assert( + /// of: view, + /// as: .image( + /// layout: .device(.iPhone15Pro), + /// precision: 0.98 + /// ) + /// ) + /// ``` + public static func image( + sessionRole: UISceneSession.Role = .windowApplication, + precision: Float = 1, + perceptualPrecision: Float = 1, + layout: SnapshotLayout = .sizeThatFits, + traits: Traits = .init(), + delay: Double = .zero, + application: UIKit.UIApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve( + layout, + with: SnapshotEnvironment.current.traits.merging(traits) + ) + + return IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ) + .withWindow( + sessionRole: sessionRole, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + #if !os(tvOS) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + #endif + .snapshot(executor) + } + ) + .inconsistentTraitsChecker(config.traits) + .withLock() + } +} + +extension AsyncSnapshot where Input: UIKit.UIView, Output == StringBytes { + + /// A snapshot strategy for comparing views based on a recursive description of their properties + /// and hierarchies. + /// + /// This strategy captures a text-based representation of the view's hierarchy, including property values like frames, opacity, and layer information. + /// It's useful for verifying the structural integrity of complex views without relying on visual image comparisons. + /// + /// Example usage: + /// ```swift + /// let view = UIView() + /// view.backgroundColor = .red + /// view.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + /// + /// try await assert(of: view, as: .recursiveDescription) + /// ``` + /// + /// Recorded snapshot: + /// + /// ``` + /// > + /// ``` + public static var recursiveDescription: AsyncSnapshot { + Snapshot.recursiveDescription() + } + + /// Creates a custom snapshot configuration for comparing views based on a recursive description of their properties and hierarchies. + /// + /// - Parameters: + /// - sessionRole: Defines the role of the UI session (default is `.windowApplication`). + /// - layout: Specifies how the view should be sized during rendering. + /// - traits: Collection of UI traits (e.g., accessibility features, display characteristics). + /// - delay: Delay before capturing the description (useful for waiting for animations or dynamic content). + /// - application: The `UIApplication` instance to render the windows. + /// + /// Example usage with custom layout: + /// ```swift + /// let view = UIView() + /// view.backgroundColor = .blue + /// view.frame = CGRect(x: 0, y: 0, width: 200, height: 300) + /// + /// try await assert( + /// of: view, + /// as: .recursiveDescription( + /// layout: .fixed(width: 200, height: 300) + /// ) + /// ) + /// ``` + public static func recursiveDescription( + sessionRole: UISceneSession.Role = .windowApplication, + layout: SnapshotLayout = .sizeThatFits, + traits: Traits = .init(), + delay: Double = .zero, + application: UIKit.UIApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve( + layout, + with: SnapshotEnvironment.current.traits.merging(traits) + ) + + return IdentitySyncSnapshot.lines + .withWindow( + sessionRole: sessionRole, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + #if !os(tvOS) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + #endif + .descriptor(executor, method: .recursiveDescription) + } + ) + .withLock() + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIViewController.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIViewController.swift new file mode 100644 index 000000000..dd2df9ae3 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+UIViewController.swift @@ -0,0 +1,251 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) +extension AsyncSnapshot where Input: UIKit.UIViewController, Output == ImageBytes { + + /// Default configuration for capturing `UIViewController` as image snapshots. + /// + /// This configuration provides a basic setup for capturing view controllers with default settings. + /// It renders the view controller's view with default sizing and comparison precision. + /// + /// - Note: This configuration is suitable for simple view controllers where custom sizing or + /// comparison settings are not required. + public static var image: AsyncSnapshot { + .image() + } + + /// Creates a custom image snapshot configuration for `UIViewController`. + /// + /// This configuration allows you to capture `UIViewController` instances with custom settings for + /// layout, comparison precision, and other visual traits. It renders the view controller's view + /// in isolation, making it ideal for screen testing. + /// + /// - Parameters: + /// - sessionRole: Defines the role of the UI session (default is `.windowApplication`). + /// - precision: Pixel tolerance for comparison (1 = perfect match, lower values allow more variation). + /// - perceptualPrecision: Tolerance for color and tonal differences (values closer to 1 require more exact matches). + /// - layout: Specifies how the view should be sized during rendering (e.g., specific device simulation). + /// - traits: Collection of UI traits (e.g., accessibility features, display characteristics). + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `UIApplication` instance to render the windows. + /// + /// Example usage: + /// ```swift + /// let viewController = UIViewController() + /// viewController.view.backgroundColor = .red + /// viewController.view.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + /// + /// try await assert( + /// of: viewController, + /// as: .image( + /// layout: .device(.iPhone15Pro), + /// precision: 0.98 + /// ) + /// ) + /// ``` + public static func image( + sessionRole: UISceneSession.Role = .windowApplication, + precision: Float = 1, + perceptualPrecision: Float = 1, + layout: SnapshotLayout = .sizeThatFits, + traits: Traits = .init(), + delay: Double = .zero, + application: UIKit.UIApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve( + layout, + with: SnapshotEnvironment.current.traits.merging(traits) + ) + + return IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ) + .withWindow( + sessionRole: sessionRole, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + #if !os(tvOS) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + #endif + .snapshot(executor) + } + ) + .inconsistentTraitsChecker(config.traits) + .withLock() + } +} + +extension AsyncSnapshot where Input: UIKit.UIViewController, Output == StringBytes { + + /// A snapshot strategy for comparing view controllers based on their embedded controller hierarchy. + /// + /// This strategy captures a text-based representation of the view controller hierarchy, including states like "appeared" or "disappeared." + /// It's useful for verifying the structural integrity of complex view controller hierarchies without relying on visual image comparisons. + /// + /// Example usage: + /// ```swift + /// let tabBarController = UITabBarController() + /// tabBarController.viewControllers = [UIViewController(), UIViewController()] + /// + /// try await assert(of: tabBarController, as: .hierarchy) + /// ``` + /// + /// Recorded snapshot: + /// + /// ``` + /// , state: appeared, view: + /// | , state: appeared, view: + /// | | , state: appeared, view: + /// | , state: disappeared, view: not in the window + /// | | , state: disappeared, view: (view not loaded) + /// ``` + public static var hierarchy: AsyncSnapshot { + Snapshot.hierarchy() + } + + /// A snapshot strategy for comparing view controller views based on a recursive description of their properties and hierarchies. + /// + /// This strategy captures a text-based representation of the view hierarchy, including property values like frames, opacity, and layer information. + /// It's useful for verifying the structural integrity of complex views within view controllers without relying on visual image comparisons. + /// + /// Example usage: + /// ```swift + /// let viewController = UIViewController() + /// viewController.view.backgroundColor = .red + /// viewController.view.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + /// + /// try await assert(of: viewController, as: .recursiveDescription) + /// ``` + /// + /// Recorded snapshot: + /// + /// ``` + /// > + /// ``` + public static var recursiveDescription: AsyncSnapshot { + Snapshot.recursiveDescription() + } + + /// Creates a custom snapshot configuration for comparing view controllers based on their embedded controller hierarchy. + /// + /// - Parameters: + /// - sessionRole: Defines the role of the UI session (default is `.windowApplication`). + /// - layout: Specifies how the view should be sized during rendering. + /// - traits: Collection of UI traits (e.g., accessibility features, display characteristics). + /// - delay: Delay before capturing the hierarchy description (useful for waiting for animations or dynamic content). + /// - application: The `UIApplication` instance to render the windows. + /// + /// Example usage with custom layout: + /// ```swift + /// let viewController = UIViewController() + /// viewController.view.backgroundColor = .blue + /// viewController.view.frame = CGRect(x: 0, y: 0, width: 200, height: 300) + /// + /// try await assert( + /// of: viewController, + /// as: .hierarchy( + /// layout: .fixed(width: 200, height: 300) + /// ) + /// ) + /// ``` + public static func hierarchy( + sessionRole: UISceneSession.Role = .windowApplication, + layout: SnapshotLayout = .sizeThatFits, + traits: Traits = .init(), + delay: Double = .zero, + application: UIKit.UIApplication? = nil + ) -> AsyncSnapshot { + descriptor( + method: .hierarchy, + sessionRole: sessionRole, + layout: layout, + traits: traits, + delay: delay, + application: application + ) + } + + /// Creates a custom snapshot configuration for comparing view controller views based on a recursive description of their properties and hierarchies. + /// + /// - Parameters: + /// - sessionRole: Defines the role of the UI session (default is `.windowApplication`). + /// - layout: Specifies how the view should be sized during rendering. + /// - traits: Collection of UI traits (e.g., accessibility features, display characteristics). + /// - delay: Delay before capturing the recursive description (useful for waiting for animations or dynamic content). + /// - application: The `UIApplication` instance to render the windows. + /// + /// Example usage with custom layout: + /// ```swift + /// let viewController = UIViewController() + /// viewController.view.backgroundColor = .green + /// viewController.view.frame = CGRect(x: 0, y: 0, width: 300, height: 400) + /// + /// try await assert( + /// of: viewController, + /// as: .recursiveDescription( + /// layout: .fixed(width: 300, height: 400) + /// ) + /// ) + /// ``` + public static func recursiveDescription( + sessionRole: UISceneSession.Role = .windowApplication, + layout: SnapshotLayout = .sizeThatFits, + traits: Traits = .init(), + delay: Double = .zero, + application: UIKit.UIApplication? = nil + ) -> AsyncSnapshot { + descriptor( + method: .recursiveDescription, + sessionRole: sessionRole, + layout: layout, + traits: traits, + delay: delay, + application: application + ) + } + + private static func descriptor( + method: SnapshotUIController.DescriptorMethod, + sessionRole: UISceneSession.Role, + layout: SnapshotLayout, + traits: Traits, + delay: Double, + application: UIKit.UIApplication? + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve( + layout, + with: SnapshotEnvironment.current.traits.merging(traits) + ) + + return IdentitySyncSnapshot.lines + .withWindow( + sessionRole: sessionRole, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + #if !os(tvOS) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + #endif + .descriptor(executor, method: method) + } + ) + .withLock() + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Snapshot+View.swift b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+View.swift new file mode 100644 index 000000000..5542e06ad --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Snapshot+View.swift @@ -0,0 +1,230 @@ +#if canImport(SwiftUI) +@preconcurrency import SwiftUI + +#if os(macOS) +extension AsyncSnapshot where Input: SwiftUI.View & Sendable, Output == ImageBytes { + + /// Default configuration for capturing SwiftUI `View` as image snapshots. + /// + /// This configuration provides a basic setup for capturing SwiftUI views with default settings. + /// It renders the view in its initial state with default sizing and comparison precision. + /// + /// - Note: This configuration is suitable for simple views where custom sizing or + /// comparison settings are not required. + public static var image: AsyncSnapshot { + .image() + } + + /// Creates a custom image snapshot configuration for SwiftUI `View`. + /// + /// This configuration allows you to capture SwiftUI `View` instances with custom settings for + /// layout, comparison precision, and other visual traits. It renders the view in isolation, + /// making it ideal for component testing. + /// + /// - Parameters: + /// - precision: Pixel tolerance for comparison (1 = perfect match, lower values allow more variation). + /// - perceptualPrecision: Tolerance for color and tonal differences (values closer to 1 require more exact matches). + /// - layout: Specifies how the view should be sized during rendering (e.g., specific device simulation). + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `NSApplication` instance to render the windows. + /// + /// Example usage: + /// ```swift + /// struct MyView: View { + /// var body: some View { + /// Text("Hello, World!") + /// .padding() + /// .background(Color.blue) + /// } + /// } + /// + /// try assert( + /// of: MyView(), + /// as: .image( + /// layout: .device(.iPhone15Pro), + /// precision: 0.98 + /// ) + /// ) + /// ``` + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1, + layout: SnapshotLayout = .sizeThatFits, + delay: Double = .zero, + application: NSApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve(layout) + + return IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ) + .withWindow( + sessionRole: .windowApplication, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + .snapshot(executor) + } + ) + } +} +#elseif os(iOS) || os(tvOS) || os(visionOS) +extension Snapshot where Input: SwiftUI.View & Sendable, Output == ImageBytes { + + /// Default configuration for capturing SwiftUI `View` as image snapshots. + /// + /// This configuration provides a basic setup for capturing SwiftUI views with default settings. + /// It renders the view in its initial state with default sizing and comparison precision. + /// + /// - Note: This configuration is suitable for simple views where custom sizing or + /// comparison settings are not required. + public static var image: AsyncSnapshot { + .image() + } + + /// Creates a custom image snapshot configuration for SwiftUI `View`. + /// + /// This configuration allows you to capture SwiftUI `View` instances with custom settings for + /// layout, comparison precision, and other visual traits. It renders the view in isolation, + /// making it ideal for component testing. + /// + /// - Parameters: + /// - sessionRole: Defines the role of the UI session (default is `.windowApplication`). + /// - precision: Pixel tolerance for comparison (1 = perfect match, lower values allow more variation). + /// - perceptualPrecision: Tolerance for color and tonal differences (values closer to 1 require more exact matches). + /// - layout: Specifies how the view should be sized during rendering (e.g., specific device simulation). + /// - traits: Collection of UI traits (e.g., accessibility features, display characteristics). + /// - delay: Delay before capturing the image (useful for waiting for animations or dynamic content). + /// - application: The `UIApplication` instance to render the windows. + /// + /// Example usage: + /// ```swift + /// struct MyView: View { + /// var body: some View { + /// Text("Hello, World!") + /// .padding() + /// .background(Color.blue) + /// } + /// } + /// + /// try assert( + /// of: MyView(), + /// as: .image( + /// layout: .device(.iPhone15Pro), + /// precision: 0.98 + /// ) + /// ) + /// ``` + public static func image( + sessionRole: UISceneSession.Role = .windowApplication, + precision: Float = 1, + perceptualPrecision: Float = 1, + layout: SnapshotLayout = .sizeThatFits, + traits: Traits = .init(), + delay: Double = .zero, + application: UIKit.UIApplication? = nil + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve( + layout, + with: SnapshotEnvironment.current.traits.merging(traits) + ) + + return IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ) + .withWindow( + sessionRole: sessionRole, + application: application, + operation: { windowConfiguration, executor in + Async(Input.self) { @MainActor in + SnapshotUIController($0, with: config) + } + .connectToWindow(windowConfiguration) + .layoutIfNeeded() + .sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + #if !os(tvOS) + .waitLoadingStateIfNeeded(tolerance: SnapshotEnvironment.current.webViewTolerance) + #endif + .snapshot(executor) + } + ) + } +} +#elseif os(watchOS) +@available(watchOS, introduced: 9.0) +extension Snapshot where Input: SwiftUI.View & Sendable, Output == ImageBytes { + + /// Default configuration for capturing SwiftUI `View` as image snapshots on watchOS. + /// + /// This configuration provides a basic setup for capturing SwiftUI views with default settings. + /// It renders the view in its initial state with default sizing and comparison precision. + /// + /// - Note: This configuration is suitable for simple watchOS views where custom sizing or + /// comparison settings are not required. + public static var image: AsyncSnapshot { + .image() + } + + /// Creates a custom image snapshot configuration for SwiftUI `View` on watchOS. + /// + /// This configuration allows you to capture SwiftUI `View` instances with custom settings for + /// layout and comparison precision. It renders the view in isolation, making it ideal for component testing. + /// + /// - Parameters: + /// - precision: Pixel tolerance for comparison (1 = perfect match, lower values allow more variation). + /// - scale: The scale factor for the rendered image. + /// - layout: Specifies how the view should be sized during rendering. + /// + /// Example usage: + /// ```swift + /// struct MyWatchView: View { + /// var body: some View { + /// Text("Hello, watch!") + /// .padding() + /// .background(Color.green) + /// } + /// } + /// + /// try assert( + /// of: MyWatchView(), + /// as: .image( + /// precision: 0.98, + /// scale: 2.0 + /// ) + /// ) + /// ``` + public static func image( + precision: Float = 1, + scale: CGFloat = 1, + layout: SnapshotLayout = .sizeThatFits + ) -> AsyncSnapshot { + let config = LayoutConfiguration.resolve(layout) + + return IdentitySyncSnapshot.image( + precision: precision + ).map { executor in + Async(Input.self) { @MainActor view in + let renderer = ImageRenderer(content: view) + renderer.scale = scale + + if let size = config.size { + renderer.proposedSize = .init(size) + } + + return try await executor( + renderer.uiImage ?? UIImage() + ) + } + } + } +} +#endif +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/InterfaceSizeClassTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/InterfaceSizeClassTraitKey.swift new file mode 100644 index 000000000..54738402f --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/InterfaceSizeClassTraitKey.swift @@ -0,0 +1,321 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +/// A size class that describes the horizontal or vertical space available for a user interface. +/// +/// `InterfaceSizeClass` helps determine how UI elements should layout based on the available space. +/// Commonly used to differentiate between compact and regular layouts for different device sizes and orientations. +public enum InterfaceSizeClass: Sendable, Hashable { + /// The size class is not specified. + case unspecified + /// The size class is regular (typically for larger screens or orientations). + case regular + /// The size class is compact (typically for smaller screens or orientations). + case compact +} + +/// A structure representing the horizontal and vertical interface size classes for a device. +/// +/// `DeviceInterfaceSizeClass` combines horizontal and vertical size classes to fully describe the layout environment of a device. +/// This helps in creating adaptive UIs that respond appropriately to different screen sizes and orientations. +public struct DeviceInterfaceSizeClass: Sendable, Hashable { + + /// The horizontal interface size class. + public let horizontal: InterfaceSizeClass + + /// The vertical interface size class. + public let vertical: InterfaceSizeClass + + /// Initializes a `DeviceInterfaceSizeClass` with the specified horizontal and vertical size classes. + /// + /// - Parameters: + /// - horizontal: The horizontal interface size class. + /// - vertical: The vertical interface size class. + public init(horizontal: InterfaceSizeClass, vertical: InterfaceSizeClass) { + self.horizontal = horizontal + self.vertical = vertical + } +} + +/// A struct that dynamically determines the device interface size class based on a given size. +/// +/// `DeviceDynamicInterfaceSizeClass` allows you to define how interface size classes should be determined +/// for different device sizes. This is useful for adaptive UI layouts that need to respond to different +/// screen sizes and orientations. +/// +/// You can create a dynamic size class provider using either a closure or a constant value. +/// +/// Example usage with a closure: +/// ```swift +/// let dynamicSizeClass = DeviceDynamicInterfaceSizeClass { size in +/// if size.width < 768 { +/// return .compact +/// } else { +/// return .regular +/// } +/// } +/// let sizeClass = dynamicSizeClass(CGSize(width: 500, height: 300)) // Returns .compact +/// ``` +/// +/// Example usage with a constant: +/// ```swift +/// let constantSizeClass = DeviceDynamicInterfaceSizeClass(constant: .regular) +/// let sizeClass = constantSizeClass(CGSize(width: 1000, height: 800)) // Returns .regular +/// ``` +public struct DeviceDynamicInterfaceSizeClass: Sendable { + + private let provider: @Sendable (CGSize) -> DeviceInterfaceSizeClass + + /// Creates a `DeviceDynamicInterfaceSizeClass` instance using a provider closure. + /// + /// - Parameter provider: A closure that takes a size and returns the corresponding interface size class. + public init(provider: @escaping @Sendable (CGSize) -> DeviceInterfaceSizeClass) { + self.provider = provider + } + + /// Creates a `DeviceDynamicInterfaceSizeClass` instance with a constant value. + /// + /// - Parameter constant: The interface size class to always return. + public init(constant: DeviceInterfaceSizeClass) { + self.init(provider: { _ in constant }) + } + + /// Evaluates the interface size class for the given size. + /// + /// - Parameter size: The size to evaluate. + /// - Returns: The interface size class corresponding to the provided size. + public func callAsFunction(_ size: CGSize) -> DeviceInterfaceSizeClass { + provider(size) + } +} + +// MARK: - iPhone +extension DeviceDynamicInterfaceSizeClass { + + // MARK: - iPhone 16 + + public static let iPhone16ProMax = withRegularLandscape + + public static let iPhone16Pro = withCompactLandscape + + public static let iPhone16Plus = withRegularLandscape + + public static let iPhone16 = withCompactLandscape + + public static let iPhone16e = withCompactLandscape + + // MARK: - iPhone 15 + + public static let iPhone15ProMax = withRegularLandscape + + public static let iPhone15Pro = withCompactLandscape + + public static let iPhone15Plus = withRegularLandscape + + public static let iPhone15 = withCompactLandscape + + // MARK: - iPhone 14 + + public static let iPhone14ProMax = withRegularLandscape + + public static let iPhone14Pro = withCompactLandscape + + public static let iPhone14Plus = withRegularLandscape + + public static let iPhone14 = withCompactLandscape + + // MARK: - iPhone 13 + + public static let iPhone13ProMax = withRegularLandscape + + public static let iPhone13Pro = withCompactLandscape + + public static let iPhone13 = withCompactLandscape + + public static let iPhone13Mini = withCompactLandscape + + // MARK: - iPhone 12 + + public static let iPhone12ProMax = withRegularLandscape + + public static let iPhone12Pro = withCompactLandscape + + public static let iPhone12 = withCompactLandscape + + public static let iPhone12Mini = withCompactLandscape + + // MARK: - iPhone 11 + + public static let iPhone11ProMax = withRegularLandscape + + public static let iPhone11Pro = withCompactLandscape + + public static let iPhone11 = withRegularLandscape + + // MARK: - iPhone XS + + public static let iPhoneXSMax = withRegularLandscape + + public static let iPhoneXS = withCompactLandscape + + // MARK: - iPhone XR + + public static let iPhoneXR = withRegularLandscape + + // MARK: - iPhone X + + public static let iPhoneX = withCompactLandscape + + // MARK: - iPhone 8 + + public static let iPhone8Plus = withRegularLandscape + + public static let iPhone8 = withCompactLandscape + + // MARK: - iPhone SE + + public static let iPhoneSE = withCompactLandscape + + // MARK: - iPhone Private Methods + + private static let withRegularLandscape = iOS( + landscape: .init(horizontal: .regular, vertical: .compact) + ) + + private static let withCompactLandscape = iOS( + landscape: .init(horizontal: .compact, vertical: .compact) + ) + + private static func iOS( + landscape: @autoclosure @escaping @Sendable () -> DeviceInterfaceSizeClass + ) -> DeviceDynamicInterfaceSizeClass { + DeviceDynamicInterfaceSizeClass { + if $0.width > $0.height { + return landscape() + } + + return .init( + horizontal: .compact, + vertical: .regular + ) + } + } +} + +// MARK: - iPads +extension DeviceDynamicInterfaceSizeClass { + + public static let iPadOS = DeviceDynamicInterfaceSizeClass { size in + let horizontal: InterfaceSizeClass + let vertical: InterfaceSizeClass + + if size.width >= size.height * 0.75 { + horizontal = .regular + } else { + horizontal = .compact + } + + if size.height > size.width * 0.5 { + vertical = .regular + } else { + vertical = .compact + } + + return .init(horizontal: horizontal, vertical: vertical) + } +} + +// MARK: - Regular Sizes +extension DeviceDynamicInterfaceSizeClass { + + public static let macOS = DeviceDynamicInterfaceSizeClass( + constant: DeviceInterfaceSizeClass(horizontal: .regular, vertical: .regular) + ) + + public static let tvOS = DeviceDynamicInterfaceSizeClass( + constant: DeviceInterfaceSizeClass(horizontal: .regular, vertical: .regular) + ) + + public static let visionOS = DeviceDynamicInterfaceSizeClass( + constant: DeviceInterfaceSizeClass(horizontal: .regular, vertical: .regular) + ) +} + +// MARK: - WatchOS +extension DeviceDynamicInterfaceSizeClass { + + public static let watchOS = DeviceDynamicInterfaceSizeClass( + constant: DeviceInterfaceSizeClass(horizontal: .compact, vertical: .compact) + ) +} + +#if os(iOS) || os(tvOS) || os(visionOS) +private struct DeviceInterfaceSizeClassTraitKey: TraitKey { + + static let defaultValue = DeviceInterfaceSizeClass( + horizontal: .unspecified, + vertical: .unspecified + ) + + @available(iOS 17, tvOS 17, *) + static func apply(_ value: Value, to traitsOverrides: inout UITraitOverrides) { + traitsOverrides.verticalSizeClass = .init(value.vertical) + traitsOverrides.horizontalSizeClass = .init(value.horizontal) + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.verticalSizeClass = .init(value.vertical) + $0.horizontalSizeClass = .init(value.horizontal) + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.verticalSizeClass = .init(value.vertical) + $0.horizontalSizeClass = .init(value.horizontal) + } + } else { + traitCollection = .init(traitsFrom: [ + .init(verticalSizeClass: .init(value.vertical)), + .init(horizontalSizeClass: .init(value.horizontal)), + ]) + } + #endif + } +} + +extension UIUserInterfaceSizeClass { + + fileprivate init(_ interfaceSizeClass: InterfaceSizeClass) { + switch interfaceSizeClass { + case .unspecified: + self = .unspecified + case .regular: + self = .regular + case .compact: + self = .compact + } + } +} + +extension Traits { + + /// Specifies the size class for the device interface. + public var deviceInterfaceSizeClass: DeviceInterfaceSizeClass { + get { self[DeviceInterfaceSizeClassTraitKey.self] } + set { self[DeviceInterfaceSizeClassTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified device interface size class. + public init(deviceInterfaceSizeClass: DeviceInterfaceSizeClass) { + self.init() + self.deviceInterfaceSizeClass = deviceInterfaceSizeClass + } +} +#endif +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/AccessibilityContrastTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/AccessibilityContrastTraitKey.swift new file mode 100644 index 000000000..ce5d7f4be --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/AccessibilityContrastTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct AccessibilityContrastTraitKey: TraitKey { + + static let defaultValue = UIAccessibilityContrast.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.accessibilityContrast = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.accessibilityContrast = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.accessibilityContrast = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(accessibilityContrast: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the accessibility contrast setting. + public var accessibilityContrast: UIAccessibilityContrast { + get { self[AccessibilityContrastTraitKey.self] } + set { self[AccessibilityContrastTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified accessibility contrast. + public init(accessibilityContrast: UIAccessibilityContrast) { + self.init() + self.accessibilityContrast = accessibilityContrast + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ContentSizeCategoryTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ContentSizeCategoryTraitKey.swift new file mode 100644 index 000000000..700c29093 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ContentSizeCategoryTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct ContentSizeCategoryTraitKey: TraitKey { + + static let defaultValue = UIContentSizeCategory.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.preferredContentSizeCategory = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.preferredContentSizeCategory = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.preferredContentSizeCategory = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(preferredContentSizeCategory: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the preferred content size category for accessibility. + public var preferredContentSizeCategory: UIContentSizeCategory { + get { self[ContentSizeCategoryTraitKey.self] } + set { self[ContentSizeCategoryTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified preferred content size category. + public init(preferredContentSizeCategory: UIContentSizeCategory) { + self.init() + self.preferredContentSizeCategory = preferredContentSizeCategory + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/DisplayGamutTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/DisplayGamutTraitKey.swift new file mode 100644 index 000000000..e4c7b8a4b --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/DisplayGamutTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct DisplayGamutTraitKey: TraitKey { + + static let defaultValue = UIDisplayGamut.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.displayGamut = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.displayGamut = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.displayGamut = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(displayGamut: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the display gamut (color range) of the screen. + public var displayGamut: UIDisplayGamut { + get { self[DisplayGamutTraitKey.self] } + set { self[DisplayGamutTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified display gamut. + public init(displayGamut: UIDisplayGamut) { + self.init() + self.displayGamut = displayGamut + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/DisplayScaleTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/DisplayScaleTraitKey.swift new file mode 100644 index 000000000..d3de14150 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/DisplayScaleTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct DisplayScaleTraitKey: TraitKey { + + static let defaultValue: CGFloat = 1 + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.displayScale = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.displayScale = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.displayScale = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(displayScale: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the display scale (screen resolution) of the device. + public var displayScale: CGFloat { + get { self[DisplayScaleTraitKey.self] } + set { self[DisplayScaleTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified display scale. + public init(displayScale: CGFloat) { + self.init() + self.displayScale = displayScale + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ForceTouchCapabilityTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ForceTouchCapabilityTraitKey.swift new file mode 100644 index 000000000..25e5735a2 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ForceTouchCapabilityTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct ForceTouchCapabilityTraitKey: TraitKey { + + static let defaultValue = UIForceTouchCapability.unknown + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.forceTouchCapability = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.forceTouchCapability = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.forceTouchCapability = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(forceTouchCapability: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the force touch capability of the device. + public var forceTouchCapability: UIForceTouchCapability { + get { self[ForceTouchCapabilityTraitKey.self] } + set { self[ForceTouchCapabilityTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified force touch capability. + public init(forceTouchCapability: UIForceTouchCapability) { + self.init() + self.forceTouchCapability = forceTouchCapability + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ImageDynamicRangeTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ImageDynamicRangeTraitKey.swift new file mode 100644 index 000000000..274e09a6e --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ImageDynamicRangeTraitKey.swift @@ -0,0 +1,38 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +@available(iOS 17, tvOS 17, *) +private struct ImageDynamicRangeTraitKey: TraitKey { + + static let defaultValue = UIImage.DynamicRange.unspecified + + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.imageDynamicRange = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + traitCollection = traitCollection.modifyingTraits { + $0.imageDynamicRange = value + } + } +} + +@available(iOS 17, tvOS 17, *) +extension Traits { + + /// Specifies the dynamic range of images displayed. + public var imageDynamicRange: UIImage.DynamicRange { + get { self[ImageDynamicRangeTraitKey.self] } + set { self[ImageDynamicRangeTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified image dynamic range. + public init(imageDynamicRange: UIImage.DynamicRange) { + self.init() + self.imageDynamicRange = imageDynamicRange + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/LayoutDirectionTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/LayoutDirectionTraitKey.swift new file mode 100644 index 000000000..2222681c0 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/LayoutDirectionTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct LayoutDirectionTraitKey: TraitKey { + + static let defaultValue = UITraitEnvironmentLayoutDirection.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.layoutDirection = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.layoutDirection = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.layoutDirection = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(layoutDirection: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the layout direction for the UI. + public var layoutDirection: UITraitEnvironmentLayoutDirection { + get { self[LayoutDirectionTraitKey.self] } + set { self[LayoutDirectionTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified layout direction. + public init(layoutDirection: UITraitEnvironmentLayoutDirection) { + self.init() + self.layoutDirection = layoutDirection + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/LegibilityWeightTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/LegibilityWeightTraitKey.swift new file mode 100644 index 000000000..4f73ce5e9 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/LegibilityWeightTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct LegibilityWeightTraitKey: TraitKey { + + static let defaultValue = UILegibilityWeight.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.legibilityWeight = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.legibilityWeight = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.legibilityWeight = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(legibilityWeight: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the legibility weight for text. + public var legibilityWeight: UILegibilityWeight { + get { self[LegibilityWeightTraitKey.self] } + set { self[LegibilityWeightTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified legibility weight. + public init(legibilityWeight: UILegibilityWeight) { + self.init() + self.legibilityWeight = legibilityWeight + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ListEnvironmentTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ListEnvironmentTraitKey.swift new file mode 100644 index 000000000..3cf87c00c --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ListEnvironmentTraitKey.swift @@ -0,0 +1,38 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +@available(iOS 18, tvOS 18, *) +private struct ListEnvironmentTraitKey: TraitKey { + + static let defaultValue = UIListEnvironment.unspecified + + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.listEnvironment = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + traitCollection = traitCollection.modifyingTraits { + $0.listEnvironment = value + } + } +} + +@available(iOS 18, tvOS 18, *) +extension Traits { + + /// Specifies the list environment characteristics. + public var listEnvironment: UIListEnvironment { + get { self[ListEnvironmentTraitKey.self] } + set { self[ListEnvironmentTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified list environment. + public init(listEnvironment: UIListEnvironment) { + self.init() + self.listEnvironment = listEnvironment + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/SceneCaptureStateTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/SceneCaptureStateTraitKey.swift new file mode 100644 index 000000000..313699c0e --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/SceneCaptureStateTraitKey.swift @@ -0,0 +1,38 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +@available(iOS 17, tvOS 17, *) +private struct SceneCaptureStateTraitKey: TraitKey { + + static let defaultValue = UISceneCaptureState.unspecified + + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.sceneCaptureState = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + traitCollection = traitCollection.modifyingTraits { + $0.sceneCaptureState = value + } + } +} + +@available(iOS 17, tvOS 17, *) +extension Traits { + + /// Specifies the scene capture state. + public var sceneCaptureState: UISceneCaptureState { + get { self[SceneCaptureStateTraitKey.self] } + set { self[SceneCaptureStateTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified scene capture state. + public init(sceneCaptureState: UISceneCaptureState) { + self.init() + self.sceneCaptureState = sceneCaptureState + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ToolbarItemPresentationSizeTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ToolbarItemPresentationSizeTraitKey.swift new file mode 100644 index 000000000..3deebb43c --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/ToolbarItemPresentationSizeTraitKey.swift @@ -0,0 +1,52 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +@available(macCatalyst 16, *) +private struct ToolbarItemPresentationSizeTraitKey: TraitKey { + + static let defaultValue = UINSToolbarItemPresentationSize.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.toolbarItemPresentationSize = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.toolbarItemPresentationSize = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.toolbarItemPresentationSize = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(toolbarItemPresentationSize: value), + ]) + } + #endif + } +} + +@available(macCatalyst 16, *) +extension Traits { + + /// Specifies the presentation size for toolbar items. + public var toolbarItemPresentationSize: UINSToolbarItemPresentationSize { + get { self[ToolbarItemPresentationSizeTraitKey.self] } + set { self[ToolbarItemPresentationSizeTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified toolbar item presentation size. + public init(toolbarItemPresentationSize: UINSToolbarItemPresentationSize) { + self.init() + self.toolbarItemPresentationSize = toolbarItemPresentationSize + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/TypesettingLanguageTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/TypesettingLanguageTraitKey.swift new file mode 100644 index 000000000..9d2f720d8 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/TypesettingLanguageTraitKey.swift @@ -0,0 +1,38 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +@available(iOS 17, tvOS 17, *) +private struct TypesettingLanguageTraitKey: TraitKey { + + static let defaultValue: Locale.Language? = nil + + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.typesettingLanguage = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + traitCollection = traitCollection.modifyingTraits { + $0.typesettingLanguage = value + } + } +} + +@available(iOS 17, tvOS 17, *) +extension Traits { + + /// Specifies the language used for typesetting. + public var typesettingLanguage: Locale.Language? { + get { self[TypesettingLanguageTraitKey.self] } + set { self[TypesettingLanguageTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified typesetting language. + public init(typesettingLanguage: Locale.Language?) { + self.init() + self.typesettingLanguage = typesettingLanguage + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceActiveAppearanceTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceActiveAppearanceTraitKey.swift new file mode 100644 index 000000000..a111b4139 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceActiveAppearanceTraitKey.swift @@ -0,0 +1,52 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +@available(iOS 14, tvOS 14, *) +private struct UserInterfaceActiveAppearanceTraitKey: TraitKey { + + static let defaultValue = UIUserInterfaceActiveAppearance.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.activeAppearance = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.activeAppearance = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.activeAppearance = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(activeAppearance: value), + ]) + } + #endif + } +} + +@available(iOS 14, tvOS 14, *) +extension Traits { + + /// Specifies the active appearance of the interface. + public var activeAppearance: UIUserInterfaceActiveAppearance { + get { self[UserInterfaceActiveAppearanceTraitKey.self] } + set { self[UserInterfaceActiveAppearanceTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified active appearance. + public init(activeAppearance: UIUserInterfaceActiveAppearance) { + self.init() + self.activeAppearance = activeAppearance + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceIdiomTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceIdiomTraitKey.swift new file mode 100644 index 000000000..7fd78ff6c --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceIdiomTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct UserInterfaceIdiomTraitKey: TraitKey { + + static let defaultValue = UIUserInterfaceIdiom.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.userInterfaceIdiom = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.userInterfaceIdiom = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.userInterfaceIdiom = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(userInterfaceIdiom: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the user interface idiom (e.g., phone, pad, mac). + public var userInterfaceIdiom: UIUserInterfaceIdiom { + get { self[UserInterfaceIdiomTraitKey.self] } + set { self[UserInterfaceIdiomTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified user interface idiom. + public init(userInterfaceIdiom: UIUserInterfaceIdiom) { + self.init() + self.userInterfaceIdiom = userInterfaceIdiom + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceLevelTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceLevelTraitKey.swift new file mode 100644 index 000000000..531e07e25 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceLevelTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(visionOS) +import UIKit + +private struct UserInterfaceLevelTraitKey: TraitKey { + + static let defaultValue = UIUserInterfaceLevel.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.userInterfaceLevel = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.userInterfaceLevel = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.userInterfaceLevel = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(userInterfaceLevel: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the user interface level (e.g., normal, elevated). + public var userInterfaceLevel: UIUserInterfaceLevel { + get { self[UserInterfaceLevelTraitKey.self] } + set { self[UserInterfaceLevelTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified user interface level. + public init(userInterfaceLevel: UIUserInterfaceLevel) { + self.init() + self.userInterfaceLevel = userInterfaceLevel + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceStyleTraitKey.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceStyleTraitKey.swift new file mode 100644 index 000000000..0663b94ff --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Trait Keys/UserInterfaceStyleTraitKey.swift @@ -0,0 +1,50 @@ +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +private struct UserInterfaceStyleTraitKey: TraitKey { + + static let defaultValue = UIUserInterfaceStyle.unspecified + + @available(iOS 17, tvOS 17, *) + static func apply( + _ value: Value, + to traitsOverrides: inout UITraitOverrides + ) { + traitsOverrides.userInterfaceStyle = value + } + + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) { + #if os(visionOS) + traitCollection = traitCollection.modifyingTraits { + $0.userInterfaceStyle = value + } + #else + if #available(iOS 17, tvOS 17, *) { + traitCollection = traitCollection.modifyingTraits { + $0.userInterfaceStyle = value + } + } else { + traitCollection = .init(traitsFrom: [ + traitCollection, + UITraitCollection(userInterfaceStyle: value), + ]) + } + #endif + } +} + +extension Traits { + + /// Specifies the user interface style (light, dark). + public var userInterfaceStyle: UIUserInterfaceStyle { + get { self[UserInterfaceStyleTraitKey.self] } + set { self[UserInterfaceStyleTraitKey.self] = newValue } + } + + /// Creates a `Traits` instance with the specified user interface style. + public init(userInterfaceStyle: UIUserInterfaceStyle) { + self.init() + self.userInterfaceStyle = userInterfaceStyle + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/Traits/Traits.swift b/Sources/XCSnapshotTesting/Methods/UI/Traits/Traits.swift new file mode 100644 index 000000000..8bfa27ad0 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/Traits/Traits.swift @@ -0,0 +1,294 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) +protocol TraitKey: Sendable, Hashable { + + associatedtype Value: Hashable & Sendable + + static var defaultValue: Value { get } + + @available(iOS 17, tvOS 17, *) + @MainActor + static func apply(_ value: Value, to traitsOverrides: inout UITraitOverrides) + + @MainActor + static func apply(_ value: Value, to traitCollection: inout UITraitCollection) +} + +/// A set of characteristics that describe the environment in which UI elements are displayed during snapshot testing. +/// +/// `Traits` allows you to customize the appearance and behavior of UI elements by specifying various display and accessibility characteristics. +/// These traits help ensure that your UI renders correctly across different devices and configurations during testing. +/// +/// ```swift +/// let traits = Traits(preferredContentSizeCategory: .extraLarge) +/// SnapshotEnvironment.current.traits = traits +/// ``` +public struct Traits: Sendable, Hashable { + + private var traits = [ObjectIdentifier: Storage]() + + /// Creates a default `Traits` instance with no specific characteristics set. + public init() {} + + /// Creates a `Traits` instance from a collection of trait dictionaries. + public init(traitsFrom traitCollection: [Traits]) { + self.init( + traitCollection.lazy.map(\.traits).reduce([:]) { + $0.merging($1, uniquingKeysWith: { $1 }) + } + ) + } + + private init(_ traits: [ObjectIdentifier: Storage]) { + self.traits = traits + } + + subscript(_ key: Key.Type) -> Key.Value { + get { + let id = ObjectIdentifier(key) + + guard let storage = traits[id] else { + return key.defaultValue + } + + return storage.value as! Key.Value + } + set { + let id = ObjectIdentifier(key) + traits[id, default: .init(key)].value = newValue + } + } + + /// Combines this traits instance with another, returning a new instance that merges the properties of both. + public func merging(_ traits: Traits) -> Traits { + .init(traitsFrom: [self, traits]) + } + + /// Returns a `UITraitCollection` representation of these traits. + public func callAsFunction() -> UITraitCollection { + performOnMainThread { + traits.reduce(into: UITraitCollection()) { + $1.value.apply(to: &$0) + } + } + } + + @MainActor + func commit(in viewController: UIViewController) { + #if !os(visionOS) + var pendingTraitCollection: UITraitCollection? + #endif + + for (_, trait) in traits { + #if os(visionOS) + trait.apply(to: &viewController.traitOverrides) + #else + if #available(iOS 17, tvOS 17, *) { + trait.apply(to: &viewController.traitOverrides) + } else { + var traitCollection = pendingTraitCollection ?? UITraitCollection() + trait.apply(to: &traitCollection) + pendingTraitCollection = traitCollection + } + #endif + } + + #if !os(visionOS) + guard let pendingTraitCollection else { + return + } + + for childViewController in viewController.children { + viewController.setOverrideTraitCollection( + pendingTraitCollection, + forChild: childViewController + ) + } + #endif + } +} + +private struct Storage: Sendable, Hashable { + + var value: any Hashable & Sendable + + private let mutating: @MainActor (Self, inout Any) -> Void + private let asserting: @Sendable (Self, Self) -> Bool + private let hashing: @Sendable (Self, inout Hasher) -> Void + + init(_ key: Key.Type) { + value = Key.defaultValue + mutating = { + #if os(visionOS) + if var traitsOverrides = $1 as? UITraitOverrides { + key.apply($0.value as! Key.Value, to: &traitsOverrides) + $1 = traitsOverrides + } else { + var traitCollection = $1 as! UITraitCollection + key.apply($0.value as! Key.Value, to: &traitCollection) + $1 = traitCollection + } + #else + if #available(iOS 17, tvOS 17, *), var traitsOverrides = $1 as? UITraitOverrides { + key.apply($0.value as! Key.Value, to: &traitsOverrides) + $1 = traitsOverrides + } else { + var traitCollection = $1 as! UITraitCollection + key.apply($0.value as! Key.Value, to: &traitCollection) + $1 = traitCollection + } + #endif + } + asserting = { + if let lhs = $0.value as? Key.Value, let rhs = $1.value as? Key.Value { + return lhs == rhs + } else { + return false + } + } + hashing = { + let value = $0.value as! Key.Value + value.hash(into: &$1) + } + } + + static func == (_ lhs: Self, _ rhs: Self) -> Bool { + lhs.asserting(lhs, rhs) + } + + func hash(into hasher: inout Hasher) { + hashing(self, &hasher) + } + + @available(iOS 17, tvOS 17, *) + @MainActor + func apply(to traitsOverrides: inout UITraitOverrides) { + var reference = traitsOverrides as Any + mutating(self, &reference) + traitsOverrides = reference as! UITraitOverrides + } + + @MainActor + func apply(to traitCollection: inout UITraitCollection) { + var reference = traitCollection as Any + mutating(self, &reference) + traitCollection = reference as! UITraitCollection + } +} + +extension Traits { + + func inconsistentTraitsChecker(_ object: Object, to otherTraits: Traits) { + if self != otherTraits { + guard !SnapshotEnvironment.current.disableInconsistentTraitsChecker else { + print("[DISABLED] ⚠️ Inconsistent Traits Detected - Snapshot Integrity Risk") + return + } + + print( + """ + ⚠️ Inconsistent Traits Detected - Snapshot Integrity Risk + + The same instance of \(type(of: object)) is being reused with a different \(Traits.self) \ + configuration. + + This may cause: + - Unreliable snapshot comparisons + - State contamination between test runs + - Non-deterministic test failures + + 🛠️ Recommended approach for reliable snapshot testing: + + try await assert( + of: await \(type(of: object))(), // Always use a clean instance + as: ... + ) + + ℹ️ Tip: Create fresh input instances for each test scenario to ensure consistent results. + """ + ) + } + } +} + +extension Traits { + + static func iOS( + displayScale: CGFloat, + size: CGSize, + deviceInterfaceSizeClass: DeviceDynamicInterfaceSizeClass + ) -> Traits { + iOS( + userInterfaceIdiom: .phone, + displayScale: displayScale, + size: size, + deviceInterfaceSizeClass: deviceInterfaceSizeClass + ) + } + + static func iPadOS( + displayScale: CGFloat, + size: CGSize, + deviceInterfaceSizeClass: DeviceDynamicInterfaceSizeClass + ) -> Traits { + iOS( + userInterfaceIdiom: .pad, + displayScale: displayScale, + size: size, + deviceInterfaceSizeClass: deviceInterfaceSizeClass + ) + } + + static func tvOS( + displayScale: CGFloat, + size: CGSize, + deviceInterfaceSizeClass: DeviceDynamicInterfaceSizeClass + ) -> Traits { + iOS( + userInterfaceIdiom: .tv, + displayScale: displayScale, + size: size, + deviceInterfaceSizeClass: deviceInterfaceSizeClass + ) + } + + @available(iOS 17, tvOS 17, *) + static func visionOS( + displayScale: CGFloat, + size: CGSize, + deviceInterfaceSizeClass: DeviceDynamicInterfaceSizeClass + ) -> Traits { + iOS( + userInterfaceIdiom: .vision, + displayScale: displayScale, + size: size, + deviceInterfaceSizeClass: deviceInterfaceSizeClass + ) + } + + private static func iOS( + userInterfaceIdiom: UIUserInterfaceIdiom = .phone, + displayScale: CGFloat, + size: CGSize, + deviceInterfaceSizeClass: DeviceDynamicInterfaceSizeClass + ) -> Traits { + var traits = Traits( + deviceInterfaceSizeClass: deviceInterfaceSizeClass(size) + ) + + #if os(tvOS) + traits.userInterfaceIdiom = .tv + #else + traits.userInterfaceIdiom = userInterfaceIdiom + #endif + traits.displayScale = displayScale + + return traits + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/WindowPool/NoWindowAvailableError.swift b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/NoWindowAvailableError.swift new file mode 100644 index 000000000..374bc040a --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/NoWindowAvailableError.swift @@ -0,0 +1,53 @@ +#if os(macOS) +@preconcurrency import AppKit +#elseif os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) || os(macOS) +struct NoWindowAvailableError: Error, CustomDebugStringConvertible { + #if !os(macOS) + let sessionRole: UISceneSession.Role + #endif + + var debugDescription: String { + #if !os(macOS) + """ + Failed to find a valid window for role: \(sessionRole). + + This typically occurs in two scenarios: + + - When running tests in a Package.swift environment, SnapshotTesting cannot \ + instantiate a UIWindowScene because UIApplication may be nil. Verify that the \ + UISceneSession.Role matches the platform's expected behavior (e.g., \ + .windowApplication for iOS apps). + + - When using an xcodeproj, ensure you activate the scene session before testing via: \ + `UIApplication.shared.activateSceneSession(for:)` + """ + #else + """ + [BETA OUTPUT] + + Failed to find a valid NSWindow instance for snapshot testing. + + Common causes: + + - NSApplication may not be properly initialized. Ensure sharedApplication is set up \ + before testing begins. + + - The window might not be ordered or key. Verify your window creation flow and call \ + `makeKeyAndOrderFront(_:)` explicitly if needed. + """ + #endif + } +} +#endif + +#if os(macOS) +enum UISceneSession { + enum Role { + case windowApplication + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/WindowPool/Snapshot+SDKApplication.swift b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/Snapshot+SDKApplication.swift new file mode 100644 index 000000000..5e8923c9e --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/Snapshot+SDKApplication.swift @@ -0,0 +1,38 @@ +#if os(macOS) +@preconcurrency import AppKit +#elseif os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) || os(macOS) +extension Snapshot { + + func withApplication( + sessionRole: UISceneSession.Role, + operation: @escaping @Sendable (SDKWindow, Executor) async throws -> Async + ) -> Snapshot> { + map { executor in + Async { @MainActor newInput in + #if os(macOS) + guard let window = newInput.keyWindow else { + throw NoWindowAvailableError() + } + #else + let windowScenes = newInput.windowScenes(for: sessionRole) + let window = windowScenes.lazy + .map(\.windows) + .reduce([], +) + .first(where: \.isKeyWindow) + + guard let window else { + throw NoWindowAvailableError(sessionRole: sessionRole) + } + #endif + + let executor = try await operation(window, executor) + return try await executor(newInput) + } + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/WindowPool/Snapshot+WindowPool.swift b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/Snapshot+WindowPool.swift new file mode 100644 index 000000000..667520d18 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/Snapshot+WindowPool.swift @@ -0,0 +1,43 @@ +#if os(macOS) +@preconcurrency import AppKit +#elseif os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) || os(macOS) +extension Snapshot { + func withWindow( + sessionRole: UISceneSession.Role, + application: SDKApplication?, + operation: @escaping @Sendable (SnapshotWindowConfiguration, Executor) async throws + -> Async + ) -> AsyncSnapshot { + map { executor in + Async(NewInput.self) { @MainActor newInput in + let windowPool = application?.windowPool ?? WindowPool.shared + + let window = try await windowPool.acquire( + sessionRole: sessionRole, + maxConcurrentTests: SnapshotEnvironment.current.maxConcurrentTests + ) + + let configuration = SnapshotWindowConfiguration( + window: window, + input: newInput + ) + + do { + let executor = try await operation(configuration, executor) + let output = try await executor(newInput) + + await windowPool.release(window) + return output + } catch { + await windowPool.release(window) + throw error + } + } + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/WindowPool/SnapshotWindowConfiguration.swift b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/SnapshotWindowConfiguration.swift new file mode 100644 index 000000000..dbf679568 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/SnapshotWindowConfiguration.swift @@ -0,0 +1,7 @@ +#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) +@MainActor +struct SnapshotWindowConfiguration { + let window: SDKWindow + let input: Input +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/WindowPool/WindowLease.swift b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/WindowLease.swift new file mode 100644 index 000000000..3d18d695b --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/WindowLease.swift @@ -0,0 +1,54 @@ +#if os(macOS) +@preconcurrency import AppKit +#elseif os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) || os(macOS) +@MainActor +class WindowLease { + + private let _lock = AsyncLock() + private(set) var pendingTasks: Int = .zero + + let window: SDKWindow + + init(window: SDKWindow) { + self.window = window + window.windowLease = self + } + + func lock() async throws { + pendingTasks += 1 + try await _lock.lock() + pendingTasks -= 1 + } + + func unlock() async { + await _lock.unlock() + } +} + +@MainActor +private var kWindowLeaseKey = 0 + +@MainActor +extension SDKWindow { + + fileprivate(set) var windowLease: WindowLease? { + get { + objc_getAssociatedObject(self, &kWindowLeaseKey) as? WindowLease + } + set { + precondition(windowLease == nil) + + objc_setAssociatedObject( + self, + &kWindowLeaseKey, + newValue, + .OBJC_ASSOCIATION_ASSIGN + ) + } + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Methods/UI/WindowPool/WindowPool.swift b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/WindowPool.swift new file mode 100644 index 000000000..bf185d188 --- /dev/null +++ b/Sources/XCSnapshotTesting/Methods/UI/WindowPool/WindowPool.swift @@ -0,0 +1,186 @@ +import Foundation + +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) +@MainActor +final class WindowPool { + + // MARK: - Internal static properties + + static let shared = WindowPool(SDKApplication.sharedIfAvailable) + + // MARK: - Private properties + + private var leases: [WindowLease] = [] + private weak var application: SDKApplication? + + #if !os(macOS) + private var activeWindowScenes: [UIWindowScene] { + leases.compactMap(\.window.windowScene) + } + #endif + + // MARK: - Inits + + init(_ application: SDKApplication?) { + self.application = application + } + + // MARK: - Internal methods + + func acquire( + sessionRole: UISceneSession.Role, + maxConcurrentTests: Int + ) async throws -> SDKWindow { + #if !os(macOS) + let windowScene = try windowScene(for: sessionRole) + #endif + + if leases.count >= maxConcurrentTests { + let leases = leases[0..= $1.pendingTasks + }).first! + try await lease.lock() + #if !os(macOS) + lease.window.windowScene = windowScene + #endif + display(lease.window, visible: true) + return lease.window + } + + #if os(macOS) + let window = SDKWindow() + window.isReleasedWhenClosed = false + #else + let window = SDKWindow(windowScene: windowScene) + #endif + + display(window, visible: true) + + let WindowLease = WindowLease(window: window) + leases.append(WindowLease) + try await WindowLease.lock() + return window + } + + func release(_ window: SDKWindow) async { + defer { display(window, visible: false) } + + await window.windowLease?.unlock() + } + + // MARK: - Private methods + + #if !os(macOS) + private func windowScene(for sessionRole: UISceneSession.Role) throws -> UIWindowScene { + if let windowScene = application?.windowScenes(for: sessionRole).first { + return windowScene + } + + if let windowScene = activeWindowScenes.first(where: { $0.session.role == sessionRole }) { + return windowScene + } + + if #available(iOS 17, tvOS 17, *) { + if let windowScene = activateNewSceneSession(sessionRole) { + return windowScene + } + } + + if let windowScene = windowSceneThroughTemporaryWindow(sessionRole) { + return windowScene + } + + throw NoWindowAvailableError(sessionRole: sessionRole) + } + + @available(iOS 17, tvOS 17, *) + private func activateNewSceneSession(_ sessionRole: UISceneSession.Role) -> UIWindowScene? { + guard let application = application else { + return nil + } + + application.activateSceneSession( + for: UISceneSessionActivationRequest(role: sessionRole) + ) + + return application.windowScenes(for: sessionRole).first + } + + private func windowSceneThroughTemporaryWindow( + _ sessionRole: UISceneSession.Role + ) -> UIWindowScene? { + let window = SDKWindow() + window.isHidden = false + + defer { + window.isHidden = true + window.windowScene = nil + } + + return window.windowScene + } + #endif + + private func display(_ window: SDKWindow, visible: Bool) { + #if os(macOS) + if !visible { + window.close() + } else { + window.setIsVisible(visible) + } + #else + window.isHidden = !visible + #endif + + guard visible else { + return + } + + #if os(macOS) + guard let screen = window.screen else { + return + } + + let screenSize = screen.frame.size + if window.frame.size != screenSize { + window.setFrame( + NSRect(origin: .zero, size: screenSize), + display: true + ) + } + #elseif os(iOS) || os(tvOS) + let screenSize = window.screen.bounds.size + + if window.bounds.size != screenSize { + window.frame = .init(origin: .zero, size: screenSize) + } + #endif + } +} + +// MARK: - SnapshotWindowConfiguration + +@MainActor +private var kApplicationWindowPool = 0 + +@MainActor +extension SDKApplication { + + var windowPool: WindowPool { + if let windowPool = objc_getAssociatedObject(self, &kApplicationWindowPool) as? WindowPool { + return windowPool + } + + let windowPool = WindowPool(self) + objc_setAssociatedObject(self, &kApplicationWindowPool, windowPool, .OBJC_ASSOCIATION_RETAIN) + return windowPool + } +} +#endif diff --git a/Sources/XCSnapshotTesting/Serialization/BytesContainer.swift b/Sources/XCSnapshotTesting/Serialization/BytesContainer.swift new file mode 100644 index 000000000..6b8644c58 --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/BytesContainer.swift @@ -0,0 +1,120 @@ +import Foundation + +/// A Sendable container for storing and manipulating data during serialization/deserialization. +/// +/// `BytesContainer` provides an interface for reading and writing data using a specific +/// serialization configuration. +public struct BytesContainer: Sendable { + + private enum OperationMode { + case read + case write + } + + private struct OperationNotAllowed: Error { + fileprivate init() {} + } + + fileprivate class State: @unchecked Sendable { + var data: Data { + get { lock.withLock { _data } } + set { lock.withLock { _data = newValue } } + } + + private let lock = NSLock() + private var _data: Data + + init(_ data: Data) { + self._data = data + } + } + + // MARK: - Public properties + + /// The data serialization configuration used by this container. + /// + /// Defines how data will be serialized/deserialized, including options like image scaling + /// or formatting. + public let configuration: DataSerializationConfiguration + + // MARK: - Internal properties + + var data: Data { + state.data + } + + // MARK: - Private properties + + private let operationMode: OperationMode + private let state: State + + // MARK: - Init + + private init( + _ operationMode: OperationMode, + data: Data, + with configuration: DataSerializationConfiguration + ) { + self.operationMode = operationMode + self.state = .init(data) + self.configuration = configuration + } + + // MARK: - Static internal methods + + /// Creates a read-only container initialized with existing data. + /// + /// - Parameter data: The binary data to store in the container. + /// - Parameter configuration: The serialization configuration to use. + /// - Returns: A read-only `BytesContainer` instance. + static func readOnly( + _ data: Data, + with configuration: DataSerializationConfiguration + ) -> BytesContainer { + BytesContainer( + .read, + data: data, + with: configuration + ) + } + + /// Creates a write-only container for storing new data. + /// + /// - Parameter configuration: The serialization configuration to use. + /// - Returns: A write-only `BytesContainer` instance. + static func writeOnly( + with configuration: DataSerializationConfiguration + ) -> BytesContainer { + BytesContainer( + .write, + data: Data(), + with: configuration + ) + } + + // MARK: - Public methods + + /// Retrieves data stored in the container. + /// + /// - Returns: The stored `Data` object. + /// - Throws: An `OperationNotAllowed` error if the container is not in read mode. + public func read() throws -> Data { + guard case .read = operationMode else { + throw OperationNotAllowed() + } + + return state.data + } + + /// Writes data to the container. + /// + /// - Parameter data: The data to store. + /// - Throws: An `OperationNotAllowed` error if the container is not in write mode. + public func write(_ data: Data) throws { + guard case .write = operationMode else { + throw OperationNotAllowed() + } + + state.data = data + } +} diff --git a/Sources/XCSnapshotTesting/Serialization/BytesRepresentable.swift b/Sources/XCSnapshotTesting/Serialization/BytesRepresentable.swift new file mode 100644 index 000000000..e2854bb06 --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/BytesRepresentable.swift @@ -0,0 +1,38 @@ +import Foundation + +/// Protocol for types that can be serialized/deserialized to/from a byte container. +/// +/// Types conforming to `BytesRepresentable` must be able to: +/// 1. Create an instance from a `BytesContainer` (deserialization). +/// 2. Write their data to a `BytesContainer` (serialization). +/// +/// This protocol is essential for snapshot testing, enabling objects to be converted into binary data +/// for comparison. +public protocol BytesRepresentable: Sendable { + + associatedtype RawValue + + /// The underlying value used for serialization and deserialization. + /// This can be any type, such as `String`, `Data`, or `UIImage`. + var rawValue: RawValue { get } + + /// Initializes an instance from a raw value. + /// + /// This initializer is used to create a type from its underlying raw value, which is + /// typically the serialized form of the data (e.g., `String`, `Data`, or `UIImage`). + /// + /// - Parameter rawValue: The raw value representing the instance's data. + init(rawValue: RawValue) + + /// Initializes an instance from data stored in the container. + /// + /// - Parameter container: The byte container containing data to read. + /// - Throws: An error if deserialization fails. + init(from container: BytesContainer) throws + + /// Serializes the instance's data and writes it to the provided container. + /// + /// - Parameter container: The byte container where data will be stored. + /// - Throws: An error if serialization fails. + func serialize(to container: BytesContainer) throws +} diff --git a/Sources/XCSnapshotTesting/Serialization/BytesSerializationError.swift b/Sources/XCSnapshotTesting/Serialization/BytesSerializationError.swift new file mode 100644 index 000000000..9ea0af180 --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/BytesSerializationError.swift @@ -0,0 +1,14 @@ +import Foundation + +/// Represents an error that occurred during binary data serialization or deserialization. +/// +/// This structure indicates failures in operations involving conversion of data to or from binary formats, +/// such as reading/writing in a `BytesContainer`. +public struct BytesSerializationError: Error { + + /// Initializes an instance of `BytesSerializationError`. + /// + /// This initializer creates a basic error instance, typically used to indicate generic failures during + /// serialization/deserialization processes. + public init() {} +} diff --git a/Sources/XCSnapshotTesting/Serialization/DataSerialization.swift b/Sources/XCSnapshotTesting/Serialization/DataSerialization.swift new file mode 100644 index 000000000..12d14cf60 --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/DataSerialization.swift @@ -0,0 +1,54 @@ +import Foundation + +/// Manager for data serialization and deserialization based on a specific configuration. +/// +/// The `DataSerialization` handles conversion between objects conforming to the +/// `BytesRepresentable` protocol and `Data` (serialization) and vice versa (deserialization), +/// using the configuration defined in `DataSerializationConfiguration`. +public struct DataSerialization: Sendable { + + // MARK: - Public properties + + /// Configuration for serialization/deserialization operations. + /// + /// Defines parameters such as image scaling, formatting, or other customized options, + /// accessible via `DataSerializationConfiguration`. + public var configuration: DataSerializationConfiguration + + // MARK: - Inits + + /// Initializes a `DataSerialization` instance with default configuration. + /// + /// The default configuration includes values like `imageScale` defined in + /// `DataSerializationConfiguration`. + public init() { + configuration = .init() + } + + // MARK: - Public methods + + /// Deserializes binary data into an instance of the specified type. + /// + /// - Parameter bytesType: Type of object to create (must conform to `BytesRepresentable`). + /// - Parameter data: Binary data to convert. + /// - Returns: An instance of the `Bytes` type created from the data. + /// - Throws: Errors if deserialization fails. + public func deserialize( + _ bytesType: Bytes.Type, + from data: Data + ) throws -> Bytes { + let container = BytesContainer.readOnly(data, with: configuration) + return try Bytes(from: container) + } + + /// Serializes an object into binary data. + /// + /// - Parameter bytes: Object to serialize (must conform to `BytesRepresentable`). + /// - Returns: Binary data resulting from serialization. + /// - Throws: Errors if serialization fails. + public func serialize(_ bytes: Bytes) throws -> Data { + let container = BytesContainer.writeOnly(with: configuration) + try bytes.serialize(to: container) + return container.data + } +} diff --git a/Sources/XCSnapshotTesting/Serialization/DataSerializationConfiguration.swift b/Sources/XCSnapshotTesting/Serialization/DataSerializationConfiguration.swift new file mode 100644 index 000000000..8fff2be7d --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/DataSerializationConfiguration.swift @@ -0,0 +1,47 @@ +import Foundation + +/// Configuration for parameters used during data serialization/deserialization. +/// +/// The `DataSerializationConfiguration` stores specific configuration values, accessible via keys +/// conforming to the `DataSerializationConfigurationKey` protocol. +/// These configurations control behaviors such as image scaling or formatting during data conversion. +/// +/// Example extension: +/// +/// extension DataSerializationConfiguration { +/// var imageScale: Double { +/// get { self[ImageBytes.ImageScaleKey.self] } +/// set { self[ImageBytes.ImageScaleKey.self] = newValue } +/// } +/// } +/// +/// Example access: +/// +/// func serialize(to container: BytesContainer) throws { +/// let imageScale = container.configuration.imageScale +/// ... +/// } +public struct DataSerializationConfiguration: Sendable { + + // MARK: - Private properties + + private var values: [ObjectIdentifier: Sendable] + + // MARK: - Inits + + init() { + self.values = [:] + } + + // MARK: - Public methods + + /// Accesses or sets a configuration value associated with a specific key. + /// + /// - Parameter keyType: The key type identifying the configuration (must conform to + /// `DataSerializationConfigurationKey`). + /// - Returns: The stored value for the provided key. If no value is set, returns the key's default value. + public subscript(_ keyType: Key.Type) -> Key.Value { + get { values[ObjectIdentifier(keyType)] as? Key.Value ?? Key.defaultValue } + set { values[ObjectIdentifier(keyType)] = newValue } + } +} diff --git a/Sources/XCSnapshotTesting/Serialization/DataSerializationConfigurationKey.swift b/Sources/XCSnapshotTesting/Serialization/DataSerializationConfigurationKey.swift new file mode 100644 index 000000000..bf2ab13a6 --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/DataSerializationConfigurationKey.swift @@ -0,0 +1,18 @@ +import Foundation + +/// Protocol defining keys for serialization/deserialization configurations with default values. +/// +/// Types conforming to `DataSerializationConfigurationKey` represent unique keys for specific configurations like +/// image scaling or formatting. Each key must define: +/// 1. An **associated type** (`Value`) representing the configuration value's type. +/// 2. A **static default value** for the key. +public protocol DataSerializationConfigurationKey: Sendable { + + /// Associated type representing the value type for this configuration key. + associatedtype Value: Sendable + + /// Default value for this key when not explicitly configured. + /// + /// Example: For an image scaling configuration key, the default might be `1.0`. + static var defaultValue: Value { get } +} diff --git a/Sources/XCSnapshotTesting/Serialization/Defaults/DataBytes.swift b/Sources/XCSnapshotTesting/Serialization/Defaults/DataBytes.swift new file mode 100644 index 000000000..f08e67b89 --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/Defaults/DataBytes.swift @@ -0,0 +1,33 @@ +import Foundation + +/// A wrapper type for `Data` that conforms to `BytesRepresentable`, enabling serialization and deserialization of byte-based data. +/// +/// This type provides a convenient way to work with raw byte data through a `Data` value, supporting reading from and writing to `BytesContainer` instances. It is designed for scenarios where direct byte manipulation or inspection is required. +/// +/// - SeeAlso: +/// - ``BytesRepresentable`` +/// - ``BytesContainer`` +public struct DataBytes: BytesRepresentable { + + public let rawValue: Data + + public init(from container: BytesContainer) throws { + self.rawValue = try container.read() + } + + public init(rawValue: Data) { + self.rawValue = rawValue + } + + public func serialize(to container: BytesContainer) throws { + try container.write(rawValue) + } +} + +extension IdentitySyncSnapshot { + /// A snapshot strategy for comparing strings based on equality. + public static let data = IdentitySyncSnapshot( + pathExtension: nil, + attachmentGenerator: .data + ) +} diff --git a/Sources/XCSnapshotTesting/Serialization/Defaults/ImageBytes.swift b/Sources/XCSnapshotTesting/Serialization/Defaults/ImageBytes.swift new file mode 100644 index 000000000..99009e842 --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/Defaults/ImageBytes.swift @@ -0,0 +1,224 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) +import UIKit +#elseif os(macOS) +@preconcurrency import AppKit +#endif + +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) || os(macOS) +/// Represents a serializable image for snapshot testing. +/// +/// `ImageBytes` wraps a `UIImage`or `NSImage` and provides serialization/deserialization capabilities, +/// enabling images to be compared in snapshot tests. Implements `BytesRepresentable` +/// to convert images to binary data (`Data`) and vice versa. +public struct ImageBytes: BytesRepresentable { + + fileprivate struct ImageScaleKey: DataSerializationConfigurationKey { + static let defaultValue: Double = 1 + } + + #if os(macOS) + public let rawValue: NSImage + #else + public let rawValue: UIKit.UIImage + #endif + + /// Initializes an instance from image data in a `BytesContainer`. + /// + /// - Parameter container: Container holding the binary image data. + /// - Throws: `BytesSerializationError` if deserialization fails (e.g., corrupted data). + public init(from container: BytesContainer) throws { + #if os(macOS) + guard let image = SDKImage(data: try container.read()) else { + throw BytesSerializationError() + } + #else + guard + let image = SDKImage( + data: try container.read(), + scale: container.configuration.imageScale + ) + else { + throw BytesSerializationError() + } + #endif + self.rawValue = image + } + + #if os(macOS) + /// Initializes from a `NSImage`. + /// + /// - Parameter rawValue: Image to convert to bytes for snapshot testing. + public init(rawValue: NSImage) { + self.rawValue = rawValue + } + #else + /// Initializes from a `UIImage`. + /// + /// - Parameter rawValue: Image to convert to bytes for snapshot testing. + public init(rawValue: UIKit.UIImage) { + self.rawValue = rawValue + } + #endif + + /// Serializes the image to binary data and writes it to the container. + /// + /// - Parameter container: Destination container for the image data. + /// - Throws: Error if serialization fails (e.g., failed image-to-data conversion). + public func serialize(to container: BytesContainer) throws { + guard let data = rawValue.pngData() else { + return + } + + try container.write(data) + } +} + +// MARK: - DataSerializationConfiguration + +extension DataSerializationConfiguration { + + /// Gets/sets the image scaling factor during serialization/deserialization. + /// + /// Controls how images are resized when converting to `Data` or reading from a `BytesContainer`. + /// - Note: Default value is `1.0`. + public var imageScale: Double { + get { self[ImageBytes.ImageScaleKey.self] } + set { self[ImageBytes.ImageScaleKey.self] = newValue } + } +} + +// MARK: - IdentitySyncSnapshot + +extension IdentitySyncSnapshot { + + /// Default configuration for image snapshots. + /// + /// - Notes: + /// - Uses default values like `precision: 1` (strict comparison) and `perceptualPrecision: nil` (Color/tonal tolerance). + /// - Uses `ImageDiffAttachmentGenerator` for visual diffs. + public static var image: Self { + .image() + } + + #if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) + /// Creates a custom image snapshot configuration with precision and scale settings. + /// + /// - Parameters: + /// - precision: Pixel tolerance for comparison (e.g., `0.95` allows 5% difference). + /// - perceptualPrecision: Color/tonal tolerance for perceptual comparison. + /// + /// - Example: + /// ```swift + /// let config = IdentitySyncSnapshot.image( + /// precision: 0.98 + /// ) + /// ``` + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1 + ) -> Self { + .init( + pathExtension: "png", + attachmentGenerator: ImageDiffAttachmentGenerator( + precision: precision, + perceptualPrecision: perceptualPrecision + ) + ) + } + #else + public static func image( + precision: Float = 1 + ) -> Self { + .init( + pathExtension: "png", + attachmentGenerator: ImageDiffAttachmentGenerator( + precision: precision, + perceptualPrecision: 1 + ) + ) + } + #endif +} + +// MARK: - IdentitySyncSnapshot + +#if os(macOS) +extension SyncSnapshot where Input: NSImage, Output == ImageBytes { + + /// Default configuration for image snapshots. + /// + /// - Notes: + /// - Uses default values like `precision: 1` (strict comparison) and `perceptualPrecision: nil` (Color/tonal tolerance). + /// - Uses `ImageDiffAttachmentGenerator` for visual diffs. + public static var image: SyncSnapshot { + .image() + } + + /// Creates a custom image snapshot configuration with precision and scale settings. + /// + /// - Parameters: + /// - precision: Pixel tolerance for comparison (e.g., `0.95` allows 5% difference). + /// - perceptualPrecision: Color/tonal tolerance for perceptual comparison. + /// + /// - Example: + /// ```swift + /// let config = IdentitySyncSnapshot.image( + /// precision: 0.98 + /// ) + /// ``` + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1 + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ).pullback { $0 } + } +} +#else +extension SyncSnapshot where Input: UIImage, Output == ImageBytes { + + /// Default configuration for image snapshots. + /// + /// - Notes: + /// - Uses default values like `precision: 1` (strict comparison) and `perceptualPrecision: nil` (Color/tonal tolerance). + /// - Uses `ImageDiffAttachmentGenerator` for visual diffs. + public static var image: SyncSnapshot { + .image() + } + + #if os(iOS) || os(tvOS) || os(visionOS) + /// Creates a custom image snapshot configuration with precision and scale settings. + /// + /// - Parameters: + /// - precision: Pixel tolerance for comparison (e.g., `0.95` allows 5% difference). + /// - perceptualPrecision: Color/tonal tolerance for perceptual comparison. + /// + /// - Example: + /// ```swift + /// let config = IdentitySyncSnapshot.image( + /// precision: 0.98 + /// ) + /// ``` + public static func image( + precision: Float = 1, + perceptualPrecision: Float = 1 + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision, + perceptualPrecision: perceptualPrecision + ).pullback { $0 } + } + #elseif os(watchOS) + public static func image( + precision: Float = 1 + ) -> SyncSnapshot { + IdentitySyncSnapshot.image( + precision: precision + ).pullback { $0 } + } + #endif +} +#endif +#endif diff --git a/Sources/XCSnapshotTesting/Serialization/Defaults/StringBytes.swift b/Sources/XCSnapshotTesting/Serialization/Defaults/StringBytes.swift new file mode 100644 index 000000000..8155ccc25 --- /dev/null +++ b/Sources/XCSnapshotTesting/Serialization/Defaults/StringBytes.swift @@ -0,0 +1,61 @@ +import Foundation + +/// A wrapper for string data in a bytes container, supporting UTF-8 encoding/decoding. +/// +/// `StringBytes` conforms to `BytesRepresentable` for byte stream serialization +/// and `ExpressibleByStringLiteral` for direct string initialization. It handles +/// UTF-8 encoding when writing to bytes containers and decoding when reading from them. +/// +/// - SeeAlso: BytesRepresentable, ExpressibleByStringLiteral +public struct StringBytes: BytesRepresentable, ExpressibleByStringLiteral { + + /// The raw string value stored by this wrapper. + public let rawValue: String + + /// Initializes a `StringBytes` instance by decoding UTF-8 data from a bytes container. + /// + /// - Parameter container: The bytes container to read from. + /// - Throws: `BytesSerializationError` if decoding fails. + public init(from container: BytesContainer) throws { + guard + let string = String( + data: try container.read(), + encoding: .utf8 + ) + else { throw BytesSerializationError() } + + self.rawValue = string + } + + /// Initializes a `StringBytes` instance directly from a string. + /// + /// - Parameter rawValue: The string value to wrap. + public init(rawValue: String) { + self.rawValue = rawValue + } + + /// Supports string literal initialization for `StringBytes`. + /// + /// - Parameter value: The string literal value. + public init(stringLiteral value: String) { + self.init(rawValue: value) + } + + /// Serializes the string value as UTF-8 data into a bytes container. + /// + /// - Parameter container: The bytes container to write to. + /// - Throws: Any error occurring during writing. + public func serialize(to container: BytesContainer) throws { + try container.write(Data(rawValue.utf8)) + } +} + +extension IdentitySyncSnapshot { + /// A snapshot strategy for comparing strings based on line-by-line equality. + /// + /// Uses `.lines` as the attachment generator and saves files with `.txt` extensions. + public static let lines = Self( + pathExtension: "txt", + attachmentGenerator: .lines + ) +} diff --git a/Sources/XCSnapshotTesting/Snapshot.swift b/Sources/XCSnapshotTesting/Snapshot.swift new file mode 100644 index 000000000..80a6f39d9 --- /dev/null +++ b/Sources/XCSnapshotTesting/Snapshot.swift @@ -0,0 +1,309 @@ +import Foundation + +/// A protocol defining an executor that processes input to produce a serializable output. +/// +/// `SnapshotExecutor` serves as the core processing unit for converting input data into +/// a format compatible with snapshot testing. It defines the relationship between +/// input and output types, and is used by ``Snapshot`` configurations to prepare data +/// for comparison and storage. +/// +/// - Notes: +/// - The `Output` type must conform to ``BytesRepresentable`` for serialization/deserialization +/// - Implementations should define how `Input` is transformed into `Output` +/// - Used by ``Snapshot`` configurations to execute processing flows +public protocol SnapshotExecutor: Sendable { + associatedtype Input + associatedtype Output +} + +/// A type alias for a synchronous snapshot configuration using ``Sync``. +/// +/// `SyncSnapshot` is a convenience type that wraps ``Snapshot`` with a synchronous executor +/// (`Sync`), enabling synchronous processing of input to serializable output. +/// +/// - Parameters: +/// - Input: The type of input data to be processed +/// - Output: The type of output data after processing, must conform to ``BytesRepresentable`` +/// +/// This type is useful when working with synchronous operations where input needs to be +/// transformed into a format suitable for snapshot testing and comparison. +public typealias SyncSnapshot = Snapshot> + +/// A type alias for an asynchronous snapshot configuration using ``Async``. +/// +/// `AsyncSnapshot` is a convenience type that wraps ``Snapshot`` with an asynchronous executor +/// (`Async`), enabling asynchronous processing of input to serializable output. +/// +/// - Parameters: +/// - Input: The type of input data to be processed, must conform to `Sendable` +/// - Output: The type of output data after processing, must conform to ``BytesRepresentable`` +/// +/// This type is useful when working with asynchronous operations where input needs to be +/// transformed into a format suitable for snapshot testing and comparison. +public typealias AsyncSnapshot = Snapshot< + Async +> + +/// A convenience type for synchronous snapshots where input and output types are identical. +/// +/// `IdentitySyncSnapshot` is a shorthand for `SyncSnapshot`, representing a +/// snapshot configuration where the input data type is the same as the output type. This +/// is useful when no transformation is needed between input and output, and the executor +/// simply passes the input through as the output. +/// +/// - Parameters: +/// - Output: The type of data being snapshotted, must conform to ``BytesRepresentable`` +/// +/// This type simplifies configuration when working with data that doesn't require processing +/// before comparison, such as raw values or unmodified objects. +public typealias IdentitySyncSnapshot = SyncSnapshot + +/// A convenience type for asynchronous snapshots where input and output types are identical. +/// +/// `IdentityAsyncSnapshot` is a shorthand for `AsyncSnapshot`, representing a +/// snapshot configuration where the input data type is the same as the output type. This +/// is useful when no transformation is needed between input and output, and the executor +/// simply passes the input through as the output. +/// +/// - Parameters: +/// - Output: The type of data being snapshotted, must conform to ``BytesRepresentable`` +/// +/// This type simplifies configuration when working with asynchronous data that doesn't require processing +/// before comparison, such as raw values or unmodified objects. +public typealias IdentityAsyncSnapshot = AsyncSnapshot + +/// Configuration defining how snapshots are generated, compared, and displayed during tests. +/// +/// `Snapshot` defines parameters including: +/// - Processing executor from input to serializable output +/// - Comparison attachment (diff) generator for mismatch cases +/// - File extension for saved snapshots +/// +/// - NOTE: The `Output` type must implement ``BytesRepresentable`` for serialization/deserialization +public struct Snapshot: Sendable where Executor.Output: BytesRepresentable { + + public typealias Input = Executor.Input + public typealias Output = Executor.Output + + /// The file extension used when saving snapshots (e.g., "json" for JSON files, "png" for images). + /// This determines the format of the snapshot file stored on disk. + /// Defaults to `nil` if not explicitly set. + public let pathExtension: String? + + /// The generator for diagnostic messages/attachments when a snapshot mismatch occurs. + /// + /// This attachment generator is responsible for creating visual diffs or diagnostic + /// information when the actual output doesn't match the expected snapshot. It's used + /// during test failures to provide detailed comparisons between the actual output + /// and the stored snapshot. + /// + /// - Note: The generator's value type must match the `Output` type of the snapshot + /// configuration for proper comparison diagnostics. + public let attachmentGenerator: any DiffAttachmentGenerator + + /// The processing flow that converts `Input` to serializable `Output`. + /// This executor is responsible for transforming the input data into the output format + /// compatible with snapshot testing and comparison. + /// + /// - Note: The executor is used to prepare data before comparison, ensuring + /// output matches the expected format for snapshot storage and validation. + public let executor: Executor + + /// Initializes configuration with executor, diff generator, and storage options. + /// + /// - Parameters: + /// - pathExtension: File extension used when saving snapshots (e.g., `"png"` for images) + /// - attachmentGenerator: Generator for diagnostic messages/attachments on snapshot mismatch + /// - executor: Processing flow that converts `Input` to serializable `Output` + /// + /// Notes: + /// - `Output` must match `AttachmentGenerator.Value` type + /// - Executor executes before comparison to prepare data + /// + /// Example: + /// ```swift + /// let config = Snapshot( + /// pathExtension: "json", + /// attachmentGenerator: MyDiffGenerator(), + /// executor: Async { input in + /// return try await encodeToJSON(input) + /// } + /// ) + /// ``` + public init( + pathExtension: String? = nil, + attachmentGenerator: AttachmentGenerator, + executor: Executor + ) where AttachmentGenerator: DiffAttachmentGenerator { + self.pathExtension = pathExtension + self.attachmentGenerator = attachmentGenerator + self.executor = executor + } + + /// Changes the configuration's input type using a executor transformation closure. + /// + /// - Parameter closure: Function receiving current executor and returning new executor with modified input type + /// - Returns: New configuration with updated input type and same output + /// + /// Notes: + /// - Useful for adapting existing configurations to new input types + /// - Propagates errors from closure + /// + /// Example: + /// ```swift + /// let newConfig = config.map { executor in + /// executor.pullback { rawInput in + /// try await processRawInput(rawInput) + /// } + /// } + /// ``` + public func map( + _ closure: @Sendable (Executor) throws -> NewExecutor + ) rethrows -> Snapshot where NewExecutor.Output == Executor.Output { + Snapshot( + pathExtension: pathExtension, + attachmentGenerator: attachmentGenerator, + executor: try closure(executor) + ) + } +} + +extension IdentitySyncSnapshot where Executor.Output: BytesRepresentable, Executor.Input == Executor.Output.RawValue { + + /// Initializes a new configuration with the specified path extension and attachment generator. + /// + /// - Parameters: + /// - pathExtension: The file extension used when saving snapshots (e.g., `"png"` for images). + /// - attachmentGenerator: Generator for diagnostic messages/attachments on snapshot mismatch. + /// + /// Notes: + /// - The executor is set to identity function for input type matching. + /// - `Output` must match `AttachmentGenerator.Value` type. + /// + /// Example: + /// ```swift + /// let config = IdentitySyncSnapshot( + /// pathExtension: "json", + /// attachmentGenerator: MyDiffGenerator() + /// ) + /// ``` + public init( + pathExtension: String?, + attachmentGenerator: AttachmentGenerator + ) where AttachmentGenerator: DiffAttachmentGenerator, Executor == Sync { + self.init( + pathExtension: pathExtension, + attachmentGenerator: attachmentGenerator, + executor: .init { .init(rawValue: $0) } + ) + } +} + +extension IdentityAsyncSnapshot where Executor.Output: BytesRepresentable, Executor.Input == Executor.Output.RawValue { + + /// Initializes a new configuration with the specified path extension and attachment generator for asynchronous context. + /// + /// - Parameters: + /// - pathExtension: The file extension used when saving snapshots (e.g., `"png"` for images). + /// - attachmentGenerator: Generator for diagnostic messages/attachments on snapshot mismatch. + /// + /// Notes: + /// - The executor is set to identity function for input type matching. + /// - `Output` must match `AttachmentGenerator.Value` type. + /// + /// Example: + /// ```swift + /// let config = IdentityAsyncSnapshot( + /// pathExtension: "json", + /// attachmentGenerator: MyDiffGenerator() + /// ) + /// ``` + public init( + pathExtension: String?, + attachmentGenerator: AttachmentGenerator + ) where AttachmentGenerator: DiffAttachmentGenerator, Executor == Async { + self.init( + pathExtension: pathExtension, + attachmentGenerator: attachmentGenerator, + executor: .init { .init(rawValue: $0) } + ) + } +} + +extension AsyncSnapshot { + + /// Changes the configuration's input type by applying a closure that transforms new input into the original input type. + /// + /// This method allows adapting a snapshot configuration to work with a different input type while reusing the same executor. + /// The closure is responsible for converting the new input type into the original input type expected by the executor. + /// + /// - Parameter closure: A function that takes a new input type and returns the original input type, compatible with the executor. + /// - Returns: A new snapshot configuration with the updated input type, maintaining the same output type. + /// + /// - Note: This is useful for wrapping raw input types with additional processing before passing them to the executor. + /// + /// Example: + /// ```swift + /// let adaptedConfig = config.pullback { rawInput in + /// try await processRawInput(rawInput) + /// } + /// ``` + public func pullback( + _ closure: @escaping @Sendable (NewInput) async throws -> Input + ) -> AsyncSnapshot where Executor == Async { + map { executor in + executor.pullback(closure) + } + } +} + +extension SyncSnapshot { + + /// Changes the configuration's input type by applying a closure that transforms new input into the original input type. + /// + /// This method allows adapting a snapshot configuration to work with a different input type while reusing the same executor. + /// The closure is responsible for converting the new input type into the original input type expected by the executor. + /// + /// - Parameter closure: A function that takes a new input type and returns the original input type, compatible with the executor. + /// - Returns: A new snapshot configuration with the updated input type, maintaining the same output type. + /// + /// - Note: This is useful for wrapping raw input types with additional processing before passing them to the executor. + /// + /// Example: + /// ```swift + /// let adaptedConfig = config.pullback { rawInput in + /// try processRawInput(rawInput) + /// } + /// ``` + public func pullback( + _ closure: @escaping @Sendable (NewInput) throws -> Input + ) -> SyncSnapshot where Executor == Sync { + map { executor in + executor.pullback(closure) + } + } + + /// Changes the configuration's input type by applying a closure that transforms new input into the original input type. + /// + /// This method allows adapting a snapshot configuration to work with a different input type while reusing the same executor. + /// The closure is responsible for converting the new input type into the original input type expected by the executor. + /// + /// - Parameter closure: A function that takes a new input type and returns the original input type, compatible with the executor. + /// - Returns: A new snapshot configuration with the updated input type, maintaining the same output type. + /// + /// - Note: This is useful for wrapping raw input types with additional processing before passing them to the executor. + /// + /// Example: + /// ```swift + /// let adaptedConfig = config.pullback { rawInput in + /// try await processRawInput(rawInput) + /// } + /// ``` + public func pullback( + _ closure: @escaping @Sendable (NewInput) async throws -> Input + ) -> AsyncSnapshot where Executor == Sync { + map { executor in + executor.pullback(closure) + } + } +} diff --git a/Sources/XCSnapshotTesting/Sync.swift b/Sources/XCSnapshotTesting/Sync.swift new file mode 100644 index 000000000..89dc1fe1c --- /dev/null +++ b/Sources/XCSnapshotTesting/Sync.swift @@ -0,0 +1,396 @@ +import Foundation + +/// A continuation that allows resuming a synchronous operation with a value or error. +/// +/// `SyncContinuation` is used to signal the completion of a synchronous operation, +/// either by returning a value or throwing an error. It provides a way to bridge +/// between synchronous and asynchronous code, particularly in the context of +/// ``Sync`` workflows. +/// +/// - Note: This type is designed to be `Sendable`, making it suitable for use +/// in concurrent environments. +public struct SyncContinuation: Sendable { + + fileprivate let block: @Sendable (Result) -> Void + + fileprivate init( + block: @Sendable @escaping (Result) -> Void + ) { + self.block = block + } + + /// Resumes the continuation with a result. + /// + /// - Parameter result: The result to resume the continuation with. + public func resume(with result: Result) { + block(result) + } + + /// Resumes the continuation with a success value. + /// + /// - Parameter value: The value to resume the continuation with. + public func resume(returning value: Output) { + resume(with: .success(value)) + } + + /// Resumes the continuation by throwing an error. + /// + /// - Parameter error: The error to resume the continuation with. + public func resume(throwing error: Error) { + resume(with: .failure(error)) + } +} + +/// A wrapper for synchronous operations that allows composing and transforming workflows with input and output values. +/// +/// `Sync` provides a functional interface for executing synchronous tasks, with support for mapping outputs, pulling back inputs, and inserting delays. +/// +/// - Parameters: +/// - Input: The type of input value the synchronous operation accepts. +/// - Output: The type of output value the synchronous operation produces. +public struct Sync: SnapshotExecutor { + + private let producer: @Sendable (Input, SyncContinuation) -> Void + + /// Initializes a `Sync` instance with a specific input type and block. + /// + /// - Parameters: + /// - inputType: The type of input value. This is inferred from the block if not explicitly provided. + /// - block: A closure that takes an input value and a continuation for the output, potentially throwing an error. + public init( + _ inputType: Input.Type = Input.self, + _ block: @escaping @Sendable (Input, SyncContinuation) throws -> Void + ) { + self.producer = { input, continuation in + do { + try block(input, continuation) + } catch { + continuation.resume(throwing: error) + } + } + } + + /// Initializes a `Sync` instance with a specific input type and block. + /// + /// - Parameters: + /// - inputType: The type of input value. This is inferred from the block if not explicitly provided. + /// - block: A closure that takes an input value and returns an output value, potentially throwing an error. + public init( + _ inputType: Input.Type = Input.self, + _ block: @escaping @Sendable (Input) throws -> Output + ) { + self.init(inputType) { input, continuation in + continuation.resume(returning: try block(input)) + } + } + + /// Invokes the synchronous operation with the given input and completion handler. + /// + /// This method allows executing the `Sync` operation by providing an `Input` value and a + /// completion callback that receives the result. The callback is invoked when the operation + /// completes, either with a success value or an error. + /// + /// - Parameters: + /// - input: The input value to pass to the synchronous operation. + /// - callback: A closure that handles the result of the operation. It receives a + /// `Result` indicating success or failure. + /// + /// - Note: This method is designed for synchronous execution, but the ``SyncContinuation`` + /// mechanism allows bridging to asynchronous patterns when needed. + public func callAsFunction( + _ input: Input, + callback: @escaping @Sendable (Result) -> Void + ) { + producer( + input, + SyncContinuation(block: callback) + ) + } + + func callAsFunction(_ input: Input) async throws -> Output where Output: Sendable { + try await withUnsafeThrowingContinuation { continuation in + self(input) { + continuation.resume(with: $0) + } + } + } +} + +extension Sync { + + /// Transforms the output of this `Sync` operation using a closure that takes the output and a continuation for the new type. + /// + /// This method allows for custom transformation logic where the closure can manually manage the continuation + /// for the new output type. The closure is provided with the original output and a continuation for the new type. + /// + /// - Parameters: + /// - block: A closure that takes the output of this `Sync` and a continuation for the new output type, + /// and performs the transformation. + /// + /// - Returns: A new `Sync` instance that produces the transformed output. + public func map( + _ block: @escaping @Sendable (Output, SyncContinuation) -> Void + ) -> Sync { + .init { input, continuation in + self(input) { result in + switch result { + case .success(let output): + block(output, continuation) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } + + /// Transforms the output of this `Sync` operation using a closure that maps the output to a new type. + /// + /// This method provides a convenient way to transform the output by applying a closure that may throw. + /// The closure is called with the output of this `Sync`, and the result is passed to the continuation. + /// + /// - Parameters: + /// - block: A closure that takes the output of this `Sync`, may throw an error, and returns a new output type. + /// + /// - Returns: A new `Sync` instance that produces the transformed output. + public func map( + _ block: @escaping @Sendable (Output) throws -> NewOutput + ) -> Sync { + .init { input, continuation in + self(input) { result in + switch result { + case .success(let output): + continuation.resume( + with: Result { + try block(output) + } + ) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } + + /// Creates a new `Sync` instance that transforms the input using the provided closure. + /// + /// This method allows adapting a `NewInput` to the expected `Input` type of the original `Sync` operation. + /// The closure is responsible for converting the new input into the original input type. + /// + /// - Parameter block: A closure that takes a `NewInput` and produces an `Input`, possibly throwing an error. + /// - Returns: A new `Sync` instance that operates on `NewInput` but uses the original `Input` type internally. + public func pullback( + _ block: @escaping @Sendable (NewInput) throws -> Input + ) -> Sync { + .init { newInput, continuation in + do { + try self(block(newInput)) { + continuation.resume(with: $0) + } + } catch { + continuation.resume(throwing: error) + } + } + } + + /// Creates a new `Sync` instance that transforms the input using a continuation-based closure. + /// + /// This method provides low-level control over input transformation, allowing asynchronous or + /// complex logic through the `SyncContinuation` parameter. The closure must handle both success + /// and failure cases when converting `NewInput` to `Input`. + /// + /// - Parameter block: A closure that takes a `NewInput` and a continuation for `Input`, performing + /// the transformation asynchronously or with custom logic. + /// - Returns: A new `Sync` instance that operates on `NewInput` but uses the original `Input` type internally. + public func pullback( + _ block: @escaping @Sendable (NewInput, SyncContinuation) -> Void + ) -> Sync { + .init { newInput, continuation in + block( + newInput, + .init { + switch $0 { + case .success(let input): + self(input) { + continuation.resume(with: $0) + } + case .failure(let error): + continuation.resume(throwing: error) + } + } + ) + } + } +} + +extension Sync where Output: Sendable { + + /// Transforms the output of this `Sync` operation using an asynchronous closure that may throw. + /// + /// This method allows for asynchronous transformation of the output by applying a closure + /// that can throw errors. The closure is called with the output of this `Sync`, and the result + /// is passed to the continuation of the new `Async` instance. + /// + /// - Parameters: + /// - block: A closure that takes the output of this `Sync`, may throw an error, and returns + /// a new output type `NewOutput` which must be `Sendable`. + /// + /// - Returns: A new `Async` instance that produces the transformed output. + public func map( + _ block: @escaping @Sendable (Output) async throws -> NewOutput + ) -> Async where Input: Sendable { + .init { input in + let output = try await self(input) + return try await block(output) + } + } + + /// Creates a new `Async` instance that transforms the input using an asynchronous closure. + /// + /// This method adapts a `NewInput` to the expected `Input` type of the original `Sync` operation + /// by applying the provided async closure. The closure may throw errors and returns an `Input`. + /// The resulting `Async` instance operates on `NewInput` but uses the original `Input` type internally. + /// + /// - Parameter block: A closure that takes a `NewInput` and returns an `Input`, possibly throwing an error. + /// - Returns: A new `Async` instance that produces the original `Output` type, adapted from `NewInput`. + /// - Requires: `Output` must conform to `Sendable` for this method to be available. + public func pullback( + _ block: @escaping @Sendable (NewInput) async throws -> Input + ) -> Async where Output: Sendable { + .init { newInput in + let input = try await block(newInput) + return try await self(input) + } + } +} + +extension Sync where Input: Sendable, Output: Sendable { + + /// Introduces a delay in the synchronous operation before resuming. + /// + /// This method provides a convenient way to pause the execution of a `Sync` operation + /// for the specified number of nanoseconds. The operation resumes after the delay, + /// preserving the original input and output values. + /// + /// - Parameter duration: The duration of the delay in nanoseconds. + /// - Returns: An `Async` instance that represents the delayed operation, preserving + /// the original input and output types. + public func sleep(nanoseconds duration: UInt64) -> Async { + map { + try await Task.sleep(nanoseconds: duration) + return $0 + } + } + + /// Delays the execution of the synchronous operation until the specified deadline. + /// + /// This method introduces a delay in the operation's execution, pausing it until the given `deadline` is reached. + /// The delay can be adjusted with a `tolerance` and uses the specified `clock` for timing. + /// + /// - Parameters: + /// - deadline: The time until which the operation should be delayed. + /// - tolerance: The allowable deviation from the deadline. If `nil`, no tolerance is applied. + /// - clock: The clock to use for measuring time. Defaults to `.continuous`. + /// + /// - Returns: An `Async` instance that represents the delayed operation, preserving the original input and output types. + @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) + public func sleep( + until deadline: C.Instant, + tolerance: C.Instant.Duration? = nil, + clock: C = .continuous + ) -> Async where C: Clock { + map { + try await Task.sleep( + until: deadline, + tolerance: tolerance, + clock: clock + ) + return $0 + } + } + + /// Delays the execution of the synchronous operation for the specified duration. + /// + /// This method introduces a delay in the operation's execution, pausing it for the given `duration`. + /// The delay can be adjusted with a `tolerance` and uses the specified `clock` for timing. + /// + /// - Parameters: + /// - duration: The duration of the delay. + /// - tolerance: The allowable deviation from the duration. If `nil`, no tolerance is applied. + /// - clock: The clock to use for measuring time. Defaults to `.continuous`. + /// + /// - Returns: An `Async` instance that represents the delayed operation, preserving + /// the original input and output types. + @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) + public func sleep( + for duration: C.Instant.Duration, + tolerance: C.Instant.Duration? = nil, + clock: C = .continuous + ) -> Async where C: Clock { + map { + try await Task.sleep( + for: duration, + tolerance: tolerance, + clock: clock + ) + return $0 + } + } +} + +@_spi(Internals) public func performOnMainThread( + _ block: @MainActor () throws -> R +) rethrows -> R { + if Thread.isMainThread { + try MainActor.assumeIsolated(block) + } else { + try DispatchQueue.main.sync(execute: block) + } +} + +private final class SyncSequence: @unchecked Sendable { + + var items: [Output] { + lock.withLock { _items } + } + + private let lock = NSLock() + private var _items: [Output] = [] + + init() {} + + func append(_ item: Output) { + lock.withLock { + _items.append(item) + } + } +} + +extension Array { + + func sequence() -> Sync where Element == Sync { + guard !isEmpty else { + return Sync { _ in [] } + } + + return .init { input, continuation in + let dispatchGroup = DispatchGroup() + let sequence = SyncSequence() + + for sync in self { + dispatchGroup.enter() + + sync(input) { result in + if case .success(let output) = result { + sequence.append(output) + } + + dispatchGroup.leave() + } + + dispatchGroup.wait() + } + + continuation.resume(returning: sequence.items) + } + } +} diff --git a/Sources/XCSnapshotTesting/Tester/SnapshotEngine.swift b/Sources/XCSnapshotTesting/Tester/SnapshotEngine.swift new file mode 100644 index 000000000..c5f8bd501 --- /dev/null +++ b/Sources/XCSnapshotTesting/Tester/SnapshotEngine.swift @@ -0,0 +1,38 @@ +import Foundation + +@_spi(Internals) +public protocol SnapshotEngine: Sendable where Executor.Output: BytesRepresentable { + + associatedtype Executor: SnapshotExecutor + + func sourceURL( + for filePath: StaticString, + using tester: SnapshotTester + ) throws -> URL + + func temporaryURL( + for filePath: StaticString, + using tester: SnapshotTester + ) throws -> URL? + + func contentExists( + at url: URL + ) -> Bool + + func loadSnapshot( + from url: URL, + using tester: SnapshotTester + ) throws -> Executor.Output + + func perform( + _ operation: SnapshotPerformOperation, + contents: Data, + to url: URL, + using tester: SnapshotTester + ) throws + + func generateFailureMessage( + for context: SnapshotFailContext, + using tester: SnapshotTester + ) -> String +} diff --git a/Sources/XCSnapshotTesting/Tester/SnapshotFailContext.swift b/Sources/XCSnapshotTesting/Tester/SnapshotFailContext.swift new file mode 100644 index 000000000..d4728bb8e --- /dev/null +++ b/Sources/XCSnapshotTesting/Tester/SnapshotFailContext.swift @@ -0,0 +1,35 @@ +import Foundation + +@_spi(Internals) +public struct SnapshotFailContext: Sendable { + + public enum Reason: Sendable { + case missing + case doesNotMatch + case allRecordMode + case timeout + } + + public let function: StaticString + public let reason: Reason + public let url: URL + public let diff: String? + public let additionalInformation: String? + public let didWriteNewSnapshot: Bool + + init( + function: StaticString, + reason: Reason, + url: URL, + diff: String?, + additionalInformation: String?, + didWriteNewSnapshot: Bool + ) { + self.function = function + self.reason = reason + self.url = url + self.diff = diff + self.additionalInformation = additionalInformation + self.didWriteNewSnapshot = didWriteNewSnapshot + } +} diff --git a/Sources/XCSnapshotTesting/Tester/SnapshotFailure.swift b/Sources/XCSnapshotTesting/Tester/SnapshotFailure.swift new file mode 100644 index 000000000..29fec43e8 --- /dev/null +++ b/Sources/XCSnapshotTesting/Tester/SnapshotFailure.swift @@ -0,0 +1,16 @@ +import Foundation + +@_spi(Internals) +public struct SnapshotFailure: Sendable { + + public let message: String + public let reason: SnapshotFailContext.Reason + + init( + message: String, + context: SnapshotFailContext + ) { + self.message = message + self.reason = context.reason + } +} diff --git a/Sources/XCSnapshotTesting/Tester/SnapshotFileEngine.swift b/Sources/XCSnapshotTesting/Tester/SnapshotFileEngine.swift new file mode 100644 index 000000000..14e833dc3 --- /dev/null +++ b/Sources/XCSnapshotTesting/Tester/SnapshotFileEngine.swift @@ -0,0 +1,197 @@ +import Foundation + +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +import CoreServices +import UniformTypeIdentifiers +#endif + +struct SnapshotFileEngine: SnapshotEngine +where Executor.Output: BytesRepresentable { + + let sourceURL: URL? + + func sourceURL( + for filePath: StaticString, + using tester: SnapshotTester> + ) throws -> URL { + let fileURL = URL( + fileURLWithPath: String(describing: filePath), + isDirectory: false + ) + + var sourceURL = + sourceURL + ?? snapshotURL( + fileURL.deletingLastPathComponent(), + at: "__Snapshots__", + using: tester + ) + + sourceURL.appendPathComponent(fileURL.deletingPathExtension().lastPathComponent) + + try FileManager.default.createDirectory( + at: sourceURL, + withIntermediateDirectories: true + ) + + return sourceURL + } + + func temporaryURL( + for filePath: StaticString, + using tester: SnapshotTester> + ) throws -> URL? { + let fileURL = URL( + fileURLWithPath: String(describing: filePath), + isDirectory: false + ) + + var snapshotURL = snapshotURL( + ProcessInfo.artifactsDirectory, + at: "Snapshots", + using: tester + ) + + snapshotURL.appendPathComponent(fileURL.deletingPathExtension().lastPathComponent) + + try FileManager.default.createDirectory( + at: snapshotURL, + withIntermediateDirectories: true + ) + + return snapshotURL + } + + func contentExists(at url: URL) -> Bool { + url.isFileURL && FileManager.default.fileExists(atPath: url.path) + } + + func loadSnapshot( + from url: URL, + using tester: SnapshotTester> + ) throws -> Executor.Output { + try tester.serialization.deserialize( + Executor.Output.self, + from: Data(contentsOf: url) + ) + } + + func perform( + _ operation: SnapshotPerformOperation, + contents: Data, + to url: URL, + using tester: SnapshotTester> + ) throws { + guard case .write = operation else { + return + } + + try contents.write(to: url, options: .atomic) + } + + func generateFailureMessage( + for context: SnapshotFailContext, + using tester: SnapshotTester> + ) -> String { + switch context.reason { + case .missing: + return missing(context) + case .doesNotMatch: + return doesNotMatch(context) + case .allRecordMode: + return allRecordMode(context) + case .timeout: + return timeout(context, timeout: tester.timeout) + } + } +} + +extension SnapshotFileEngine { + + fileprivate func snapshotURL( + _ url: URL, + at componentPath: String, + using tester: SnapshotTester> + ) -> URL { + var snapshotsURL = url + + snapshotsURL.appendPathComponent(componentPath) + + if !tester.platform.isEmpty { + snapshotsURL.appendPathComponent(tester.platform) + } + + if let platformVersion = tester.platformVersion { + snapshotsURL.appendPathComponent(platformVersion) + } + + return snapshotsURL + } +} + +extension SnapshotFileEngine { + + fileprivate func missing(_ context: SnapshotFailContext) -> String { + let name = String(describing: context.function) + + if context.didWriteNewSnapshot { + return """ + No reference was found on disk. Automatically recorded snapshot: … + + open "\(context.url.absoluteString)" + + Re-run "\(name)" to assert against the newly-recorded snapshot. + """ + } else { + return + "No reference was found on disk. New snapshot was not recorded because recording is disabled" + } + } + + fileprivate func doesNotMatch(_ context: SnapshotFailContext) -> String { + let name = String(describing: context.function) + + var message = "Snapshot \"\(name)\" does not match reference." + + if context.didWriteNewSnapshot { + message += """ + A new snapshot was automatically recorded. + + open "\(context.url.absoluteString)" + """ + } + + if let diff = context.diff { + message += "\n\n" + diff + } + + if let additionalInformation = context.additionalInformation { + message += "\n\n" + additionalInformation.trimmingCharacters(in: .whitespacesAndNewlines) + } + + return message + } + + fileprivate func allRecordMode(_ context: SnapshotFailContext) -> String { + let name = String(describing: context.function) + + return """ + Record mode is on. Automatically recorded snapshot: … + + open "\(context.url.absoluteString)" + + Turn record mode off and re-run "\(name)" to assert against the newly-recorded snapshot + """ + } + + fileprivate func timeout(_ context: SnapshotFailContext, timeout: TimeInterval) -> String { + """ + Exceeded timeout of \(timeout) seconds waiting for snapshot. + + This can happen when an asynchronously rendered view (like a web view) has not loaded. \ + Ensure that every subview of the view hierarchy has loaded to avoid timeouts, or, if a \ + timeout is unavoidable, consider setting the "timeout" parameter of "assert" to \ + a higher value. + """ + } +} diff --git a/Sources/XCSnapshotTesting/Tester/SnapshotPerformOperation.swift b/Sources/XCSnapshotTesting/Tester/SnapshotPerformOperation.swift new file mode 100644 index 000000000..26b84511b --- /dev/null +++ b/Sources/XCSnapshotTesting/Tester/SnapshotPerformOperation.swift @@ -0,0 +1,5 @@ +@_spi(Internals) +public enum SnapshotPerformOperation { + case notify + case write +} diff --git a/Sources/XCSnapshotTesting/Tester/SnapshotTester.swift b/Sources/XCSnapshotTesting/Tester/SnapshotTester.swift new file mode 100644 index 000000000..4b4da99a0 --- /dev/null +++ b/Sources/XCSnapshotTesting/Tester/SnapshotTester.swift @@ -0,0 +1,400 @@ +import Foundation + +#if canImport(XCTest) +import XCTest +#endif + +@_spi(Internals) +public struct SnapshotTester: Sendable { + + public typealias Executor = Engine.Executor + + public let serialization: DataSerialization + public let timeout: TimeInterval + + public let fileID: StaticString + public let filePath: StaticString + public let function: StaticString + public let line: UInt + public let column: UInt + + public let platform: String + public let platformVersion: String? + + private let engine: Engine + private let record: RecordMode + private let name: String? + + public init( + engine: Engine, + record: RecordMode?, + timeout: TimeInterval, + name: String?, + serialization: DataSerialization, + fileID: StaticString, + filePath: StaticString, + function: StaticString, + line: UInt, + column: UInt + ) { + if !TestingSystem.shared.isSwiftTestingRunning { + #if canImport(XCTest) + XCTestCase.registerObserverIfNeeded() + #endif + } + + self.engine = engine + self.record = record ?? SnapshotEnvironment.current.recordMode + self.platform = SnapshotEnvironment.current.platform + self.platformVersion = SnapshotEnvironment.current.platformVersion + self.timeout = timeout + self.name = name + self.serialization = serialization + self.fileID = fileID + self.filePath = filePath + self.function = function + self.line = line + self.column = column + } + + public func callAsFunction( + _ input: Input, + for snapshot: Snapshot + ) async throws -> SnapshotFailure? where Executor == Async { + do { + let diffable: Executor.Output + + if timeout > .zero { + diffable = try await Task.timeout(timeout) { + try await snapshot.executor(input) + } + } else { + diffable = try await snapshot.executor(input) + } + + return try assert( + diffable: diffable, + pathExtension: snapshot.pathExtension, + attachmentGenerator: snapshot.attachmentGenerator + ) + } catch is TaskTimeout { + return fail( + reason: .timeout, + snapshotURL: FileManager.default.temporaryDirectory, + didWriteNewSnapshot: false + ) + } catch { + throw error + } + } + + public func callAsFunction( + _ input: Input, + for snapshot: Snapshot + ) throws -> SnapshotFailure? where Executor == Sync { + let dispatchGroup = DispatchGroup() + let unsafeDiffable = UnsafeSyncDiffable() + + dispatchGroup.enter() + snapshot.executor(input) { result in + unsafeDiffable.result = result + dispatchGroup.leave() + } + + let result = dispatchGroup.wait( + timeout: .now() + .nanoseconds(Int(timeout * 1_000_000_000)) + ) + + switch result { + case .success: + return try assert( + diffable: unsafeDiffable.result.get(), + pathExtension: snapshot.pathExtension, + attachmentGenerator: snapshot.attachmentGenerator + ) + case .timedOut: + return fail( + reason: .timeout, + snapshotURL: FileManager.default.temporaryDirectory, + didWriteNewSnapshot: false + ) + } + } +} + +extension SnapshotTester { + + fileprivate func assert( + diffable: Executor.Output, + pathExtension: String?, + attachmentGenerator: any DiffAttachmentGenerator + ) throws -> SnapshotFailure? { + let snapshotURL = try snapshotURL( + pathExtension: pathExtension + ) + + guard record == .all else { + return try assertWithReference( + diffable: diffable, + snapshotURL: snapshotURL, + attachmentGenerator: attachmentGenerator + ) + } + + try record( + diffable, + url: snapshotURL, + write: true, + attachments: nil + ) + + return fail( + reason: .allRecordMode, + snapshotURL: snapshotURL, + didWriteNewSnapshot: true + ) + } + + private func assertWithReference( + diffable: Executor.Output, + snapshotURL: URL, + attachmentGenerator: any DiffAttachmentGenerator + ) throws -> SnapshotFailure? { + guard !engine.contentExists(at: snapshotURL) else { + return try compare( + reference: engine.loadSnapshot(from: snapshotURL, using: self), + diffable: diffable, + snapshotURL: snapshotURL, + attachmentGenerator: attachmentGenerator + ) + } + + try record( + diffable, + url: snapshotURL, + write: record == .missing, + attachments: nil + ) + + return fail( + reason: .missing, + snapshotURL: snapshotURL, + didWriteNewSnapshot: record == .missing + ) + } + + private func compare( + reference: Executor.Output, + diffable: Executor.Output, + snapshotURL: URL, + attachmentGenerator: any DiffAttachmentGenerator + ) throws -> SnapshotFailure? { + guard + let messageAttachment = attachmentGenerator( + from: reference, + with: diffable + ) + else { + try notify(diffable, to: snapshotURL) + return nil + } + + let failedURL: URL? + + if let temporaryURL = try engine.temporaryURL(for: filePath, using: self), record != .failed { + let url = temporaryURL.appendingPathComponent(snapshotURL.lastPathComponent) + + try engine.perform( + .write, + contents: serialization.serialize(diffable), + to: url, + using: self + ) + + failedURL = url + } else { + failedURL = nil + } + + try record( + diffable, + url: snapshotURL, + write: record == .failed, + attachments: messageAttachment.attachments + ) + + return fail( + reason: .doesNotMatch, + snapshotURL: snapshotURL, + diff: failedURL.map { + SnapshotEnvironment.current.diffTool( + currentFilePath: snapshotURL.absoluteString, + failedFilePath: $0.absoluteString + ) + }, + additionalInformation: messageAttachment.message, + didWriteNewSnapshot: record == .failed + ) + } + + private func notify( + _ diffable: Executor.Output, + to url: URL + ) throws { + let diffableData = try serialization.serialize(diffable) + + try engine.perform( + .notify, + contents: diffableData, + to: url, + using: self + ) + } + + private func record( + _ diffable: Executor.Output, + url: URL, + write: Bool, + attachments: [SnapshotAttachment]? + ) throws { + let diffableData = try serialization.serialize(diffable) + + try engine.perform( + write ? .write : .notify, + contents: diffableData, + to: url, + using: self + ) + + if let attachments { + add("Attached Failure Diff", attachments: attachments) + } else { + add(diffableData, for: url) + } + } + + private func fail( + reason: SnapshotFailContext.Reason, + snapshotURL: URL, + diff: String? = nil, + additionalInformation: String? = nil, + didWriteNewSnapshot: Bool + ) -> SnapshotFailure { + let context = SnapshotFailContext( + function: function, + reason: reason, + url: snapshotURL, + diff: diff, + additionalInformation: additionalInformation, + didWriteNewSnapshot: didWriteNewSnapshot + ) + + return SnapshotFailure( + message: engine.generateFailureMessage(for: context, using: self), + context: context + ) + } +} + +// MARK: - Source URL + +extension SnapshotTester { + + fileprivate func snapshotURL( + pathExtension: String? + ) throws -> URL { + let sourceURL = try engine.sourceURL(for: filePath, using: self) + let function = String(describing: function).sanitizingPathComponent() + + let uniqueID: String + + if let name { + uniqueID = name.sanitizingPathComponent() + } else { + uniqueID = self.uniqueID(function, at: sourceURL) + } + + let snapshotURL = sourceURL.appendingPathComponent( + function + "." + uniqueID + ) + + guard let pathExtension else { + return snapshotURL + } + + return snapshotURL.appendingPathExtension(pathExtension) + } + + private func uniqueID(_ function: String, at url: URL) -> String { + String( + TestingSession.shared.functionPosition( + fileID: fileID, + filePath: filePath, + function: function, + line: line, + column: column + ) + ) + } +} + +// MARK: - Attachments + +extension SnapshotTester { + + func add(_ named: String, attachments: [SnapshotAttachment]) { + TestingSystem.shared.add( + named, + attachments: attachments, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + } + + func add( + _ diffable: Data, + for url: URL + ) { + #if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) || os(macOS) + let attachment = SnapshotAttachment( + uniformTypeIdentifier: url.pathExtension.uniformTypeIdentifier(), + name: url.lastPathComponent, + payload: diffable + ) + #else + let attachment = SnapshotAttachment( + uniformTypeIdentifier: "public.\(url.pathExtension)", + name: url.lastPathComponent, + payload: diffable + ) + #endif + + TestingSystem.shared.add( + "Attached Recorded Snapshot", + attachments: [attachment], + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + } +} + +struct UnsafeSyncDiffableError: Error {} + +final class UnsafeSyncDiffable: @unchecked Sendable { + + var result: Result { + get { lock.withLock { _result } } + set { lock.withLock { _result = newValue } } + } + + private let lock = NSLock() + private var _result: Result + + init() { + self._result = .failure(UnsafeSyncDiffableError()) + } +} diff --git a/Sources/XCSnapshotTesting/TestingSystem.swift b/Sources/XCSnapshotTesting/TestingSystem.swift new file mode 100644 index 000000000..5a8defe1c --- /dev/null +++ b/Sources/XCSnapshotTesting/TestingSystem.swift @@ -0,0 +1,151 @@ +import Foundation + +#if canImport(XCTest) +@preconcurrency import XCTest +#endif + +@_spi(Internals) +public struct TestingSystemEnvironment { + + public var recordMode: RecordMode? + public var diffTool: DiffTool? + public var maxConcurrentTests: Int? + public var platform: String? + + public init( + recordMode: RecordMode? = nil, + diffTool: DiffTool? = nil, + maxConcurrentTests: Int? = nil, + platform: String? = nil + ) { + self.recordMode = recordMode + self.diffTool = diffTool + self.maxConcurrentTests = maxConcurrentTests + self.platform = platform + } +} + +@_spi(Internals) +public protocol SwiftTestingSystem { + + var environment: TestingSystemEnvironment? { get } + + var isRunning: Bool { get } + + var isTestCompletionAttached: Bool { get } + + func add( + _ name: String, + attachments: [SnapshotAttachment], + fileID: StaticString, + filePath: StaticString, + line: UInt, + column: UInt + ) + + func record( + message: String, + fileID: StaticString, + filePath: StaticString, + line: UInt, + column: UInt + ) +} + +@_spi(Internals) +public final class TestingSystem: Sendable { + + public static let shared = TestingSystem() + + public var isSwiftTestingRunning: Bool { + (self as? SwiftTestingSystem)?.isRunning ?? false + } + + public var isSwiftTestingCompletionAttached: Bool { + (self as? SwiftTestingSystem)?.isTestCompletionAttached ?? false + } + + var environment: TestingSystemEnvironment? { + if let swiftTestingFramework = self as? SwiftTestingSystem { + return swiftTestingFramework.environment + } else { + return nil + } + } + + public func add( + _ name: String, + attachments: [SnapshotAttachment], + fileID: StaticString, + filePath: StaticString, + line: UInt, + column: UInt + ) { + if let swiftTestingSystem = self as? SwiftTestingSystem, swiftTestingSystem.isRunning { + swiftTestingSystem.add( + name, + attachments: attachments, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + } else { + #if canImport(XCTest) && (os(iOS) || os(tvOS) || os(macOS) || os(visionOS) || os(watchOS)) + performOnMainThread { + XCTContext.runActivity(named: name) { activity in + for attachment in attachments { + activity.add( + XCTAttachment( + uniformTypeIdentifier: attachment.uniformTypeIdentifier, + name: attachment.name, + payload: attachment.payload + ) + ) + } + } + } + #endif + } + } + + public func record( + message: String, + fileID: StaticString, + filePath: StaticString, + line: UInt, + column: UInt + ) throws { + if let swiftTestingSystem = self as? SwiftTestingSystem, swiftTestingSystem.isRunning { + swiftTestingSystem.record( + message: message, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + } else { + #if canImport(XCTest) + XCTFail(message, file: filePath, line: line) + #else + throw TestingFailure( + message: message, + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + #endif + } + } +} + +#if !canImport(XCTest) +public struct TestingFailure: Error { + public let message: String + public let fileID: StaticString + public let filePath: StaticString + public let line: UInt + public let column: UInt +} +#endif diff --git a/Tests/Deprecated/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift b/Tests/Deprecated/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift new file mode 100644 index 000000000..d3f976c72 --- /dev/null +++ b/Tests/Deprecated/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift @@ -0,0 +1,340 @@ +#if !os(visionOS) +#if canImport(Testing) +import Testing +import Foundation +import InlineSnapshotTesting +import SnapshotTesting + +extension BaseSuite { + @Suite + struct AssertInlineSnapshotTests { + @Test func inlineSnapshot() { + assertInlineSnapshot(of: ["Hello", "World"], as: .dump) { + """ + ▿ 2 elements + - "Hello" + - "World" + + """ + } + } + + @Test(.snapshots(record: .missing)) func inlineSnapshotFailure() { + withKnownIssue { + assertInlineSnapshot(of: ["Hello", "World"], as: .dump) { + """ + ▿ 2 elements + - "Hello" + + """ + } + } matching: { issue in + issue.comments.first?.rawValue ?? "" == """ + Snapshot did not match. Difference: … + + @@ −1,3 +1,4 @@ +  ▿ 2 elements +   - "Hello" + + - "World" +   + """ + } + } + + @Test func inlineSnapshot_NamedTrailingClosure() { + assertInlineSnapshot( + of: ["Hello", "World"], + as: .dump, + matches: { + """ + ▿ 2 elements + - "Hello" + - "World" + + """ + } + ) + } + + @Test func inlineSnapshot_Escaping() { + assertInlineSnapshot(of: "Hello\"\"\"#, world", as: .lines) { + ##""" + Hello"""#, world + """## + } + } + + @Test func customInlineSnapshot() { + assertCustomInlineSnapshot { + "Hello" + } is: { + """ + - "Hello" + + """ + } + } + + @Test func customInlineSnapshot_Multiline() { + assertCustomInlineSnapshot { + """ + "Hello" + "World" + """ + } is: { + #""" + - "\"Hello\"\n\"World\"" + + """# + } + } + + @Test func customInlineSnapshot_SingleTrailingClosure() { + assertCustomInlineSnapshot(of: { "Hello" }) { + """ + - "Hello" + + """ + } + } + + @Test func customInlineSnapshot_MultilineSingleTrailingClosure() { + assertCustomInlineSnapshot( + of: { "Hello" } + ) { + """ + - "Hello" + + """ + } + } + + @Test func customInlineSnapshot_NoTrailingClosure() { + assertCustomInlineSnapshot( + of: { "Hello" }, + is: { + """ + - "Hello" + + """ + } + ) + } + + @Test func argumentlessInlineSnapshot() { + func assertArgumentlessInlineSnapshot( + expected: (() -> String)? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) { + assertInlineSnapshot( + of: "Hello", + as: .dump, + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + assertArgumentlessInlineSnapshot { + """ + - "Hello" + + """ + } + } + + @Test func multipleInlineSnapshots() { + func assertResponse( + of url: () -> String, + head: (() -> String)? = nil, + body: (() -> String)? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) { + assertInlineSnapshot( + of: """ + HTTP/1.1 200 OK + Content-Type: text/html; charset=utf-8 + """, + as: .lines, + message: "Head did not match", + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "head", + trailingClosureOffset: 1 + ), + matches: head, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + assertInlineSnapshot( + of: """ + + + + + Point-Free + + + +

What's the point?

+ + + """, + as: .lines, + message: "Body did not match", + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "body", + trailingClosureOffset: 2 + ), + matches: body, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + assertResponse { + """ + https://www.pointfree.co/ + """ + } head: { + """ + HTTP/1.1 200 OK + Content-Type: text/html; charset=utf-8 + """ + } body: { + """ + + + + + Point-Free + + + +

What's the point?

+ + + """ + } + } + + @Test func asyncThrowing() async throws { + func assertAsyncThrowingInlineSnapshot( + of value: () -> String, + is expected: (() -> String)? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) async throws { + assertInlineSnapshot( + of: value(), + as: .dump, + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + try await assertAsyncThrowingInlineSnapshot { + "Hello" + } is: { + """ + - "Hello" + + """ + } + } + + @Test func nestedInClosureFunction() { + func withDependencies(operation: () -> Void) { + operation() + } + + withDependencies { + assertInlineSnapshot(of: "Hello", as: .dump) { + """ + - "Hello" + + """ + } + } + } + + @Test func carriageReturnInlineSnapshot() { + assertInlineSnapshot(of: "This is a line\r\nAnd this is a line\r\n", as: .lines) { + """ + This is a line\r + And this is a line\r + + """ + } + } + + @Test func carriageReturnRawInlineSnapshot() { + assertInlineSnapshot(of: "\"\"\"#This is a line\r\nAnd this is a line\r\n", as: .lines) { + ##""" + """#This is a line\##r + And this is a line\##r + + """## + } + } + } +} + +private func assertCustomInlineSnapshot( + of value: () -> String, + is expected: (() -> String)? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) { + assertInlineSnapshot( + of: value(), + as: .dump, + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) +} + +#endif +#endif diff --git a/Tests/Deprecated/InlineSnapshotTestingTests/CustomDumpTests.swift b/Tests/Deprecated/InlineSnapshotTestingTests/CustomDumpTests.swift new file mode 100644 index 000000000..817c933c1 --- /dev/null +++ b/Tests/Deprecated/InlineSnapshotTestingTests/CustomDumpTests.swift @@ -0,0 +1,25 @@ +#if !os(visionOS) +#if canImport(Testing) +import Testing +import InlineSnapshotTesting +import SnapshotTestingCustomDump + +extension BaseSuite { + struct CustomDumpSnapshotTests { + @Test func basics() { + struct User { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + assertInlineSnapshot(of: user, as: .customDump) { + """ + BaseSuite.CustomDumpSnapshotTests.User( + id: 1, + name: "Blobby", + bio: "Blobbed around the world." + ) + """ + } + } + } +} +#endif +#endif diff --git a/Tests/Deprecated/InlineSnapshotTestingTests/InlineSnapshotTestingTests.swift b/Tests/Deprecated/InlineSnapshotTestingTests/InlineSnapshotTestingTests.swift new file mode 100644 index 000000000..d0a23a350 --- /dev/null +++ b/Tests/Deprecated/InlineSnapshotTestingTests/InlineSnapshotTestingTests.swift @@ -0,0 +1,377 @@ +#if !os(visionOS) +import Foundation +@_spi(Internals) import InlineSnapshotTesting +import SnapshotTesting +import XCTest + +@available(*, deprecated) +final class InlineSnapshotTestingTests: BaseTestCase { + func testInlineSnapshot() { + assertInlineSnapshot(of: ["Hello", "World"], as: .dump) { + """ + ▿ 2 elements + - "Hello" + - "World" + + """ + } + } + + func testInlineSnapshot_NamedTrailingClosure() { + assertInlineSnapshot( + of: ["Hello", "World"], + as: .dump, + matches: { + """ + ▿ 2 elements + - "Hello" + - "World" + + """ + } + ) + } + + func testInlineSnapshot_Escaping() { + assertInlineSnapshot(of: "Hello\"\"\"#, world", as: .lines) { + ##""" + Hello"""#, world + """## + } + } + + func testCustomInlineSnapshot() { + assertCustomInlineSnapshot { + "Hello" + } is: { + """ + - "Hello" + + """ + } + } + + func testCustomInlineSnapshot_Multiline() { + assertCustomInlineSnapshot { + """ + "Hello" + "World" + """ + } is: { + #""" + - "\"Hello\"\n\"World\"" + + """# + } + } + + func testCustomInlineSnapshot_SingleTrailingClosure() { + assertCustomInlineSnapshot(of: { "Hello" }) { + """ + - "Hello" + + """ + } + } + + func testCustomInlineSnapshot_MultilineSingleTrailingClosure() { + assertCustomInlineSnapshot( + of: { "Hello" } + ) { + """ + - "Hello" + + """ + } + } + + func testCustomInlineSnapshot_NoTrailingClosure() { + assertCustomInlineSnapshot( + of: { "Hello" }, + is: { + """ + - "Hello" + + """ + } + ) + } + + func testArgumentlessInlineSnapshot() { + func assertArgumentlessInlineSnapshot( + expected: (() -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) { + assertInlineSnapshot( + of: "Hello", + as: .dump, + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + assertArgumentlessInlineSnapshot { + """ + - "Hello" + + """ + } + } + + func testMultipleInlineSnapshots() { + func assertResponse( + of url: () -> String, + head: (() -> String)? = nil, + body: (() -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) { + assertInlineSnapshot( + of: """ + HTTP/1.1 200 OK + Content-Type: text/html; charset=utf-8 + """, + as: .lines, + message: "Head did not match", + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "head", + trailingClosureOffset: 1 + ), + matches: head, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + assertInlineSnapshot( + of: """ + + + + + Point-Free + + + +

What's the point?

+ + + """, + as: .lines, + message: "Body did not match", + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "body", + trailingClosureOffset: 2 + ), + matches: body, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + assertResponse { + """ + https://www.pointfree.co/ + """ + } head: { + """ + HTTP/1.1 200 OK + Content-Type: text/html; charset=utf-8 + """ + } body: { + """ + + + + + Point-Free + + + +

What's the point?

+ + + """ + } + } + + func testAsyncThrowing() async throws { + func assertAsyncThrowingInlineSnapshot( + of value: () -> String, + is expected: (() -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) async throws { + assertInlineSnapshot( + of: value(), + as: .dump, + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + try await assertAsyncThrowingInlineSnapshot { + "Hello" + } is: { + """ + - "Hello" + + """ + } + } + + func testNestedInClosureFunction() { + func withDependencies(operation: () -> Void) { + operation() + } + + withDependencies { + assertInlineSnapshot(of: "Hello", as: .dump) { + """ + - "Hello" + + """ + } + } + } + + func testCarriageReturnInlineSnapshot() { + assertInlineSnapshot(of: "This is a line\r\nAnd this is a line\r\n", as: .lines) { + """ + This is a line\r + And this is a line\r + + """ + } + } + + func testCarriageReturnRawInlineSnapshot() { + assertInlineSnapshot(of: "\"\"\"#This is a line\r\nAnd this is a line\r\n", as: .lines) { + ##""" + """#This is a line\##r + And this is a line\##r + + """## + } + } + + #if canImport(Darwin) + func testRecordFailed_IncorrectExpectation() throws { + let initialInlineSnapshotState = inlineSnapshotState + defer { inlineSnapshotState = initialInlineSnapshotState } + + XCTExpectFailure { + withSnapshotTesting(record: .failed) { + assertInlineSnapshot(of: 42, as: .json) { + """ + 4 + """ + } + } + } issueMatcher: { + $0.compactDescription == """ + failed - Snapshot did not match. Difference: … + + @@ −1,1 +1,1 @@ + −4 + +42 + + A new snapshot was automatically recorded. + """ + } + + XCTAssertEqual(inlineSnapshotState.count, 1) + XCTAssertEqual( + String(describing: inlineSnapshotState.keys.first!.path) + .hasSuffix("InlineSnapshotTestingTests.swift"), + true + ) + } + #endif + + #if canImport(Darwin) + func testRecordFailed_MissingExpectation() throws { + let initialInlineSnapshotState = inlineSnapshotState + defer { inlineSnapshotState = initialInlineSnapshotState } + + XCTExpectFailure { + withSnapshotTesting(record: .failed) { + assertInlineSnapshot(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription == """ + failed - Automatically recorded a new snapshot. Difference: … + + @@ −1,1 +1,1 @@ + − + +42 + + Re-run "testRecordFailed_MissingExpectation()" to assert against the newly-recorded snapshot. + """ + } + + XCTAssertEqual(inlineSnapshotState.count, 1) + XCTAssertEqual( + String(describing: inlineSnapshotState.keys.first!.path) + .hasSuffix("InlineSnapshotTestingTests.swift"), + true + ) + } + #endif +} + +private func assertCustomInlineSnapshot( + of value: () -> String, + is expected: (() -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) { + assertInlineSnapshot( + of: value(), + as: .dump, + syntaxDescriptor: InlineSnapshotSyntaxDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) +} +#endif diff --git a/Tests/Deprecated/InlineSnapshotTestingTests/Internal/BaseSuite.swift b/Tests/Deprecated/InlineSnapshotTestingTests/Internal/BaseSuite.swift new file mode 100644 index 000000000..ec8550126 --- /dev/null +++ b/Tests/Deprecated/InlineSnapshotTestingTests/Internal/BaseSuite.swift @@ -0,0 +1,10 @@ +#if !os(visionOS) +#if canImport(Testing) +import Testing +import SnapshotTesting + +@Suite(.snapshots(record: .failed, diffTool: .ksdiff)) +struct BaseSuite { +} +#endif +#endif diff --git a/Tests/Deprecated/InlineSnapshotTestingTests/Internal/BaseTestCase.swift b/Tests/Deprecated/InlineSnapshotTestingTests/Internal/BaseTestCase.swift new file mode 100644 index 000000000..118a53f35 --- /dev/null +++ b/Tests/Deprecated/InlineSnapshotTestingTests/Internal/BaseTestCase.swift @@ -0,0 +1,16 @@ +#if !os(visionOS) +import SnapshotTesting +import XCTest + +@available(*, deprecated) +class BaseTestCase: XCTestCase { + override func invokeTest() { + withSnapshotTesting( + record: .failed, + diffTool: .ksdiff + ) { + super.invokeTest() + } + } +} +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/AssertSnapshotSwiftTests.swift b/Tests/Deprecated/SnapshotTestingTests/AssertSnapshotSwiftTests.swift new file mode 100644 index 000000000..49d82fa80 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/AssertSnapshotSwiftTests.swift @@ -0,0 +1,26 @@ +#if !os(visionOS) +#if canImport(Testing) +import Testing +import Foundation +import SnapshotTesting + +extension BaseSuite { + struct AssertSnapshotTests { + @Test(.snapshots(record: .missing)) func dump() { + struct User { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + assertSnapshot(of: user, as: .dump) + } + } + + @MainActor + struct MainActorTests { + @Test func dump() { + struct User { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + assertSnapshot(of: user, as: .dump) + } + } +} +#endif +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/DeprecationTests.swift b/Tests/Deprecated/SnapshotTestingTests/DeprecationTests.swift new file mode 100644 index 000000000..70eef650a --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/DeprecationTests.swift @@ -0,0 +1,16 @@ +#if !os(visionOS) +import SnapshotTesting +import XCTest + +@available(*, deprecated) +final class DeprecationTests: XCTestCase { + @available(*, deprecated) + func testIsRecordingProxy() { + SnapshotTesting.record = true + XCTAssertEqual(isRecording, true) + + SnapshotTesting.record = false + XCTAssertEqual(isRecording, false) + } +} +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/Internal/BaseSuite.swift b/Tests/Deprecated/SnapshotTestingTests/Internal/BaseSuite.swift new file mode 100644 index 000000000..ec8550126 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/Internal/BaseSuite.swift @@ -0,0 +1,10 @@ +#if !os(visionOS) +#if canImport(Testing) +import Testing +import SnapshotTesting + +@Suite(.snapshots(record: .failed, diffTool: .ksdiff)) +struct BaseSuite { +} +#endif +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/Internal/BaseTestCase.swift b/Tests/Deprecated/SnapshotTestingTests/Internal/BaseTestCase.swift new file mode 100644 index 000000000..118a53f35 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/Internal/BaseTestCase.swift @@ -0,0 +1,16 @@ +#if !os(visionOS) +import SnapshotTesting +import XCTest + +@available(*, deprecated) +class BaseTestCase: XCTestCase { + override func invokeTest() { + withSnapshotTesting( + record: .failed, + diffTool: .ksdiff + ) { + super.invokeTest() + } + } +} +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/Internal/TestHelpers.swift b/Tests/Deprecated/SnapshotTestingTests/Internal/TestHelpers.swift new file mode 100644 index 000000000..33517d9ef --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/Internal/TestHelpers.swift @@ -0,0 +1,114 @@ +#if !os(visionOS) +import XCTest + +@testable import SnapshotTesting + +#if os(iOS) +let platform = "ios" +#elseif os(tvOS) +let platform = "tvos" +#elseif os(macOS) +let platform = "macos" +extension NSTextField { + var text: String { + get { self.stringValue } + set { self.stringValue = newValue } + } +} +#endif + +#if os(macOS) || os(iOS) || os(tvOS) +extension CGPath { + /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: + static var heart: CGPath { + let scale: CGFloat = 30.0 + let path = CGMutablePath() + + path.move(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) + path.addLine(to: CGPoint(x: 0.0 * scale, y: 2.0 * scale)) + path.addQuadCurve( + to: CGPoint(x: 1.0 * scale, y: 3.0 * scale), + control: CGPoint(x: 0.125 * scale, y: 2.875 * scale) + ) + path.addQuadCurve( + to: CGPoint(x: 2.0 * scale, y: 2.0 * scale), + control: CGPoint(x: 1.875 * scale, y: 2.875 * scale) + ) + path.addCurve( + to: CGPoint(x: 3.0 * scale, y: 1.0 * scale), + control1: CGPoint(x: 2.5 * scale, y: 2.0 * scale), + control2: CGPoint(x: 3.0 * scale, y: 1.5 * scale) + ) + path.addCurve( + to: CGPoint(x: 2.0 * scale, y: 0.0 * scale), + control1: CGPoint(x: 3.0 * scale, y: 0.5 * scale), + control2: CGPoint(x: 2.5 * scale, y: 0.0 * scale) + ) + path.addLine(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) + path.closeSubpath() + + path.addEllipse( + in: CGRect( + origin: CGPoint(x: 2.0 * scale, y: 2.0 * scale), + size: CGSize(width: scale, height: scale) + ) + ) + + return path + } +} +#endif + +#if os(iOS) || os(tvOS) +extension UIBezierPath { + /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: + static var heart: UIBezierPath { + UIBezierPath(cgPath: .heart) + } +} +#endif + +#if os(macOS) +extension NSBezierPath { + /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: + static var heart: NSBezierPath { + let scale: CGFloat = 30.0 + let path = NSBezierPath() + + path.move(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) + path.line(to: CGPoint(x: 0.0 * scale, y: 2.0 * scale)) + path.curve( + to: CGPoint(x: 1.0 * scale, y: 3.0 * scale), + controlPoint1: CGPoint(x: 0.0 * scale, y: 2.5 * scale), + controlPoint2: CGPoint(x: 0.5 * scale, y: 3.0 * scale) + ) + path.curve( + to: CGPoint(x: 2.0 * scale, y: 2.0 * scale), + controlPoint1: CGPoint(x: 1.5 * scale, y: 3.0 * scale), + controlPoint2: CGPoint(x: 2.0 * scale, y: 2.5 * scale) + ) + path.curve( + to: CGPoint(x: 3.0 * scale, y: 1.0 * scale), + controlPoint1: CGPoint(x: 2.5 * scale, y: 2.0 * scale), + controlPoint2: CGPoint(x: 3.0 * scale, y: 1.5 * scale) + ) + path.curve( + to: CGPoint(x: 2.0 * scale, y: 0.0 * scale), + controlPoint1: CGPoint(x: 3.0 * scale, y: 0.5 * scale), + controlPoint2: CGPoint(x: 2.5 * scale, y: 0.0 * scale) + ) + path.line(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) + path.close() + + path.appendOval( + in: CGRect( + origin: CGPoint(x: 2.0 * scale, y: 2.0 * scale), + size: CGSize(width: scale, height: scale) + ) + ) + + return path + } +} +#endif +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/RecordTests.swift b/Tests/Deprecated/SnapshotTestingTests/RecordTests.swift new file mode 100644 index 000000000..d2360841f --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/RecordTests.swift @@ -0,0 +1,216 @@ +#if !os(visionOS) +import SnapshotTesting +import XCTest + +@available(*, deprecated) +class RecordTests: BaseTestCase { + var snapshotURL: URL! + + override func setUp() { + super.setUp() + + let testName = String( + self.name + .split(separator: " ") + .flatMap { String($0).split(separator: ".") } + .last! + ) + .prefix(while: { $0 != "]" }) + let fileURL = URL(fileURLWithPath: #file, isDirectory: false) + let testClassName = fileURL.deletingPathExtension().lastPathComponent + let testDirectory = + fileURL + .deletingLastPathComponent() + .appendingPathComponent("__Snapshots__") + .appendingPathComponent(testClassName) + snapshotURL = + testDirectory + .appendingPathComponent("\(testName).1.json") + try? FileManager.default + .removeItem(at: snapshotURL.deletingLastPathComponent()) + try? FileManager.default + .createDirectory(at: testDirectory, withIntermediateDirectories: true) + } + + override func tearDown() { + super.tearDown() + try? FileManager.default + .removeItem(at: snapshotURL.deletingLastPathComponent()) + } + + #if canImport(Darwin) + func testRecordNever() { + XCTExpectFailure { + withSnapshotTesting(record: .never) { + assertSnapshot(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription == """ + failed - No reference was found on disk. New snapshot was not recorded because recording is disabled + """ + } + + XCTAssertEqual( + FileManager.default.fileExists(atPath: snapshotURL.path), + false + ) + } + #endif + + #if canImport(Darwin) + func testRecordMissing() { + XCTExpectFailure { + withSnapshotTesting(record: .missing) { + assertSnapshot(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription.hasPrefix( + """ + failed - No reference was found on disk. Automatically recorded snapshot: … + """ + ) + } + + try XCTAssertEqual( + String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), + "42" + ) + } + #endif + + #if canImport(Darwin) + func testRecordMissing_ExistingFile() throws { + try Data("999".utf8).write(to: snapshotURL) + + XCTExpectFailure { + withSnapshotTesting(record: .missing) { + assertSnapshot(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription.hasPrefix( + """ + failed - Snapshot does not match reference. + """ + ) + } + + try XCTAssertEqual( + String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), + "999" + ) + } + #endif + + #if canImport(Darwin) + func testRecordAll_Fresh() throws { + XCTExpectFailure { + withSnapshotTesting(record: .all) { + assertSnapshot(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription.hasPrefix( + """ + failed - Record mode is on. Automatically recorded snapshot: … + """ + ) + } + + try XCTAssertEqual( + String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), + "42" + ) + } + #endif + + #if canImport(Darwin) + func testRecordAll_Overwrite() throws { + try Data("999".utf8).write(to: snapshotURL) + + XCTExpectFailure { + withSnapshotTesting(record: .all) { + assertSnapshot(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription.hasPrefix( + """ + failed - Record mode is on. Automatically recorded snapshot: … + """ + ) + } + + try XCTAssertEqual( + String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), + "42" + ) + } + #endif + + #if canImport(Darwin) + func testRecordFailed_WhenFailure() throws { + try Data("999".utf8).write(to: snapshotURL) + + XCTExpectFailure { + withSnapshotTesting(record: .failed) { + assertSnapshot(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription.hasPrefix( + """ + failed - Snapshot does not match reference. A new snapshot was automatically recorded. + """ + ) + } + + try XCTAssertEqual( + String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), + "42" + ) + } + #endif + + func testRecordFailed_NoFailure() throws { + #if os(Android) + throw XCTSkip("cannot save next to file on Android") + #endif + try Data("42".utf8).write(to: snapshotURL) + let modifiedDate = + try FileManager.default + .attributesOfItem(atPath: snapshotURL.path)[FileAttributeKey.modificationDate] as! Date + + withSnapshotTesting(record: .failed) { + assertSnapshot(of: 42, as: .json) + } + + try XCTAssertEqual( + String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), + "42" + ) + XCTAssertEqual( + try FileManager.default + .attributesOfItem(atPath: snapshotURL.path)[FileAttributeKey.modificationDate] as! Date, + modifiedDate + ) + } + + #if canImport(Darwin) + func testRecordFailed_MissingFile() throws { + XCTExpectFailure { + withSnapshotTesting(record: .failed) { + assertSnapshot(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription.hasPrefix( + """ + failed - No reference was found on disk. Automatically recorded snapshot: … + """ + ) + } + + try XCTAssertEqual( + String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), + "42" + ) + } + #endif +} +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/SnapshotTestingTests.swift b/Tests/Deprecated/SnapshotTestingTests/SnapshotTestingTests.swift new file mode 100644 index 000000000..cb4b59df4 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/SnapshotTestingTests.swift @@ -0,0 +1,1677 @@ +#if !os(visionOS) +import Foundation +import XCTest + +@testable import SnapshotTesting + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif +#if canImport(SceneKit) +import SceneKit +#endif +#if canImport(SpriteKit) +import SpriteKit +import SwiftUI +#endif +#if canImport(WebKit) +@preconcurrency import WebKit +#endif +#if canImport(UIKit) && !os(watchOS) +import UIKit.UIView +#endif + +@available(*, deprecated) +final class SnapshotTestingTests: BaseTestCase { + func testAny() { + struct User { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + assertSnapshot(of: user, as: .dump) + } + + func testRecursion() { + withSnapshotTesting { + class Father { + var child: Child? + init(_ child: Child? = nil) { self.child = child } + } + class Child { + let father: Father + init(_ father: Father) { + self.father = father + father.child = self + } + } + let father = Father() + let child = Child(father) + assertSnapshot(of: father, as: .dump) + assertSnapshot(of: child, as: .dump) + } + } + + @available(macOS 10.13, tvOS 11.0, *) + func testAnyAsJson() throws { + struct User: Encodable { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + + let data = try JSONEncoder().encode(user) + let any = try JSONSerialization.jsonObject(with: data, options: []) + + assertSnapshot(of: any, as: .json) + } + + func testAnySnapshotStringConvertible() { + assertSnapshot(of: "a" as Character, as: .dump, named: "character") + assertSnapshot(of: Data("Hello, world!".utf8), as: .dump, named: "data") + assertSnapshot(of: Date(timeIntervalSinceReferenceDate: 0), as: .dump, named: "date") + assertSnapshot(of: NSObject(), as: .dump, named: "nsobject") + assertSnapshot(of: "Hello, world!", as: .dump, named: "string") + assertSnapshot(of: "Hello, world!".dropLast(8), as: .dump, named: "substring") + assertSnapshot(of: URL(string: "https://www.pointfree.co")!, as: .dump, named: "url") + } + + func testAutolayout() { + #if os(iOS) + let vc = UIViewController() + vc.view.translatesAutoresizingMaskIntoConstraints = false + let subview = UIView() + subview.translatesAutoresizingMaskIntoConstraints = false + vc.view.addSubview(subview) + NSLayoutConstraint.activate([ + subview.topAnchor.constraint(equalTo: vc.view.topAnchor), + subview.bottomAnchor.constraint(equalTo: vc.view.bottomAnchor), + subview.leftAnchor.constraint(equalTo: vc.view.leftAnchor), + subview.rightAnchor.constraint(equalTo: vc.view.rightAnchor), + ]) + assertSnapshot(of: vc, as: .image) + #endif + } + + func testDeterministicDictionaryAndSetSnapshots() { + struct Person: Hashable { let name: String } + struct DictionarySetContainer { let dict: [String: Int], set: Set } + let set = DictionarySetContainer( + dict: ["c": 3, "a": 1, "b": 2], + set: [.init(name: "Brandon"), .init(name: "Stephen")] + ) + assertSnapshot(of: set, as: .dump) + } + + func testCaseIterable() { + enum Direction: String, CaseIterable { + case up, down, left, right + var rotatedLeft: Direction { + switch self { + case .up: return .left + case .down: return .right + case .left: return .down + case .right: return .up + } + } + } + + assertSnapshot( + of: { $0.rotatedLeft }, + as: Snapshotting.func(into: .description) + ) + } + + func testCGPath() { + #if os(iOS) || os(tvOS) || os(macOS) + let path = CGPath.heart + + let osName: String + #if os(iOS) + osName = "iOS" + #elseif os(tvOS) + osName = "tvOS" + #elseif os(macOS) + osName = "macOS" + #endif + + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot(of: path, as: .image, named: osName) + } + + if #available(iOS 11.0, OSX 10.13, tvOS 11.0, *) { + assertSnapshot(of: path, as: .elementsDescription, named: osName) + } + #endif + } + + func testData() { + let data = Data([0xDE, 0xAD, 0xBE, 0xEF]) + + assertSnapshot(of: data, as: .data) + } + + func testEncodable() { + struct User: Encodable { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + + if #available(iOS 11.0, macOS 10.13, tvOS 11.0, *) { + assertSnapshot(of: user, as: .json) + } + assertSnapshot(of: user, as: .plist) + } + + func testMixedViews() { + // #if os(iOS) || os(macOS) + // // NB: CircleCI crashes while trying to instantiate SKView. + // if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + // let webView = WKWebView(frame: .init(x: 0, y: 0, width: 50, height: 50)) + // webView.loadHTMLString("🌎", baseURL: nil) + // + // let skView = SKView(frame: .init(x: 50, y: 0, width: 50, height: 50)) + // let scene = SKScene(size: .init(width: 50, height: 50)) + // let node = SKShapeNode(circleOfRadius: 15) + // node.fillColor = .red + // node.position = .init(x: 25, y: 25) + // scene.addChild(node) + // skView.presentScene(scene) + // + // let view = View(frame: .init(x: 0, y: 0, width: 100, height: 50)) + // view.addSubview(webView) + // view.addSubview(skView) + // + // assertSnapshot(of: view, as: .image, named: platform) + // } + // #endif + } + + func testMultipleSnapshots() { + assertSnapshot(of: [1], as: .dump) + assertSnapshot(of: [1, 2], as: .dump) + } + + func testNamedAssertion() { + struct User { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + assertSnapshot(of: user, as: .dump, named: "named") + } + + func testNSBezierPath() { + #if os(macOS) + let path = NSBezierPath.heart + + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot(of: path, as: .image, named: "macOS") + } + + assertSnapshot(of: path, as: .elementsDescription, named: "macOS") + #endif + } + + func testNSView() { + #if os(macOS) + let button = NSButton() + button.bezelStyle = .rounded + button.title = "Push Me" + button.sizeToFit() + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot(of: button, as: .image) + assertSnapshot(of: button, as: .recursiveDescription) + } + #endif + } + + func testNSViewWithLayer() { + #if os(macOS) + let view = NSView() + view.frame = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0) + view.wantsLayer = true + view.layer?.backgroundColor = NSColor.green.cgColor + view.layer?.cornerRadius = 5 + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot(of: view, as: .image) + assertSnapshot(of: view, as: .recursiveDescription) + } + #endif + } + + func testPrecision() { + #if os(iOS) || os(macOS) || os(tvOS) + #if os(iOS) || os(tvOS) + let label = UILabel() + #if os(iOS) + label.frame = CGRect(origin: .zero, size: CGSize(width: 43.5, height: 20.5)) + #elseif os(tvOS) + label.frame = CGRect(origin: .zero, size: CGSize(width: 98, height: 46)) + #endif + label.backgroundColor = .white + #elseif os(macOS) + let label = NSTextField() + label.frame = CGRect(origin: .zero, size: CGSize(width: 37, height: 16)) + label.backgroundColor = .white + label.isBezeled = false + label.isEditable = false + #endif + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + label.text = "Hello." + assertSnapshot(of: label, as: .image(precision: 0.9), named: platform) + label.text = "Hello" + assertSnapshot(of: label, as: .image(precision: 0.9), named: platform) + } + #endif + } + + func testImagePrecision() throws { + #if os(iOS) || os(tvOS) || os(macOS) + let imageURL = URL(fileURLWithPath: String(#file), isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/testImagePrecision.reference.png") + #if os(iOS) || os(tvOS) + let image = try XCTUnwrap(UIImage(contentsOfFile: imageURL.path)) + #elseif os(macOS) + let image = try XCTUnwrap(NSImage(byReferencing: imageURL)) + #endif + + assertSnapshot(of: image, as: .image(precision: 0.995), named: "exact") + if #available(iOS 11.0, tvOS 11.0, macOS 10.13, *) { + assertSnapshot(of: image, as: .image(perceptualPrecision: 0.98), named: "perceptual") + } + #endif + } + + func testSCNView() { + // #if os(iOS) || os(macOS) || os(tvOS) + // // NB: CircleCI crashes while trying to instantiate SCNView. + // if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + // let scene = SCNScene() + // + // let sphereGeometry = SCNSphere(radius: 3) + // sphereGeometry.segmentCount = 200 + // let sphereNode = SCNNode(geometry: sphereGeometry) + // sphereNode.position = SCNVector3Zero + // scene.rootNode.addChildNode(sphereNode) + // + // sphereGeometry.firstMaterial?.diffuse.contents = URL(fileURLWithPath: String(#file), isDirectory: false) + // .deletingLastPathComponent() + // .appendingPathComponent("__Fixtures__/earth.png") + // + // let cameraNode = SCNNode() + // cameraNode.camera = SCNCamera() + // cameraNode.position = SCNVector3Make(0, 0, 8) + // scene.rootNode.addChildNode(cameraNode) + // + // let omniLight = SCNLight() + // omniLight.type = .omni + // let omniLightNode = SCNNode() + // omniLightNode.light = omniLight + // omniLightNode.position = SCNVector3Make(10, 10, 10) + // scene.rootNode.addChildNode(omniLightNode) + // + // assertSnapshot( + // of: scene, + // as: .image(size: .init(width: 500, height: 500)), + // named: platform + // ) + // } + // #endif + } + + func testSKView() { + // #if os(iOS) || os(macOS) || os(tvOS) + // // NB: CircleCI crashes while trying to instantiate SKView. + // if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + // let scene = SKScene(size: .init(width: 50, height: 50)) + // let node = SKShapeNode(circleOfRadius: 15) + // node.fillColor = .red + // node.position = .init(x: 25, y: 25) + // scene.addChild(node) + // + // assertSnapshot( + // of: scene, + // as: .image(size: .init(width: 50, height: 50)), + // named: platform + // ) + // } + // #endif + } + + func testTableViewController() { + #if os(iOS) + class TableViewController: UITableViewController { + override func viewDidLoad() { + super.viewDidLoad() + self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") + } + override func tableView( + _ tableView: UITableView, + numberOfRowsInSection section: Int + ) + -> Int + { + 10 + } + override func tableView( + _ tableView: UITableView, + cellForRowAt indexPath: IndexPath + ) + -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) + cell.textLabel?.text = "\(indexPath.row)" + return cell + } + } + let tableViewController = TableViewController() + assertSnapshot(of: tableViewController, as: .image(on: .iPhoneSe)) + #endif + } + + func testAssertMultipleSnapshot() { + #if os(iOS) + class TableViewController: UITableViewController { + override func viewDidLoad() { + super.viewDidLoad() + self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") + } + override func tableView( + _ tableView: UITableView, + numberOfRowsInSection section: Int + ) + -> Int + { + 10 + } + override func tableView( + _ tableView: UITableView, + cellForRowAt indexPath: IndexPath + ) + -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) + cell.textLabel?.text = "\(indexPath.row)" + return cell + } + } + let tableViewController = TableViewController() + assertSnapshots( + of: tableViewController, + as: ["iPhoneSE-image": .image(on: .iPhoneSe), "iPad-image": .image(on: .iPadMini)] + ) + assertSnapshots( + of: tableViewController, + as: [.image(on: .iPhoneX), .image(on: .iPhoneXsMax)] + ) + #endif + } + + func testTraits() { + #if os(iOS) || os(tvOS) + if #available(iOS 11.0, tvOS 11.0, *) { + class MyViewController: UIViewController { + let topLabel = UILabel() + let leadingLabel = UILabel() + let trailingLabel = UILabel() + let bottomLabel = UILabel() + + override func viewDidLoad() { + super.viewDidLoad() + + self.navigationItem.leftBarButtonItem = .init( + barButtonSystemItem: .add, + target: nil, + action: nil + ) + + self.view.backgroundColor = .white + + self.topLabel.text = "What's" + self.leadingLabel.text = "the" + self.trailingLabel.text = "point" + self.bottomLabel.text = "?" + + self.topLabel.translatesAutoresizingMaskIntoConstraints = false + self.leadingLabel.translatesAutoresizingMaskIntoConstraints = false + self.trailingLabel.translatesAutoresizingMaskIntoConstraints = false + self.bottomLabel.translatesAutoresizingMaskIntoConstraints = false + + self.view.addSubview(self.topLabel) + self.view.addSubview(self.leadingLabel) + self.view.addSubview(self.trailingLabel) + self.view.addSubview(self.bottomLabel) + + NSLayoutConstraint.activate([ + self.topLabel.topAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.topAnchor + ), + self.topLabel.centerXAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + self.leadingLabel.leadingAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.leadingAnchor + ), + self.leadingLabel.trailingAnchor.constraint( + lessThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + // self.leadingLabel.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor), + self.leadingLabel.centerYAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerYAnchor + ), + self.trailingLabel.leadingAnchor.constraint( + greaterThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + self.trailingLabel.trailingAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.trailingAnchor + ), + self.trailingLabel.centerYAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerYAnchor + ), + self.bottomLabel.bottomAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.bottomAnchor + ), + self.bottomLabel.centerXAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + ]) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + self.topLabel.font = .preferredFont( + forTextStyle: .headline, + compatibleWith: self.traitCollection + ) + self.leadingLabel.font = .preferredFont( + forTextStyle: .body, + compatibleWith: self.traitCollection + ) + self.trailingLabel.font = .preferredFont( + forTextStyle: .body, + compatibleWith: self.traitCollection + ) + self.bottomLabel.font = .preferredFont( + forTextStyle: .subheadline, + compatibleWith: self.traitCollection + ) + self.view.setNeedsUpdateConstraints() + self.view.updateConstraintsIfNeeded() + } + } + + let viewController = MyViewController() + + #if os(iOS) + assertSnapshot(of: viewController, as: .image(on: .iPhoneSe), named: "iphone-se") + assertSnapshot(of: viewController, as: .image(on: .iPhone8), named: "iphone-8") + assertSnapshot(of: viewController, as: .image(on: .iPhone8Plus), named: "iphone-8-plus") + assertSnapshot(of: viewController, as: .image(on: .iPhoneX), named: "iphone-x") + assertSnapshot(of: viewController, as: .image(on: .iPhoneXr), named: "iphone-xr") + assertSnapshot(of: viewController, as: .image(on: .iPhoneXsMax), named: "iphone-xs-max") + assertSnapshot(of: viewController, as: .image(on: .iPadMini), named: "ipad-mini") + assertSnapshot(of: viewController, as: .image(on: .iPad9_7), named: "ipad-9-7") + assertSnapshot(of: viewController, as: .image(on: .iPad10_2), named: "ipad-10-2") + assertSnapshot(of: viewController, as: .image(on: .iPadPro10_5), named: "ipad-pro-10-5") + assertSnapshot(of: viewController, as: .image(on: .iPadPro11), named: "ipad-pro-11") + assertSnapshot(of: viewController, as: .image(on: .iPadPro12_9), named: "ipad-pro-12-9") + + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPhoneSe), + named: "iphone-se" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPhone8), + named: "iphone-8" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPhone8Plus), + named: "iphone-8-plus" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPhoneX), + named: "iphone-x" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPhoneXr), + named: "iphone-xr" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPhoneXsMax), + named: "iphone-xs-max" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPadMini), + named: "ipad-mini" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPad9_7), + named: "ipad-9-7" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPad10_2), + named: "ipad-10-2" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPadPro10_5), + named: "ipad-pro-10-5" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPadPro11), + named: "ipad-pro-11" + ) + assertSnapshot( + of: viewController, + as: .recursiveDescription(on: .iPadPro12_9), + named: "ipad-pro-12-9" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneSe(.portrait)), + named: "iphone-se" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhone8(.portrait)), + named: "iphone-8" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhone8Plus(.portrait)), + named: "iphone-8-plus" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneX(.portrait)), + named: "iphone-x" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneXr(.portrait)), + named: "iphone-xr" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneXsMax(.portrait)), + named: "iphone-xs-max" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.landscape)), + named: "ipad-mini" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.landscape)), + named: "ipad-9-7" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.landscape)), + named: "ipad-10-2" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.landscape)), + named: "ipad-pro-10-5" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.landscape)), + named: "ipad-pro-11" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.landscape)), + named: "ipad-pro-12-9" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.landscape(splitView: .oneThird))), + named: "ipad-mini-33-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.landscape(splitView: .oneHalf))), + named: "ipad-mini-50-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.landscape(splitView: .twoThirds))), + named: "ipad-mini-66-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.portrait(splitView: .oneThird))), + named: "ipad-mini-33-split-portrait" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.portrait(splitView: .twoThirds))), + named: "ipad-mini-66-split-portrait" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.landscape(splitView: .oneThird))), + named: "ipad-9-7-33-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.landscape(splitView: .oneHalf))), + named: "ipad-9-7-50-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.landscape(splitView: .twoThirds))), + named: "ipad-9-7-66-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.portrait(splitView: .oneThird))), + named: "ipad-9-7-33-split-portrait" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.portrait(splitView: .twoThirds))), + named: "ipad-9-7-66-split-portrait" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.landscape(splitView: .oneThird))), + named: "ipad-10-2-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.landscape(splitView: .oneHalf))), + named: "ipad-10-2-50-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.landscape(splitView: .twoThirds))), + named: "ipad-10-2-66-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.portrait(splitView: .oneThird))), + named: "ipad-10-2-33-split-portrait" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.portrait(splitView: .twoThirds))), + named: "ipad-10-2-66-split-portrait" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.landscape(splitView: .oneThird))), + named: "ipad-pro-10inch-33-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.landscape(splitView: .oneHalf))), + named: "ipad-pro-10inch-50-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.landscape(splitView: .twoThirds))), + named: "ipad-pro-10inch-66-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.portrait(splitView: .oneThird))), + named: "ipad-pro-10inch-33-split-portrait" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.portrait(splitView: .twoThirds))), + named: "ipad-pro-10inch-66-split-portrait" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.landscape(splitView: .oneThird))), + named: "ipad-pro-11inch-33-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.landscape(splitView: .oneHalf))), + named: "ipad-pro-11inch-50-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.landscape(splitView: .twoThirds))), + named: "ipad-pro-11inch-66-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.portrait(splitView: .oneThird))), + named: "ipad-pro-11inch-33-split-portrait" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.portrait(splitView: .twoThirds))), + named: "ipad-pro-11inch-66-split-portrait" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.landscape(splitView: .oneThird))), + named: "ipad-pro-12inch-33-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.landscape(splitView: .oneHalf))), + named: "ipad-pro-12inch-50-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.landscape(splitView: .twoThirds))), + named: "ipad-pro-12inch-66-split-landscape" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.portrait(splitView: .oneThird))), + named: "ipad-pro-12inch-33-split-portrait" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.portrait(splitView: .twoThirds))), + named: "ipad-pro-12inch-66-split-portrait" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneSe(.landscape)), + named: "iphone-se-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhone8(.landscape)), + named: "iphone-8-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhone8Plus(.landscape)), + named: "iphone-8-plus-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneX(.landscape)), + named: "iphone-x-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneXr(.landscape)), + named: "iphone-xr-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneXsMax(.landscape)), + named: "iphone-xs-max-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.portrait)), + named: "ipad-mini-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.portrait)), + named: "ipad-9-7-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.portrait)), + named: "ipad-10-2-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.portrait)), + named: "ipad-pro-10-5-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.portrait)), + named: "ipad-pro-11-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.portrait)), + named: "ipad-pro-12-9-alternative" + ) + + allContentSizes.forEach { name, contentSize in + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneSe, traits: .init(preferredContentSizeCategory: contentSize)), + named: "iphone-se-\(name)" + ) + } + #elseif os(tvOS) + assertSnapshot( + of: viewController, + as: .image(on: .tv), + named: "tv" + ) + assertSnapshot( + of: viewController, + as: .image(on: .tv4K), + named: "tv4k" + ) + #endif + } + #endif + } + + func testTraitsEmbeddedInTabNavigation() { + #if os(iOS) + if #available(iOS 11.0, *) { + class MyViewController: UIViewController { + let topLabel = UILabel() + let leadingLabel = UILabel() + let trailingLabel = UILabel() + let bottomLabel = UILabel() + + override func viewDidLoad() { + super.viewDidLoad() + + self.navigationItem.leftBarButtonItem = .init( + barButtonSystemItem: .add, + target: nil, + action: nil + ) + + self.view.backgroundColor = .white + + self.topLabel.text = "What's" + self.leadingLabel.text = "the" + self.trailingLabel.text = "point" + self.bottomLabel.text = "?" + + self.topLabel.translatesAutoresizingMaskIntoConstraints = false + self.leadingLabel.translatesAutoresizingMaskIntoConstraints = false + self.trailingLabel.translatesAutoresizingMaskIntoConstraints = false + self.bottomLabel.translatesAutoresizingMaskIntoConstraints = false + + self.view.addSubview(self.topLabel) + self.view.addSubview(self.leadingLabel) + self.view.addSubview(self.trailingLabel) + self.view.addSubview(self.bottomLabel) + + NSLayoutConstraint.activate([ + self.topLabel.topAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.topAnchor + ), + self.topLabel.centerXAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + self.leadingLabel.leadingAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.leadingAnchor + ), + self.leadingLabel.trailingAnchor.constraint( + lessThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + // self.leadingLabel.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor), + self.leadingLabel.centerYAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerYAnchor + ), + self.trailingLabel.leadingAnchor.constraint( + greaterThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + self.trailingLabel.trailingAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.trailingAnchor + ), + self.trailingLabel.centerYAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerYAnchor + ), + self.bottomLabel.bottomAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.bottomAnchor + ), + self.bottomLabel.centerXAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + ]) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + self.topLabel.font = .preferredFont( + forTextStyle: .headline, + compatibleWith: self.traitCollection + ) + self.leadingLabel.font = .preferredFont( + forTextStyle: .body, + compatibleWith: self.traitCollection + ) + self.trailingLabel.font = .preferredFont( + forTextStyle: .body, + compatibleWith: self.traitCollection + ) + self.bottomLabel.font = .preferredFont( + forTextStyle: .subheadline, + compatibleWith: self.traitCollection + ) + self.view.setNeedsUpdateConstraints() + self.view.updateConstraintsIfNeeded() + } + } + + let myViewController = MyViewController() + let navController = UINavigationController(rootViewController: myViewController) + let viewController = UITabBarController() + viewController.setViewControllers([navController], animated: false) + + assertSnapshot(of: viewController, as: .image(on: .iPhoneSe), named: "iphone-se") + assertSnapshot(of: viewController, as: .image(on: .iPhone8), named: "iphone-8") + assertSnapshot(of: viewController, as: .image(on: .iPhone8Plus), named: "iphone-8-plus") + assertSnapshot(of: viewController, as: .image(on: .iPhoneX), named: "iphone-x") + assertSnapshot(of: viewController, as: .image(on: .iPhoneXr), named: "iphone-xr") + assertSnapshot(of: viewController, as: .image(on: .iPhoneXsMax), named: "iphone-xs-max") + assertSnapshot(of: viewController, as: .image(on: .iPadMini), named: "ipad-mini") + assertSnapshot(of: viewController, as: .image(on: .iPad9_7), named: "ipad-9-7") + assertSnapshot(of: viewController, as: .image(on: .iPad10_2), named: "ipad-10-2") + assertSnapshot(of: viewController, as: .image(on: .iPadPro10_5), named: "ipad-pro-10-5") + assertSnapshot(of: viewController, as: .image(on: .iPadPro11), named: "ipad-pro-11") + assertSnapshot(of: viewController, as: .image(on: .iPadPro12_9), named: "ipad-pro-12-9") + + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneSe(.portrait)), + named: "iphone-se" + ) + assertSnapshot(of: viewController, as: .image(on: .iPhone8(.portrait)), named: "iphone-8") + assertSnapshot( + of: viewController, + as: .image(on: .iPhone8Plus(.portrait)), + named: "iphone-8-plus" + ) + assertSnapshot(of: viewController, as: .image(on: .iPhoneX(.portrait)), named: "iphone-x") + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneXr(.portrait)), + named: "iphone-xr" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneXsMax(.portrait)), + named: "iphone-xs-max" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.landscape)), + named: "ipad-mini" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.landscape)), + named: "ipad-9-7" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.landscape)), + named: "ipad-10-2" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.landscape)), + named: "ipad-pro-10-5" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.landscape)), + named: "ipad-pro-11" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.landscape)), + named: "ipad-pro-12-9" + ) + + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneSe(.landscape)), + named: "iphone-se-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhone8(.landscape)), + named: "iphone-8-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhone8Plus(.landscape)), + named: "iphone-8-plus-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneX(.landscape)), + named: "iphone-x-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneXr(.landscape)), + named: "iphone-xr-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPhoneXsMax(.landscape)), + named: "iphone-xs-max-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadMini(.portrait)), + named: "ipad-mini-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad9_7(.portrait)), + named: "ipad-9-7-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPad10_2(.portrait)), + named: "ipad-10-2-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro10_5(.portrait)), + named: "ipad-pro-10-5-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro11(.portrait)), + named: "ipad-pro-11-alternative" + ) + assertSnapshot( + of: viewController, + as: .image(on: .iPadPro12_9(.portrait)), + named: "ipad-pro-12-9-alternative" + ) + } + #endif + } + + func testCollectionViewsWithMultipleScreenSizes() { + #if os(iOS) + + final class CollectionViewController: UIViewController, UICollectionViewDataSource, + UICollectionViewDelegateFlowLayout + { + + let flowLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.minimumLineSpacing = 20 + return layout + }() + + lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + view.addSubview(collectionView) + + collectionView.backgroundColor = .white + collectionView.dataSource = self + collectionView.delegate = self + collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell") + collectionView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + collectionView.leadingAnchor.constraint( + equalTo: view.layoutMarginsGuide.leadingAnchor + ), + collectionView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), + collectionView.trailingAnchor.constraint( + equalTo: view.layoutMarginsGuide.trailingAnchor + ), + collectionView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor), + ]) + + collectionView.reloadData() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + collectionView.collectionViewLayout.invalidateLayout() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + collectionView.collectionViewLayout.invalidateLayout() + } + + func collectionView( + _ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath + ) + -> UICollectionViewCell + { + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: "Cell", + for: indexPath + ) + cell.contentView.backgroundColor = .orange + return cell + } + + func collectionView( + _ collectionView: UICollectionView, + numberOfItemsInSection section: Int + ) + -> Int + { + 20 + } + + func collectionView( + _ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + sizeForItemAt indexPath: IndexPath + ) -> CGSize { + CGSize( + width: min(collectionView.frame.width - 50, 300), + height: collectionView.frame.height + ) + } + + } + + let viewController = CollectionViewController() + + assertSnapshots( + of: viewController, + as: [ + "ipad": .image(on: .iPadPro12_9), + "iphoneSe": .image(on: .iPhoneSe), + "iphone8": .image(on: .iPhone8), + "iphoneMax": .image(on: .iPhoneXsMax), + ] + ) + #endif + } + + func testTraitsWithView() { + #if os(iOS) + if #available(iOS 11.0, *) { + let label = UILabel() + label.font = .preferredFont(forTextStyle: .title1) + label.adjustsFontForContentSizeCategory = true + label.text = "What's the point?" + + allContentSizes.forEach { name, contentSize in + assertSnapshot( + of: label, + as: .image(traits: .init(preferredContentSizeCategory: contentSize)), + named: "label-\(name)" + ) + } + } + #endif + } + + func testTraitsWithViewController() { + #if os(iOS) + let label = UILabel() + label.font = .preferredFont(forTextStyle: .title1) + label.adjustsFontForContentSizeCategory = true + label.text = "What's the point?" + + let viewController = UIViewController() + viewController.view.addSubview(label) + + label.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + label.leadingAnchor.constraint( + equalTo: viewController.view.layoutMarginsGuide.leadingAnchor + ), + label.topAnchor.constraint(equalTo: viewController.view.layoutMarginsGuide.topAnchor), + label.trailingAnchor.constraint( + equalTo: viewController.view.layoutMarginsGuide.trailingAnchor + ), + ]) + + allContentSizes.forEach { name, contentSize in + assertSnapshot( + of: viewController, + as: .recursiveDescription( + on: .iPhoneSe, + traits: .init(preferredContentSizeCategory: contentSize) + ), + named: "label-\(name)" + ) + } + #endif + } + + func testUIBezierPath() { + #if os(iOS) || os(tvOS) + let path = UIBezierPath.heart + + let osName: String + #if os(iOS) + osName = "iOS" + #elseif os(tvOS) + osName = "tvOS" + #endif + + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot(of: path, as: .image, named: osName) + } + + if #available(iOS 11.0, tvOS 11.0, *) { + assertSnapshot(of: path, as: .elementsDescription, named: osName) + } + #endif + } + + func testUIView() { + #if os(iOS) + let view = UIButton(type: .contactAdd) + assertSnapshot(of: view, as: .image) + assertSnapshot(of: view, as: .recursiveDescription) + #endif + } + + func testUIViewControllerLifeCycle() { + #if os(iOS) + class ViewController: UIViewController { + let viewDidLoadExpectation: XCTestExpectation + let viewWillAppearExpectation: XCTestExpectation + let viewDidAppearExpectation: XCTestExpectation + let viewWillDisappearExpectation: XCTestExpectation + let viewDidDisappearExpectation: XCTestExpectation + init( + viewDidLoadExpectation: XCTestExpectation, + viewWillAppearExpectation: XCTestExpectation, + viewDidAppearExpectation: XCTestExpectation, + viewWillDisappearExpectation: XCTestExpectation, + viewDidDisappearExpectation: XCTestExpectation + ) { + self.viewDidLoadExpectation = viewDidLoadExpectation + self.viewWillAppearExpectation = viewWillAppearExpectation + self.viewDidAppearExpectation = viewDidAppearExpectation + self.viewWillDisappearExpectation = viewWillDisappearExpectation + self.viewDidDisappearExpectation = viewDidDisappearExpectation + super.init(nibName: nil, bundle: nil) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { + super.viewDidLoad() + viewDidLoadExpectation.fulfill() + } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + viewWillAppearExpectation.fulfill() + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + viewDidAppearExpectation.fulfill() + } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + viewWillDisappearExpectation.fulfill() + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + viewDidDisappearExpectation.fulfill() + } + } + + let viewDidLoadExpectation = expectation(description: "viewDidLoad") + let viewWillAppearExpectation = expectation(description: "viewWillAppear") + let viewDidAppearExpectation = expectation(description: "viewDidAppear") + let viewWillDisappearExpectation = expectation(description: "viewWillDisappear") + let viewDidDisappearExpectation = expectation(description: "viewDidDisappear") + viewWillAppearExpectation.expectedFulfillmentCount = 4 + viewDidAppearExpectation.expectedFulfillmentCount = 4 + viewWillDisappearExpectation.expectedFulfillmentCount = 4 + viewDidDisappearExpectation.expectedFulfillmentCount = 4 + + let viewController = ViewController( + viewDidLoadExpectation: viewDidLoadExpectation, + viewWillAppearExpectation: viewWillAppearExpectation, + viewDidAppearExpectation: viewDidAppearExpectation, + viewWillDisappearExpectation: viewWillDisappearExpectation, + viewDidDisappearExpectation: viewDidDisappearExpectation + ) + + assertSnapshot(of: viewController, as: .image) + assertSnapshot(of: viewController, as: .image) + + wait( + for: [ + viewDidLoadExpectation, + viewWillAppearExpectation, + viewDidAppearExpectation, + viewWillDisappearExpectation, + viewDidDisappearExpectation, + ], + timeout: 1.0, + enforceOrder: true + ) + #endif + } + + func testCALayer() { + #if os(iOS) + let layer = CALayer() + layer.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + layer.backgroundColor = UIColor.red.cgColor + layer.borderWidth = 4.0 + layer.borderColor = UIColor.black.cgColor + assertSnapshot(of: layer, as: .image) + #endif + } + + func testCALayerWithGradient() { + #if os(iOS) + let baseLayer = CALayer() + baseLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + let gradientLayer = CAGradientLayer() + gradientLayer.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor] + gradientLayer.frame = baseLayer.frame + baseLayer.addSublayer(gradientLayer) + assertSnapshot(of: baseLayer, as: .image) + #endif + } + + func testViewControllerHierarchy() { + #if os(iOS) + let page = UIPageViewController( + transitionStyle: .scroll, + navigationOrientation: .horizontal + ) + page.setViewControllers([UIViewController()], direction: .forward, animated: false) + let tab = UITabBarController() + tab.viewControllers = [ + UINavigationController(rootViewController: page), + UINavigationController(rootViewController: UIViewController()), + UINavigationController(rootViewController: UIViewController()), + UINavigationController(rootViewController: UIViewController()), + UINavigationController(rootViewController: UIViewController()), + ] + assertSnapshot(of: tab, as: .hierarchy) + #endif + } + + func testURLRequest() { + var get = URLRequest(url: URL(string: "https://www.pointfree.co/")!) + get.addValue("pf_session={}", forHTTPHeaderField: "Cookie") + get.addValue("text/html", forHTTPHeaderField: "Accept") + get.addValue("application/json", forHTTPHeaderField: "Content-Type") + assertSnapshot(of: get, as: .raw, named: "get") + assertSnapshot(of: get, as: .curl, named: "get-curl") + + var getWithQuery = URLRequest( + url: URL(string: "https://www.pointfree.co?key_2=value_2&key_1=value_1&key_3=value_3")! + ) + getWithQuery.addValue("pf_session={}", forHTTPHeaderField: "Cookie") + getWithQuery.addValue("text/html", forHTTPHeaderField: "Accept") + getWithQuery.addValue("application/json", forHTTPHeaderField: "Content-Type") + assertSnapshot(of: getWithQuery, as: .raw, named: "get-with-query") + assertSnapshot(of: getWithQuery, as: .curl, named: "get-with-query-curl") + + var post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!) + post.httpMethod = "POST" + post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie") + post.addValue("text/html", forHTTPHeaderField: "Accept") + post.httpBody = Data("pricing[billing]=monthly&pricing[lane]=individual".utf8) + assertSnapshot(of: post, as: .raw, named: "post") + assertSnapshot(of: post, as: .curl, named: "post-curl") + + var postWithJSON = URLRequest( + url: URL(string: "http://dummy.restapiexample.com/api/v1/create")! + ) + postWithJSON.httpMethod = "POST" + postWithJSON.addValue("application/json", forHTTPHeaderField: "Content-Type") + postWithJSON.addValue("application/json", forHTTPHeaderField: "Accept") + postWithJSON.httpBody = Data( + "{\"name\":\"tammy134235345235\", \"salary\":0, \"age\":\"tammy133\"}".utf8 + ) + assertSnapshot(of: postWithJSON, as: .raw, named: "post-with-json") + assertSnapshot(of: postWithJSON, as: .curl, named: "post-with-json-curl") + + var head = URLRequest(url: URL(string: "https://www.pointfree.co/")!) + head.httpMethod = "HEAD" + head.addValue("pf_session={}", forHTTPHeaderField: "Cookie") + assertSnapshot(of: head, as: .raw, named: "head") + assertSnapshot(of: head, as: .curl, named: "head-curl") + + post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!) + post.httpMethod = "POST" + post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie") + post.addValue("application/json", forHTTPHeaderField: "Accept") + post.httpBody = Data( + """ + {"pricing": {"lane": "individual","billing": "monthly"}} + """.utf8 + ) + } + + func testWebView() throws { + #if os(iOS) || os(macOS) + let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/pointfree.html") + let html = try String(contentsOf: fixtureUrl) + let webView = WKWebView() + webView.loadHTMLString(html, baseURL: nil) + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot( + of: webView, + as: .image(size: .init(width: 800, height: 600)), + named: platform + ) + } + #endif + } + + func testViewWithZeroHeightOrWidth() { + #if os(iOS) || os(tvOS) + var rect = CGRect(x: 0, y: 0, width: 350, height: 0) + var view = UIView(frame: rect) + view.backgroundColor = .red + assertSnapshot(of: view, as: .image, named: "noHeight") + + rect = CGRect(x: 0, y: 0, width: 0, height: 350) + view = UIView(frame: rect) + view.backgroundColor = .green + assertSnapshot(of: view, as: .image, named: "noWidth") + + rect = CGRect(x: 0, y: 0, width: 0, height: 0) + view = UIView(frame: rect) + view.backgroundColor = .blue + assertSnapshot(of: view, as: .image, named: "noWidth.noHeight") + #endif + } + + func testViewAgainstEmptyImage() { + #if os(iOS) || os(tvOS) + let rect = CGRect(x: 0, y: 0, width: 0, height: 0) + let view = UIView(frame: rect) + view.backgroundColor = .blue + + let failure = verifySnapshot(of: view, as: .image, named: "notEmptyImage") + XCTAssertNotNil(failure) + #endif + } + + func testEmbeddedWebView() throws { + #if os(iOS) + let label = UILabel() + label.text = "Hello, Blob!" + + let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/pointfree.html") + let html = try String(contentsOf: fixtureUrl) + let webView = WKWebView() + webView.loadHTMLString(html, baseURL: nil) + webView.isHidden = true + + let stackView = UIStackView(arrangedSubviews: [label, webView]) + stackView.axis = .vertical + + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot( + of: stackView, + as: .image(size: .init(width: 800, height: 600)), + named: platform + ) + } + #endif + } + + #if os(iOS) || os(macOS) + final class ManipulatingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate { + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + webView.evaluateJavaScript("document.body.children[0].classList.remove(\"hero\")") // Change layout + } + } + func testWebViewWithManipulatingNavigationDelegate() throws { + let manipulatingWKWebViewNavigationDelegate = ManipulatingWKWebViewNavigationDelegate() + let webView = WKWebView() + webView.navigationDelegate = manipulatingWKWebViewNavigationDelegate + + let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/pointfree.html") + let html = try String(contentsOf: fixtureUrl) + webView.loadHTMLString(html, baseURL: nil) + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot( + of: webView, + as: .image(size: .init(width: 800, height: 600)), + named: platform + ) + } + _ = manipulatingWKWebViewNavigationDelegate + } + + final class CancellingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate { + func webView( + _ webView: WKWebView, + decidePolicyFor navigationAction: WKNavigationAction, + decisionHandler: @escaping (WKNavigationActionPolicy) -> Void + ) { + decisionHandler(.cancel) + } + } + + func testWebViewWithCancellingNavigationDelegate() throws { + let cancellingWKWebViewNavigationDelegate = CancellingWKWebViewNavigationDelegate() + let webView = WKWebView() + webView.navigationDelegate = cancellingWKWebViewNavigationDelegate + + let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/pointfree.html") + let html = try String(contentsOf: fixtureUrl) + webView.loadHTMLString(html, baseURL: nil) + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + assertSnapshot( + of: webView, + as: .image(size: .init(width: 800, height: 600)), + named: platform + ) + } + _ = cancellingWKWebViewNavigationDelegate + } + #endif + + #if os(iOS) + @available(iOS 13.0, *) + func testSwiftUIView_iOS() { + struct MyView: SwiftUI.View { + var body: some SwiftUI.View { + HStack { + Image(systemName: "checkmark.circle.fill") + Text("Checked").fixedSize() + } + .padding(5) + .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue)) + .padding(10) + } + } + + let view = MyView().background(Color.yellow) + + assertSnapshot(of: view, as: .image(traits: .init(userInterfaceStyle: .light))) + assertSnapshot( + of: view, + as: .image(layout: .sizeThatFits, traits: .init(userInterfaceStyle: .light)), + named: "size-that-fits" + ) + assertSnapshot( + of: view, + as: .image( + layout: .fixed(width: 200.0, height: 100.0), + traits: .init(userInterfaceStyle: .light) + ), + named: "fixed" + ) + assertSnapshot( + of: view, + as: .image(layout: .device(config: .iPhoneSe), traits: .init(userInterfaceStyle: .light)), + named: "device" + ) + } + #endif + + #if os(tvOS) + @available(tvOS 13.0, *) + func testSwiftUIView_tvOS() { + struct MyView: SwiftUI.View { + var body: some SwiftUI.View { + HStack { + Image(systemName: "checkmark.circle.fill") + Text("Checked").fixedSize() + } + .padding(5) + .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue)) + .padding(10) + } + } + let view = MyView().background(Color.yellow) + + assertSnapshot(of: view, as: .image()) + assertSnapshot(of: view, as: .image(layout: .sizeThatFits), named: "size-that-fits") + assertSnapshot( + of: view, + as: .image(layout: .fixed(width: 300.0, height: 100.0)), + named: "fixed" + ) + assertSnapshot(of: view, as: .image(layout: .device(config: .tv)), named: "device") + } + #endif +} + +#if os(iOS) +private let allContentSizes = + [ + "extra-small": UIContentSizeCategory.extraSmall, + "small": .small, + "medium": .medium, + "large": .large, + "extra-large": .extraLarge, + "extra-extra-large": .extraExtraLarge, + "extra-extra-extra-large": .extraExtraExtraLarge, + "accessibility-medium": .accessibilityMedium, + "accessibility-large": .accessibilityLarge, + "accessibility-extra-large": .accessibilityExtraLarge, + "accessibility-extra-extra-large": .accessibilityExtraExtraLarge, + "accessibility-extra-extra-extra-large": .accessibilityExtraExtraExtraLarge, + ] +#endif +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/SnapshotsTraitTests.swift b/Tests/Deprecated/SnapshotTestingTests/SnapshotsTraitTests.swift new file mode 100644 index 000000000..79c20c077 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/SnapshotsTraitTests.swift @@ -0,0 +1,53 @@ +#if !os(visionOS) +#if compiler(>=6) && canImport(Testing) +import Testing +@_spi(Internals) import SnapshotTesting + +extension BaseSuite { + struct SnapshotsTraitTests { + @Test(.snapshots(diffTool: "ksdiff")) + func testDiffTool() { + #expect( + _diffTool(currentFilePath: "old.png", failedFilePath: "new.png") + == "ksdiff old.png new.png" + ) + } + + @Suite(.snapshots(diffTool: "ksdiff")) + struct OverrideDiffTool { + @Test(.snapshots(diffTool: "difftool")) + func testDiffToolOverride() { + #expect( + _diffTool(currentFilePath: "old.png", failedFilePath: "new.png") + == "difftool old.png new.png" + ) + } + + @Suite(.snapshots(record: .all)) + struct OverrideRecord { + @Test + func config() { + #expect( + _diffTool(currentFilePath: "old.png", failedFilePath: "new.png") + == "ksdiff old.png new.png" + ) + #expect(_record == .all) + } + + @Suite(.snapshots(record: .failed, diffTool: "diff")) + struct OverrideDiffToolAndRecord { + @Test + func config() { + #expect( + _diffTool(currentFilePath: "old.png", failedFilePath: "new.png") + == "diff old.png new.png" + ) + #expect(_record == .failed) + } + } + } + } + } +} +#endif +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/SwiftTestingTests.swift b/Tests/Deprecated/SnapshotTestingTests/SwiftTestingTests.swift new file mode 100644 index 000000000..31d54642c --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/SwiftTestingTests.swift @@ -0,0 +1,28 @@ +#if !os(visionOS) +#if compiler(>=6) && canImport(Testing) +import Testing +import SnapshotTesting + +extension BaseSuite { + @Suite(.serialized, .snapshots(record: .missing)) + struct SwiftTestingTests { + @Test func testSnapshot() { + assertSnapshot(of: ["Hello", "World"], as: .dump, named: "snap") + withKnownIssue { + assertSnapshot(of: ["Goodbye", "World"], as: .dump, named: "snap") + } matching: { issue in + issue.description.hasSuffix( + """ + @@ −1,4 +1,4 @@ +  ▿ 2 elements + − - "Hello" + + - "Goodbye" +   - "World" + """ + ) + } + } + } +} +#endif +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/WaitTests.swift b/Tests/Deprecated/SnapshotTestingTests/WaitTests.swift new file mode 100644 index 000000000..471bd4acf --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/WaitTests.swift @@ -0,0 +1,21 @@ +#if !os(visionOS) +import XCTest + +@testable import SnapshotTesting + +@available(*, deprecated) +class WaitTests: BaseTestCase { + func testWait() { + var value = "Hello" + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + value = "Goodbye" + } + + let strategy = Snapshotting.lines.pullback { (_: Void) in + value + } + + assertSnapshot(of: (), as: .wait(for: 1.5, on: strategy)) + } +} +#endif diff --git a/Tests/Deprecated/SnapshotTestingTests/WithSnapshotTestingTests.swift b/Tests/Deprecated/SnapshotTestingTests/WithSnapshotTestingTests.swift new file mode 100644 index 000000000..d00c7e8bc --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/WithSnapshotTestingTests.swift @@ -0,0 +1,37 @@ +#if !os(visionOS) +@_spi(Internals) @testable import SnapshotTesting +import XCTest + +@available(*, deprecated) +class WithSnapshotTestingTests: XCTestCase { + func testNesting() { + withSnapshotTesting(record: .all) { + XCTAssertEqual( + SnapshotTestingConfiguration.current? + .diffTool?(currentFilePath: "old.png", failedFilePath: "new.png"), + """ + @− + "file://old.png" + @+ + "file://new.png" + + To configure output for a custom diff tool, use 'withSnapshotTesting'. For example: + + withSnapshotTesting(diffTool: .ksdiff) { + // ... + } + """ + ) + XCTAssertEqual(SnapshotTestingConfiguration.current?.record, .all) + withSnapshotTesting(diffTool: "ksdiff") { + XCTAssertEqual( + SnapshotTestingConfiguration.current? + .diffTool?(currentFilePath: "old.png", failedFilePath: "new.png"), + "ksdiff old.png new.png" + ) + XCTAssertEqual(SnapshotTestingConfiguration.current?.record, .all) + } + } + } +} +#endif diff --git a/Tests/SnapshotTestingTests/__Fixtures__/earth.png b/Tests/Deprecated/SnapshotTestingTests/__Fixtures__/earth.png similarity index 100% rename from Tests/SnapshotTestingTests/__Fixtures__/earth.png rename to Tests/Deprecated/SnapshotTestingTests/__Fixtures__/earth.png diff --git a/Tests/SnapshotTestingTests/__Fixtures__/pointfree.html b/Tests/Deprecated/SnapshotTestingTests/__Fixtures__/pointfree.html similarity index 100% rename from Tests/SnapshotTestingTests/__Fixtures__/pointfree.html rename to Tests/Deprecated/SnapshotTestingTests/__Fixtures__/pointfree.html diff --git a/Tests/SnapshotTestingTests/__Fixtures__/testImagePrecision.reference.png b/Tests/Deprecated/SnapshotTestingTests/__Fixtures__/testImagePrecision.reference.png similarity index 100% rename from Tests/SnapshotTestingTests/__Fixtures__/testImagePrecision.reference.png rename to Tests/Deprecated/SnapshotTestingTests/__Fixtures__/testImagePrecision.reference.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/AssertSnapshotSwiftTests/dump.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/AssertSnapshotSwiftTests/dump.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/AssertSnapshotSwiftTests/dump.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/AssertSnapshotSwiftTests/dump.1.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/AssertSnapshotSwiftTests/dump.2.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/AssertSnapshotSwiftTests/dump.2.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/AssertSnapshotSwiftTests/dump.2.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/AssertSnapshotSwiftTests/dump.2.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAny.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAny.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAny.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAny.1.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnyAsJson.1.json b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnyAsJson.1.json similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnyAsJson.1.json rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnyAsJson.1.json diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.character.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.character.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.character.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.character.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.data.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.data.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.data.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.data.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.date.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.date.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.date.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.date.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.nsobject.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.nsobject.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.nsobject.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.nsobject.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.string.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.string.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.string.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.string.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.substring.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.substring.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.substring.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.substring.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.url.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.url.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.url.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAnySnapshotStringConvertible.url.txt diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.1.png new file mode 100644 index 000000000..07e6c0318 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.1.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.2.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.2.png new file mode 100644 index 000000000..08068a033 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.2.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPad-image.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPad-image.png new file mode 100644 index 000000000..72bc67f42 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPad-image.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPhoneSE-image.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPhoneSE-image.png new file mode 100644 index 000000000..02bba0b90 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPhoneSE-image.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAutolayout.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAutolayout.1.png new file mode 100644 index 000000000..b3acdfb79 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAutolayout.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayer.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayer.1.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayer.1.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayer.1.png diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayerWithGradient.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayerWithGradient.1.png new file mode 100644 index 000000000..88d1d97ba Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayerWithGradient.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.iOS.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.iOS.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.iOS.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.iOS.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.iOS.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.iOS.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.iOS.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.iOS.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.macOS.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.macOS.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.macOS.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.macOS.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.macOS.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.macOS.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.macOS.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.macOS.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.tvOS.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.tvOS.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.tvOS.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.tvOS.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.tvOS.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.tvOS.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.tvOS.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCGPath.tvOS.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCaseIterable.1.csv b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCaseIterable.1.csv similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCaseIterable.1.csv rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCaseIterable.1.csv diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.ipad.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.ipad.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.ipad.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.ipad.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphone8.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphone8.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphone8.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphone8.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testData.1 b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testData.1 similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testData.1 rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testData.1 diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testDeterministicDictionaryAndSetSnapshots.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testDeterministicDictionaryAndSetSnapshots.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testDeterministicDictionaryAndSetSnapshots.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testDeterministicDictionaryAndSetSnapshots.1.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEmbeddedWebView.ios.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEmbeddedWebView.ios.png similarity index 91% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEmbeddedWebView.ios.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEmbeddedWebView.ios.png index 55f85885a..7bd552688 100644 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEmbeddedWebView.ios.png and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEmbeddedWebView.ios.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEncodable.1.json b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEncodable.1.json similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEncodable.1.json rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEncodable.1.json diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEncodable.2.plist b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEncodable.2.plist similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEncodable.2.plist rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testEncodable.2.plist diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testImagePrecision.exact.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testImagePrecision.exact.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testImagePrecision.exact.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testImagePrecision.exact.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testImagePrecision.perceptual.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testImagePrecision.perceptual.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testImagePrecision.perceptual.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testImagePrecision.perceptual.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMixedViews.ios.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMixedViews.ios.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMixedViews.ios.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMixedViews.ios.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMixedViews.macos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMixedViews.macos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMixedViews.macos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMixedViews.macos.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMultipleSnapshots.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMultipleSnapshots.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMultipleSnapshots.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMultipleSnapshots.1.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMultipleSnapshots.2.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMultipleSnapshots.2.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMultipleSnapshots.2.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testMultipleSnapshots.2.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSBezierPath.macOS.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSBezierPath.macOS.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSBezierPath.macOS.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSBezierPath.macOS.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSBezierPath.macOS.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSBezierPath.macOS.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSBezierPath.macOS.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSBezierPath.macOS.txt diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.1.png new file mode 100644 index 000000000..f534333b2 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.2.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.2.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.2.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.2.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSViewWithLayer.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSViewWithLayer.1.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSViewWithLayer.1.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSViewWithLayer.1.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSViewWithLayer.2.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSViewWithLayer.2.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSViewWithLayer.2.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSViewWithLayer.2.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNamedAssertion.named.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNamedAssertion.named.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNamedAssertion.named.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNamedAssertion.named.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.ios.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.ios.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.ios.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.ios.png diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.macos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.macos.png new file mode 100644 index 000000000..5e80dbfdd Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.macos.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.tvos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.tvos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.tvos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.tvos.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testRecursion.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testRecursion.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testRecursion.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testRecursion.1.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testRecursion.2.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testRecursion.2.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testRecursion.2.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testRecursion.2.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.ios.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.ios.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.ios.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.ios.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.macos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.macos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.macos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.macos.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.tvos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.tvos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.tvos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSCNView.tvos.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.ios.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.ios.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.ios.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.ios.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.macos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.macos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.macos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.macos.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.tvos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.tvos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.tvos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSKView.tvos.png diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.1.png new file mode 100644 index 000000000..0c40eba23 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.1.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.device.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.device.png new file mode 100644 index 000000000..f29963c29 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.device.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.fixed.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.fixed.png new file mode 100644 index 000000000..3cc3e3f91 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.fixed.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.size-that-fits.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.size-that-fits.png new file mode 100644 index 000000000..0c40eba23 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.size-that-fits.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.1.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.1.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.1.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.device.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.device.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.device.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.device.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.fixed.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.fixed.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.fixed.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.fixed.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.size-that-fits.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.size-that-fits.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.size-that-fits.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_tvOS.size-that-fits.png diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTableViewController.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTableViewController.1.png new file mode 100644 index 000000000..02bba0b90 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTableViewController.1.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-33-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-33-split-portrait.png new file mode 100644 index 000000000..443d6beab Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-33-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-50-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-50-split-landscape.png new file mode 100644 index 000000000..e9d9a8b52 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-50-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-landscape.png new file mode 100644 index 000000000..a825c6c9e Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-portrait.png new file mode 100644 index 000000000..c6d18506a Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-alternative.png new file mode 100644 index 000000000..a9b2b8f71 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-split-landscape.png new file mode 100644 index 000000000..cf344e15a Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.png new file mode 100644 index 000000000..f1cdf3054 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.txt new file mode 100644 index 000000000..37a8a244a --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.txt @@ -0,0 +1,5 @@ +> + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-landscape.png new file mode 100644 index 000000000..dba71012c Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-portrait.png new file mode 100644 index 000000000..7de816148 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-50-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-50-split-landscape.png new file mode 100644 index 000000000..be3566ea6 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-50-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-landscape.png new file mode 100644 index 000000000..c174efd75 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-portrait.png new file mode 100644 index 000000000..6aa73d43c Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-alternative.png new file mode 100644 index 000000000..6b24fcaa8 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.png new file mode 100644 index 000000000..0c44e0fa2 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.txt new file mode 100644 index 000000000..0c02f802c --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.txt @@ -0,0 +1,5 @@ +> + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-landscape.png new file mode 100644 index 000000000..dba71012c Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-portrait.png new file mode 100644 index 000000000..7de816148 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-50-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-50-split-landscape.png new file mode 100644 index 000000000..be3566ea6 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-50-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-landscape.png new file mode 100644 index 000000000..c174efd75 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-portrait.png new file mode 100644 index 000000000..6aa73d43c Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-alternative.png new file mode 100644 index 000000000..6b24fcaa8 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.png new file mode 100644 index 000000000..0c44e0fa2 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.txt new file mode 100644 index 000000000..0c02f802c --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.txt @@ -0,0 +1,5 @@ +> + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..83b9972cb Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.png new file mode 100644 index 000000000..ca641d003 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.txt new file mode 100644 index 000000000..14b10d2b7 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.txt @@ -0,0 +1,5 @@ +> + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-landscape.png new file mode 100644 index 000000000..d386a9a74 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-portrait.png new file mode 100644 index 000000000..f8fe96a8f Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-50-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-50-split-landscape.png new file mode 100644 index 000000000..e273f77ec Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-50-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-landscape.png new file mode 100644 index 000000000..5e9772076 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-portrait.png new file mode 100644 index 000000000..c410c133d Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11-alternative.png new file mode 100644 index 000000000..85e181a0d Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.png new file mode 100644 index 000000000..e001a5499 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.txt new file mode 100644 index 000000000..fc05aaf7b --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.txt @@ -0,0 +1,5 @@ +> + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-landscape.png new file mode 100644 index 000000000..a8c1f37d1 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-portrait.png new file mode 100644 index 000000000..7d8b995d3 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-50-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-50-split-landscape.png new file mode 100644 index 000000000..66607406d Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-50-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-landscape.png new file mode 100644 index 000000000..e90f5deb0 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-portrait.png new file mode 100644 index 000000000..ddf97cb45 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..a011a19a3 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.png new file mode 100644 index 000000000..e89c27acc Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.txt new file mode 100644 index 000000000..c5a4236d6 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.txt @@ -0,0 +1,5 @@ +> + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-landscape.png new file mode 100644 index 000000000..c0a0f47bd Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-portrait.png new file mode 100644 index 000000000..3b7c2f92b Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-50-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-50-split-landscape.png new file mode 100644 index 000000000..563d92866 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-50-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-landscape.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-landscape.png new file mode 100644 index 000000000..82e69fcd8 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-landscape.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-portrait.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-portrait.png new file mode 100644 index 000000000..a323bff74 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-portrait.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-alternative.png new file mode 100644 index 000000000..86ae82cb0 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus-alternative.png new file mode 100644 index 000000000..312e9bef3 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.png new file mode 100644 index 000000000..3564c03b5 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.txt similarity index 88% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.txt index 93b959a4b..e6944ca83 100644 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.txt +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.txt @@ -1,5 +1,5 @@ > - | > + | > | > | > | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.png new file mode 100644 index 000000000..b13833eb5 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.txt similarity index 88% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.txt index 08bee7ef6..03f11e7f0 100644 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.txt +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.txt @@ -1,5 +1,5 @@ > - | > + | > | > | > | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..f6561651b Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-large.png new file mode 100644 index 000000000..469c031a5 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-large.png new file mode 100644 index 000000000..3b262d30d Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-large.png new file mode 100644 index 000000000..b3ada66f1 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-medium.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-medium.png new file mode 100644 index 000000000..d5a2ed2ef Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-medium.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-alternative.png new file mode 100644 index 000000000..70e5dd687 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-extra-large.png new file mode 100644 index 000000000..4faa6ffab Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-large.png new file mode 100644 index 000000000..7781b951b Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-large.png new file mode 100644 index 000000000..18eb589b2 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-small.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-small.png new file mode 100644 index 000000000..b11d8d582 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-small.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-large.png new file mode 100644 index 000000000..7dff1ea58 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-medium.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-medium.png new file mode 100644 index 000000000..dbf2e39d4 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-medium.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-small.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-small.png new file mode 100644 index 000000000..658594b64 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-small.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.png new file mode 100644 index 000000000..dbf2e39d4 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.txt similarity index 88% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.txt index 763b19fec..52bc52f7f 100644 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.txt +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.txt @@ -1,5 +1,5 @@ > - | > + | > | > | > | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x-alternative.png new file mode 100644 index 000000000..62a0fa1f6 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.png new file mode 100644 index 000000000..3f02b613b Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.txt similarity index 88% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.txt index f54f99daf..6874d98e4 100644 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.txt +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.txt @@ -1,5 +1,5 @@ > - | > + | > | > | > | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr-alternative.png new file mode 100644 index 000000000..5e7ab1783 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.png new file mode 100644 index 000000000..efcf38ca9 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.txt similarity index 88% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.txt index b0bf07c2d..c0985f5a6 100644 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.txt +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.txt @@ -1,5 +1,5 @@ > - | > + | > | > | > | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max-alternative.png new file mode 100644 index 000000000..5e7ab1783 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.png new file mode 100644 index 000000000..efcf38ca9 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.txt new file mode 100644 index 000000000..c0985f5a6 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.txt @@ -0,0 +1,5 @@ +> + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.tv.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.tv.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.tv.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.tv.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.tv4k.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.tv4k.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.tv4k.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.tv4k.png diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png new file mode 100644 index 000000000..e3dfee22f Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png new file mode 100644 index 000000000..f491d3588 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png new file mode 100644 index 000000000..9b5d36f00 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png new file mode 100644 index 000000000..f42937bc7 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png new file mode 100644 index 000000000..9b5d36f00 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini.png new file mode 100644 index 000000000..f42937bc7 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..0d292d592 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png new file mode 100644 index 000000000..90f830e04 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png new file mode 100644 index 000000000..c5f712225 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png new file mode 100644 index 000000000..702c4c345 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..039a3aaff Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png new file mode 100644 index 000000000..3ee85ad00 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png new file mode 100644 index 000000000..9c0909cfb Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png new file mode 100644 index 000000000..ebf260a8f Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png new file mode 100644 index 000000000..cd2c247d8 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8.png new file mode 100644 index 000000000..943bc95fe Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png new file mode 100644 index 000000000..91c135b62 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se.png new file mode 100644 index 000000000..cb0dd805a Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png new file mode 100644 index 000000000..0536b576c Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x.png new file mode 100644 index 000000000..9375313a1 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png new file mode 100644 index 000000000..3b429a942 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr.png new file mode 100644 index 000000000..a17b11cb9 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png new file mode 100644 index 000000000..3b429a942 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png new file mode 100644 index 000000000..a17b11cb9 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..dd347677f Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-large.png new file mode 100644 index 000000000..55ea19762 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-large.png new file mode 100644 index 000000000..438efed6a Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-large.png new file mode 100644 index 000000000..ca5a0817c Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-medium.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-medium.png new file mode 100644 index 000000000..4c67374ce Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-medium.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-extra-large.png new file mode 100644 index 000000000..90fc36098 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-large.png new file mode 100644 index 000000000..d7a87600e Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-large.png new file mode 100644 index 000000000..ff3ba2f2a Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-small.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-small.png new file mode 100644 index 000000000..556bb72b1 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-small.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-large.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-large.png new file mode 100644 index 000000000..0e4451914 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-large.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-medium.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-medium.png new file mode 100644 index 000000000..e1b3bfddf Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-medium.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-small.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-small.png new file mode 100644 index 000000000..21ef1320a Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-small.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-large.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-large.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-large.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-extra-large.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-large.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-large.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-large.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-large.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-medium.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-medium.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-medium.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-accessibility-medium.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-extra-extra-large.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-extra-extra-large.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-extra-extra-large.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-extra-extra-large.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-extra-large.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-extra-large.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-extra-large.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-extra-large.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-large.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-large.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-large.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-large.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-small.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-small.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-small.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-extra-small.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-large.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-large.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-large.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-large.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-medium.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-medium.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-medium.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-medium.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-small.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-small.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-small.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithViewController.label-small.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.iOS.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.iOS.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.iOS.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.iOS.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.iOS.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.iOS.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.iOS.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.iOS.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.tvOS.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.tvOS.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.tvOS.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.tvOS.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.tvOS.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.tvOS.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.tvOS.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIBezierPath.tvOS.txt diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.1.png new file mode 100644 index 000000000..904dbcbca Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.1.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.2.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.2.txt new file mode 100644 index 000000000..65f50d508 --- /dev/null +++ b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.2.txt @@ -0,0 +1,2 @@ +> + | > \ No newline at end of file diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.1.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.1.png new file mode 100644 index 000000000..b3acdfb79 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.1.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.2.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.2.png new file mode 100644 index 000000000..b3acdfb79 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-curl.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-curl.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-curl.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-curl.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-with-query-curl.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-with-query-curl.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-with-query-curl.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-with-query-curl.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-with-query.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-with-query.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-with-query.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get-with-query.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.get.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.head-curl.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.head-curl.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.head-curl.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.head-curl.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.head.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.head.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.head.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.head.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-curl.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-curl.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-curl.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-curl.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-json.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-json.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-json.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-json.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-with-json-curl.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-with-json-curl.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-with-json-curl.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-with-json-curl.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-with-json.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-with-json.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-with-json.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post-with-json.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testURLRequest.post.txt diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewAgainstEmptyImage.notEmptyImage.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewAgainstEmptyImage.notEmptyImage.png new file mode 100644 index 000000000..3e29a56eb Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewAgainstEmptyImage.notEmptyImage.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewControllerHierarchy.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewControllerHierarchy.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewControllerHierarchy.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewControllerHierarchy.1.txt diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noHeight.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noHeight.png new file mode 100644 index 000000000..3e29a56eb Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noHeight.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth-noHeight.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth-noHeight.png new file mode 100644 index 000000000..3e29a56eb Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth-noHeight.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth.png new file mode 100644 index 000000000..3e29a56eb Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth.png differ diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.ios.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.ios.png new file mode 100644 index 000000000..3c5040659 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.ios.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.macos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.macos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.macos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.macos.png diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.ios.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.ios.png new file mode 100644 index 000000000..3c5040659 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.ios.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.macos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.macos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.macos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.macos.png diff --git a/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.ios.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.ios.png new file mode 100644 index 000000000..3c5040659 Binary files /dev/null and b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.ios.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.macos.png b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.macos.png similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.macos.png rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.macos.png diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.1.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.snap.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.snap.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.snap.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.snap.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshotFailure.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshotFailure.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshotFailure.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshotFailure.1.txt diff --git a/Tests/SnapshotTestingTests/__Snapshots__/WaitTests/testWait.1.txt b/Tests/Deprecated/SnapshotTestingTests/__Snapshots__/WaitTests/testWait.1.txt similarity index 100% rename from Tests/SnapshotTestingTests/__Snapshots__/WaitTests/testWait.1.txt rename to Tests/Deprecated/SnapshotTestingTests/__Snapshots__/WaitTests/testWait.1.txt diff --git a/Tests/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift b/Tests/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift index 8e2f3439c..9a7ce45ad 100644 --- a/Tests/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift +++ b/Tests/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift @@ -1,336 +1,336 @@ #if canImport(Testing) - import Testing - import Foundation - import InlineSnapshotTesting - import SnapshotTesting +import Testing +import Foundation +import InlineSnapshotTesting +import SnapshotTesting - extension BaseSuite { - @Suite - struct AssertInlineSnapshotTests { - @Test func inlineSnapshot() { - assertInlineSnapshot(of: ["Hello", "World"], as: .dump) { - """ - ▿ 2 elements - - "Hello" - - "World" +extension BaseSuite { - """ + struct AssertInlineSnapshotTests { + @Test func inlineSnapshot() async throws { + try assertInline(of: ["Hello", "World"], as: .customDump) { + """ + [ + [0]: "Hello", + [1]: "World" + ] + """ + } } - } - - @Test(.snapshots(record: .missing)) func inlineSnapshotFailure() { - withKnownIssue { - assertInlineSnapshot(of: ["Hello", "World"], as: .dump) { - """ - ▿ 2 elements - - "Hello" - """ - } - } matching: { issue in - issue.description == """ - Issue recorded: Snapshot did not match. Difference: … - - @@ −1,3 +1,4 @@ -  ▿ 2 elements -   - "Hello" - + - "World" -   - """ + @Test(.record(.missing)) + func inlineSnapshotFailure() throws { + try withKnownIssue { + try assertInline(of: ["Hello", "World"], as: .customDump) { + """ + ▿ 2 elements + - "Hello" + + """ + } + } matching: { + $0.comments.first?.rawValue == """ + Snapshot does not match reference. Difference: … + + @@ −1,3 +1,4 @@ + −▿ 2 elements + − - "Hello" + − + +[ + + [0]: "Hello", + + [1]: "World" + +] + """ + } } - } - - @Test func inlineSnapshot_NamedTrailingClosure() { - assertInlineSnapshot( - of: ["Hello", "World"], as: .dump, - matches: { - """ - ▿ 2 elements - - "Hello" - - "World" - """ - }) - } - - @Test func inlineSnapshot_Escaping() { - assertInlineSnapshot(of: "Hello\"\"\"#, world", as: .lines) { - ##""" - Hello"""#, world - """## + @Test func inlineSnapshot_NamedTrailingClosure() async throws { + try assertInline( + of: ["Hello", "World"], + as: .customDump, + matches: { + """ + [ + [0]: "Hello", + [1]: "World" + ] + """ + } + ) } - } - - @Test func customInlineSnapshot() { - assertCustomInlineSnapshot { - "Hello" - } is: { - """ - - "Hello" - """ + @Test func inlineSnapshot_Escaping() async throws { + try assertInline(of: "Hello\"\"\"#, world", as: .lines) { + ##""" + Hello"""#, world + """## + } } - } - - @Test func customInlineSnapshot_Multiline() { - assertCustomInlineSnapshot { - """ - "Hello" - "World" - """ - } is: { - #""" - - "\"Hello\"\n\"World\"" - """# + @Test func customInlineSnapshot() async throws { + try assertCustomInlineSnapshot { + "Hello" + } is: { + """ + "Hello" + """ + } } - } - @Test func customInlineSnapshot_SingleTrailingClosure() { - assertCustomInlineSnapshot(of: { "Hello" }) { - """ - - "Hello" - - """ + @Test func customInlineSnapshot_Multiline() async throws { + try assertCustomInlineSnapshot { + """ + "Hello" + "World" + """ + } is: { + #""" + """ + "Hello" + "World" + """ + """# + } } - } - - @Test func customInlineSnapshot_MultilineSingleTrailingClosure() { - assertCustomInlineSnapshot( - of: { "Hello" } - ) { - """ - - "Hello" - """ + @Test func customInlineSnapshot_SingleTrailingClosure() async throws { + try assertCustomInlineSnapshot(of: { "Hello" }) { + """ + "Hello" + """ + } } - } - @Test func customInlineSnapshot_NoTrailingClosure() { - assertCustomInlineSnapshot( - of: { "Hello" }, - is: { - """ - - "Hello" - - """ - } - ) - } - - @Test func argumentlessInlineSnapshot() { - func assertArgumentlessInlineSnapshot( - expected: (() -> String)? = nil, - fileID: StaticString = #fileID, - filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column - ) { - assertInlineSnapshot( - of: "Hello", - as: .dump, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "is", - trailingClosureOffset: 1 - ), - matches: expected, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) + @Test func customInlineSnapshot_MultilineSingleTrailingClosure() async throws { + try assertCustomInlineSnapshot( + of: { "Hello" } + ) { + """ + "Hello" + """ + } } - assertArgumentlessInlineSnapshot { - """ - - "Hello" - - """ + @Test func customInlineSnapshot_NoTrailingClosure() async throws { + try assertCustomInlineSnapshot( + of: { "Hello" }, + is: { + """ + "Hello" + """ + } + ) } - } - @Test func multipleInlineSnapshots() { - func assertResponse( - of url: () -> String, - head: (() -> String)? = nil, - body: (() -> String)? = nil, - fileID: StaticString = #fileID, - filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column - ) { - assertInlineSnapshot( - of: """ - HTTP/1.1 200 OK - Content-Type: text/html; charset=utf-8 - """, - as: .lines, - message: "Head did not match", - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "head", - trailingClosureOffset: 1 - ), - matches: head, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) - assertInlineSnapshot( - of: """ - - - - - Point-Free - - - -

What's the point?

- - - """, - as: .lines, - message: "Body did not match", - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "body", - trailingClosureOffset: 2 - ), - matches: body, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) + @Test func testArgumentlessInlineSnapshot() throws { + func assertArgumentlessInlineSnapshot( + expected: (@Sendable () -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) throws { + try assertInline( + of: "Hello", + as: .customDump, + closureDescriptor: SnapshotClosureDescriptor(), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + try assertArgumentlessInlineSnapshot { + """ + "Hello" + """ + } } - assertResponse { - """ - https://www.pointfree.co/ - """ - } head: { - """ - HTTP/1.1 200 OK - Content-Type: text/html; charset=utf-8 - """ - } body: { - """ - - - - - Point-Free - - - -

What's the point?

- - - """ + @Test func multipleInlineSnapshots() async throws { + func assertResponse( + of url: @Sendable () -> String, + head: (@Sendable () -> String)? = nil, + body: (@Sendable () -> String)? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) throws { + try assertInline( + of: """ + HTTP/1.1 200 OK + Content-Type: text/html; charset=utf-8 + """, + as: .lines, + message: "Head did not match", + name: "head", + closureDescriptor: SnapshotClosureDescriptor( + trailingClosureLabel: "head", + trailingClosureOffset: 1 + ), + matches: head, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + try assertInline( + of: """ + + + + + Point-Free + + + +

What's the point?

+ + + """, + as: .lines, + message: "Body did not match", + name: "body", + closureDescriptor: SnapshotClosureDescriptor( + trailingClosureLabel: "body", + trailingClosureOffset: 2 + ), + matches: body, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + try assertResponse { + """ + https://www.pointfree.co/ + """ + } head: { + """ + HTTP/1.1 200 OK + Content-Type: text/html; charset=utf-8 + """ + } body: { + """ + + + + + Point-Free + + + +

What's the point?

+ + + """ + } } - } - @Test func asyncThrowing() async throws { - func assertAsyncThrowingInlineSnapshot( - of value: () -> String, - is expected: (() -> String)? = nil, - fileID: StaticString = #fileID, - filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column - ) async throws { - assertInlineSnapshot( - of: value(), - as: .dump, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "is", - trailingClosureOffset: 1 - ), - matches: expected, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) + @Test func asyncThrowing() async throws { + func assertAsyncThrowingInlineSnapshot( + of value: @Sendable () -> String, + is expected: (@Sendable () -> String)? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) throws { + try assertInline( + of: value(), + as: .customDump, + closureDescriptor: SnapshotClosureDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + try assertAsyncThrowingInlineSnapshot { + "Hello" + } is: { + """ + "Hello" + """ + } } - try await assertAsyncThrowingInlineSnapshot { - "Hello" - } is: { - """ - - "Hello" - - """ - } - } - - @Test func nestedInClosureFunction() { - func withDependencies(operation: () -> Void) { - operation() - } - - withDependencies { - assertInlineSnapshot(of: "Hello", as: .dump) { - """ - - "Hello" - - """ - } + @Test func nestedInClosureFunction() async throws { + func withDependencies(operation: @Sendable () throws -> Void) rethrows { + try operation() + } + + try withDependencies { + try assertInline(of: "Hello", as: .customDump) { + """ + "Hello" + """ + } + } } - } - @Test func carriageReturnInlineSnapshot() { - assertInlineSnapshot(of: "This is a line\r\nAnd this is a line\r\n", as: .lines) { - """ - This is a line\r - And this is a line\r + @Test func carriageReturnInlineSnapshot() async throws { + try assertInline(of: "This is a line\r\nAnd this is a line\r\n", as: .lines) { + """ + This is a line\r + And this is a line\r - """ + """ + } } - } - @Test func carriageReturnRawInlineSnapshot() { - assertInlineSnapshot(of: "\"\"\"#This is a line\r\nAnd this is a line\r\n", as: .lines) { - ##""" - """#This is a line\##r - And this is a line\##r + @Test func carriageReturnRawInlineSnapshot() async throws { + try assertInline(of: "\"\"\"#This is a line\r\nAnd this is a line\r\n", as: .lines) { + ##""" + """#This is a line\##r + And this is a line\##r - """## + """## + } } - } } - } +} - private func assertCustomInlineSnapshot( - of value: () -> String, - is expected: (() -> String)? = nil, +private func assertCustomInlineSnapshot( + of value: @Sendable () -> String, + is expected: (@Sendable () -> String)? = nil, fileID: StaticString = #fileID, filePath: StaticString = #filePath, function: StaticString = #function, line: UInt = #line, column: UInt = #column - ) { - assertInlineSnapshot( - of: value(), - as: .dump, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "is", - trailingClosureOffset: 1 - ), - matches: expected, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column +) throws { + try assertInline( + of: value(), + as: .customDump, + closureDescriptor: SnapshotClosureDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column ) - } +} #endif diff --git a/Tests/InlineSnapshotTestingTests/CustomDumpTests.swift b/Tests/InlineSnapshotTestingTests/CustomDumpTests.swift index 1752d03c1..5c8884735 100644 --- a/Tests/InlineSnapshotTestingTests/CustomDumpTests.swift +++ b/Tests/InlineSnapshotTestingTests/CustomDumpTests.swift @@ -1,23 +1,23 @@ #if canImport(Testing) - import Testing - import InlineSnapshotTesting - import SnapshotTestingCustomDump +import Testing +import InlineSnapshotTesting +import SnapshotTestingCustomDump - extension BaseSuite { +extension BaseSuite { struct CustomDumpSnapshotTests { - @Test func basics() { - struct User { let id: Int, name: String, bio: String } - let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") - assertInlineSnapshot(of: user, as: .customDump) { - """ - BaseSuite.CustomDumpSnapshotTests.User( - id: 1, - name: "Blobby", - bio: "Blobbed around the world." - ) - """ + @Test func basics() throws { + struct User { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + try assertInline(of: user, as: .customDump) { + """ + BaseSuite.CustomDumpSnapshotTests.User( + id: 1, + name: "Blobby", + bio: "Blobbed around the world." + ) + """ + } } - } } - } +} #endif diff --git a/Tests/InlineSnapshotTestingTests/InlineSnapshotTesting.xctestplan b/Tests/InlineSnapshotTestingTests/InlineSnapshotTesting.xctestplan deleted file mode 100644 index 66bff19a7..000000000 --- a/Tests/InlineSnapshotTestingTests/InlineSnapshotTesting.xctestplan +++ /dev/null @@ -1,24 +0,0 @@ -{ - "configurations" : [ - { - "id" : "54C128DD-A06E-4F40-83B2-00CB6C7027E8", - "name" : "Configuration 1", - "options" : { - - } - } - ], - "defaultOptions" : { - - }, - "testTargets" : [ - { - "target" : { - "containerPath" : "container:", - "identifier" : "InlineSnapshotTestingTests", - "name" : "InlineSnapshotTestingTests" - } - } - ], - "version" : 1 -} diff --git a/Tests/InlineSnapshotTestingTests/InlineSnapshotTestingTests.swift b/Tests/InlineSnapshotTestingTests/InlineSnapshotTestingTests.swift index 8c23689e6..1909e19eb 100644 --- a/Tests/InlineSnapshotTestingTests/InlineSnapshotTestingTests.swift +++ b/Tests/InlineSnapshotTestingTests/InlineSnapshotTestingTests.swift @@ -1,372 +1,352 @@ import Foundation -@_spi(Internals) import InlineSnapshotTesting import SnapshotTesting + +#if canImport(XCTest) import XCTest -final class InlineSnapshotTestingTests: BaseTestCase { - func testInlineSnapshot() { - assertInlineSnapshot(of: ["Hello", "World"], as: .dump) { - """ - ▿ 2 elements - - "Hello" - - "World" +@testable import InlineSnapshotTesting - """ +final class InlineSnapshotTestingTests: BaseTestCase { + func testInlineSnapshot() throws { + try assertInline(of: ["Hello", "World"], as: .customDump) { + """ + [ + [0]: "Hello", + [1]: "World" + ] + """ + } } - } - - func testInlineSnapshot_NamedTrailingClosure() { - assertInlineSnapshot( - of: ["Hello", "World"], as: .dump, - matches: { - """ - ▿ 2 elements - - "Hello" - - "World" - """ - }) - } - - func testInlineSnapshot_Escaping() { - assertInlineSnapshot(of: "Hello\"\"\"#, world", as: .lines) { - ##""" - Hello"""#, world - """## + func testInlineSnapshot_NamedTrailingClosure() throws { + try assertInline( + of: ["Hello", "World"], + as: .customDump, + matches: { + """ + [ + [0]: "Hello", + [1]: "World" + ] + """ + } + ) } - } - - func testCustomInlineSnapshot() { - assertCustomInlineSnapshot { - "Hello" - } is: { - """ - - "Hello" - """ + func testInlineSnapshot_Escaping() throws { + try assertInline(of: "Hello\"\"\"#, world", as: .lines) { + ##""" + Hello"""#, world + """## + } } - } - func testCustomInlineSnapshot_Multiline() { - assertCustomInlineSnapshot { - """ - "Hello" - "World" - """ - } is: { - #""" - - "\"Hello\"\n\"World\"" - - """# + func testCustomInlineSnapshot() throws { + try assertCustomInlineSnapshot { + "Hello" + } is: { + """ + "Hello" + """ + } } - } - - func testCustomInlineSnapshot_SingleTrailingClosure() { - assertCustomInlineSnapshot(of: { "Hello" }) { - """ - - "Hello" - """ + func testCustomInlineSnapshot_Multiline() throws { + try assertCustomInlineSnapshot { + """ + "Hello" + "World" + """ + } is: { + #""" + """ + "Hello" + "World" + """ + """# + } } - } - - func testCustomInlineSnapshot_MultilineSingleTrailingClosure() { - assertCustomInlineSnapshot( - of: { "Hello" } - ) { - """ - - "Hello" - """ + func testCustomInlineSnapshot_SingleTrailingClosure() throws { + try assertCustomInlineSnapshot(of: { "Hello" }) { + """ + "Hello" + """ + } } - } - - func testCustomInlineSnapshot_NoTrailingClosure() { - assertCustomInlineSnapshot( - of: { "Hello" }, - is: { - """ - - "Hello" - """ - } - ) - } - - func testArgumentlessInlineSnapshot() { - func assertArgumentlessInlineSnapshot( - expected: (() -> String)? = nil, - fileID: StaticString = #fileID, - file filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column - ) { - assertInlineSnapshot( - of: "Hello", - as: .dump, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "is", - trailingClosureOffset: 1 - ), - matches: expected, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) + func testCustomInlineSnapshot_MultilineSingleTrailingClosure() throws { + try assertCustomInlineSnapshot( + of: { "Hello" } + ) { + """ + "Hello" + """ + } } - assertArgumentlessInlineSnapshot { - """ - - "Hello" - - """ + func testCustomInlineSnapshot_NoTrailingClosure() throws { + try assertCustomInlineSnapshot( + of: { "Hello" }, + is: { + """ + "Hello" + """ + } + ) } - } - func testMultipleInlineSnapshots() { - func assertResponse( - of url: () -> String, - head: (() -> String)? = nil, - body: (() -> String)? = nil, - fileID: StaticString = #fileID, - file filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column - ) { - assertInlineSnapshot( - of: """ - HTTP/1.1 200 OK - Content-Type: text/html; charset=utf-8 - """, - as: .lines, - message: "Head did not match", - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "head", - trailingClosureOffset: 1 - ), - matches: head, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) - assertInlineSnapshot( - of: """ - - - - - Point-Free - - - -

What's the point?

- - - """, - as: .lines, - message: "Body did not match", - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "body", - trailingClosureOffset: 2 - ), - matches: body, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) - } - - assertResponse { - """ - https://www.pointfree.co/ - """ - } head: { - """ - HTTP/1.1 200 OK - Content-Type: text/html; charset=utf-8 - """ - } body: { - """ - - - - - Point-Free - - - -

What's the point?

- - - """ - } - } + func testArgumentlessInlineSnapshot() throws { + func assertArgumentlessInlineSnapshot( + expected: (@Sendable () -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) throws { + try assertInline( + of: "Hello", + as: .customDump, + closureDescriptor: SnapshotClosureDescriptor(), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } - func testAsyncThrowing() async throws { - func assertAsyncThrowingInlineSnapshot( - of value: () -> String, - is expected: (() -> String)? = nil, - fileID: StaticString = #fileID, - file filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column - ) async throws { - assertInlineSnapshot( - of: value(), - as: .dump, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "is", - trailingClosureOffset: 1 - ), - matches: expected, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) + try assertArgumentlessInlineSnapshot { + """ + "Hello" + """ + } } - try await assertAsyncThrowingInlineSnapshot { - "Hello" - } is: { - """ - - "Hello" + func testMultipleInlineSnapshots() throws { + func assertResponse( + of url: @Sendable () -> String, + head: (@Sendable () -> String)? = nil, + body: (@Sendable () -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) throws { + try assertInline( + of: """ + HTTP/1.1 200 OK + Content-Type: text/html; charset=utf-8 + """, + as: .lines, + message: "Head did not match", + name: "head", + closureDescriptor: SnapshotClosureDescriptor( + trailingClosureLabel: "head", + trailingClosureOffset: 1 + ), + matches: head, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + try assertInline( + of: """ + + + + + Point-Free + + + +

What's the point?

+ + + """, + as: .lines, + message: "Body did not match", + name: "body", + closureDescriptor: SnapshotClosureDescriptor( + trailingClosureLabel: "body", + trailingClosureOffset: 2 + ), + matches: body, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } - """ + try assertResponse { + """ + https://www.pointfree.co/ + """ + } head: { + """ + HTTP/1.1 200 OK + Content-Type: text/html; charset=utf-8 + """ + } body: { + """ + + + + + Point-Free + + + +

What's the point?

+ + + """ + } } - } - func testNestedInClosureFunction() { - func withDependencies(operation: () -> Void) { - operation() + func testAsyncThrowing() throws { + func assertAsyncThrowingInlineSnapshot( + of value: @Sendable () -> String, + is expected: (@Sendable () -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column + ) throws { + try assertInline( + of: value(), + as: .customDump, + closureDescriptor: SnapshotClosureDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) + } + + try assertAsyncThrowingInlineSnapshot { + "Hello" + } is: { + """ + "Hello" + """ + } } - withDependencies { - assertInlineSnapshot(of: "Hello", as: .dump) { - """ - - "Hello" + func testNestedInClosureFunction() throws { + func withDependencies(operation: @Sendable () throws -> Void) rethrows { + try operation() + } - """ - } + try withDependencies { + try assertInline(of: "Hello", as: .customDump) { + """ + "Hello" + """ + } + } } - } - func testCarriageReturnInlineSnapshot() { - assertInlineSnapshot(of: "This is a line\r\nAnd this is a line\r\n", as: .lines) { - """ - This is a line\r - And this is a line\r + func testCarriageReturnInlineSnapshot() throws { + try assertInline(of: "This is a line\r\nAnd this is a line\r\n", as: .lines) { + """ + This is a line\r + And this is a line\r - """ + """ + } } - } - func testCarriageReturnRawInlineSnapshot() { - assertInlineSnapshot(of: "\"\"\"#This is a line\r\nAnd this is a line\r\n", as: .lines) { - ##""" - """#This is a line\##r - And this is a line\##r + func testCarriageReturnRawInlineSnapshot() throws { + try assertInline(of: "\"\"\"#This is a line\r\nAnd this is a line\r\n", as: .lines) { + ##""" + """#This is a line\##r + And this is a line\##r - """## + """## + } } - } - #if canImport(Darwin) + #if canImport(Darwin) func testRecordFailed_IncorrectExpectation() throws { - let initialInlineSnapshotState = inlineSnapshotState - defer { inlineSnapshotState = initialInlineSnapshotState } - - XCTExpectFailure { - withSnapshotTesting(record: .failed) { - assertInlineSnapshot(of: 42, as: .json) { - """ - 4 - """ - } + try XCTExpectFailure { + try withTestingEnvironment(record: .never) { + try assertInline(of: 42, as: .json) { + """ + 4 + """ + } + } + } issueMatcher: { + $0.compactDescription == """ + failed - Snapshot does not match reference. Difference: … + + @@ −1,1 +1,1 @@ + −4 + +42 + """ } - } issueMatcher: { - $0.compactDescription == """ - failed - Snapshot did not match. Difference: … - @@ −1,1 +1,1 @@ - −4 - +42 + let records = InlineSnapshotManager.current.records(for: #filePath) - A new snapshot was automatically recorded. - """ - } - - XCTAssertEqual(inlineSnapshotState.count, 1) - XCTAssertEqual( - String(describing: inlineSnapshotState.keys.first!.path) - .hasSuffix("InlineSnapshotTestingTests.swift"), - true - ) + XCTAssertTrue(records.contains { $0.function == #function && !$0.wasRecording }) } - #endif + #endif - #if canImport(Darwin) + #if canImport(Darwin) func testRecordFailed_MissingExpectation() throws { - let initialInlineSnapshotState = inlineSnapshotState - defer { inlineSnapshotState = initialInlineSnapshotState } - - XCTExpectFailure { - withSnapshotTesting(record: .failed) { - assertInlineSnapshot(of: 42, as: .json) + try XCTExpectFailure { + try withTestingEnvironment(record: .failed) { + try assertInline(of: 42, as: .json) + } + } issueMatcher: { + $0.compactDescription == """ + failed - No reference was found on disk. New snapshot was not recorded because recording is disabled + """ } - } issueMatcher: { - $0.compactDescription == """ - failed - Automatically recorded a new snapshot. Difference: … - - @@ −1,1 +1,1 @@ - − - +42 - Re-run "testRecordFailed_MissingExpectation()" to assert against the newly-recorded snapshot. - """ - } + let records = InlineSnapshotManager.current.records(for: #filePath) - XCTAssertEqual(inlineSnapshotState.count, 1) - XCTAssertEqual( - String(describing: inlineSnapshotState.keys.first!.path) - .hasSuffix("InlineSnapshotTestingTests.swift"), - true - ) + XCTAssertTrue(records.contains { $0.function == #function && !$0.wasRecording }) } - #endif + #endif } private func assertCustomInlineSnapshot( - of value: () -> String, - is expected: (() -> String)? = nil, - fileID: StaticString = #fileID, - file filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column -) { - assertInlineSnapshot( - of: value(), - as: .dump, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "is", - trailingClosureOffset: 1 - ), - matches: expected, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) + of value: @Sendable () -> String, + is expected: (@Sendable () -> String)? = nil, + fileID: StaticString = #fileID, + file filePath: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line, + column: UInt = #column +) throws { + try assertInline( + of: value(), + as: .customDump, + closureDescriptor: SnapshotClosureDescriptor( + trailingClosureLabel: "is", + trailingClosureOffset: 1 + ), + matches: expected, + fileID: fileID, + file: filePath, + function: function, + line: line, + column: column + ) } +#endif diff --git a/Tests/InlineSnapshotTestingTests/Internal/BaseSuite.swift b/Tests/InlineSnapshotTestingTests/Internal/BaseSuite.swift index 8de25191e..bc0a6b4f3 100644 --- a/Tests/InlineSnapshotTestingTests/Internal/BaseSuite.swift +++ b/Tests/InlineSnapshotTestingTests/Internal/BaseSuite.swift @@ -1,8 +1,7 @@ #if canImport(Testing) - import Testing - import SnapshotTesting +import Testing +import SnapshotTesting - @Suite(.snapshots(record: .failed, diffTool: .ksdiff)) - struct BaseSuite { - } +@Suite(.record(.failed), .diffTool(.ksdiff), .finalizeSnapshots) +struct BaseSuite {} #endif diff --git a/Tests/InlineSnapshotTestingTests/Internal/BaseTestCase.swift b/Tests/InlineSnapshotTestingTests/Internal/BaseTestCase.swift index c5edb5f85..3c65808ea 100644 --- a/Tests/InlineSnapshotTestingTests/Internal/BaseTestCase.swift +++ b/Tests/InlineSnapshotTestingTests/Internal/BaseTestCase.swift @@ -1,13 +1,16 @@ import SnapshotTesting + +#if canImport(XCTest) import XCTest class BaseTestCase: XCTestCase { - override func invokeTest() { - withSnapshotTesting( - record: .failed, - diffTool: .ksdiff - ) { - super.invokeTest() + override func invokeTest() { + withTestingEnvironment( + record: .failed, + diffTool: .ksdiff + ) { + super.invokeTest() + } } - } } +#endif diff --git a/Tests/SnapshotTestingCustomDumpTests/CustomDumpTests.swift b/Tests/SnapshotTestingCustomDumpTests/CustomDumpTests.swift new file mode 100644 index 000000000..2b82c9675 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/CustomDumpTests.swift @@ -0,0 +1,79 @@ +import SnapshotTestingCustomDump +import XCSnapshotTesting + +#if canImport(XCTest) +import XCTest + +class CustomDumpTests: XCTestCase { + + override func invokeTest() { + withTestingEnvironment( + record: .failed, + diffTool: .ksdiff, + platform: "" + ) { + super.invokeTest() + } + } + + func testAny() throws { + struct User { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + try assert( + of: user, + as: .customDump + ) + } + + func testRecursion() throws { + try withTestingEnvironment { + final class Father { + var child: Child? + init(_ child: Child? = nil) { self.child = child } + } + final class Child { + let father: Father + init(_ father: Father) { + self.father = father + father.child = self + } + } + let father = Father() + let child = Child(father) + try assert(of: father, as: .customDump) + try assert(of: child, as: .customDump) + } + } + + func testAnySnapshotStringConvertible() throws { + try assert(of: "a" as Character, as: .customDump, named: "character") + try assert(of: Data("Hello, world!".utf8), as: .customDump, named: "data") + try assert(of: Date(timeIntervalSinceReferenceDate: 0), as: .customDump, named: "date") + try assert(of: NSObject(), as: .customDump, named: "nsobject") + try assert(of: "Hello, world!", as: .customDump, named: "string") + try assert(of: "Hello, world!".dropLast(8), as: .customDump, named: "substring") + try assert(of: URL(string: "https://www.pointfree.co")!, as: .customDump, named: "url") + } + + func testDeterministicDictionaryAndSetSnapshots() throws { + struct Person: Hashable { let name: String } + struct DictionarySetContainer { let dict: [String: Int], set: Set } + let set = DictionarySetContainer( + dict: ["c": 3, "a": 1, "b": 2], + set: [.init(name: "Brandon"), .init(name: "Stephen")] + ) + try assert(of: set, as: .customDump) + } + + func testMultipleSnapshots() throws { + try assert(of: [1], as: .customDump) + try assert(of: [1, 2], as: .customDump) + } + + func testNamedAssertion() throws { + struct User { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + try assert(of: user, as: .customDump, named: "named") + } +} +#endif diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAny.1.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAny.1.txt new file mode 100644 index 000000000..36fb0b76f --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAny.1.txt @@ -0,0 +1,5 @@ +CustomDumpTests.User( + id: 1, + name: "Blobby", + bio: "Blobbed around the world." +) \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.character.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.character.txt new file mode 100644 index 000000000..075c842d1 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.character.txt @@ -0,0 +1 @@ +"a" \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.data.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.data.txt new file mode 100644 index 000000000..90bb384a8 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.data.txt @@ -0,0 +1 @@ +Data(13 bytes) \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.date.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.date.txt new file mode 100644 index 000000000..7365a6a25 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.date.txt @@ -0,0 +1 @@ +Date(2001-01-01T00:00:00.000Z) \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.nsobject.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.nsobject.txt new file mode 100644 index 000000000..7c8a7137f --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.nsobject.txt @@ -0,0 +1 @@ +NSObject() \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.string.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.string.txt new file mode 100644 index 000000000..ea7e69739 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.string.txt @@ -0,0 +1 @@ +"Hello, world!" \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.substring.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.substring.txt new file mode 100644 index 000000000..5638d8ba2 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.substring.txt @@ -0,0 +1 @@ +"Hello" \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.url.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.url.txt new file mode 100644 index 000000000..79d88da86 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testAnySnapshotStringConvertible.url.txt @@ -0,0 +1 @@ +URL(https://www.pointfree.co) \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testDeterministicDictionaryAndSetSnapshots.1.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testDeterministicDictionaryAndSetSnapshots.1.txt new file mode 100644 index 000000000..216607b70 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testDeterministicDictionaryAndSetSnapshots.1.txt @@ -0,0 +1,11 @@ +CustomDumpTests.DictionarySetContainer( + dict: [ + "a": 1, + "b": 2, + "c": 3 + ], + set: Set([ + CustomDumpTests.Person(name: "Brandon"), + CustomDumpTests.Person(name: "Stephen") + ]) +) \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testMultipleSnapshots.1.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testMultipleSnapshots.1.txt new file mode 100644 index 000000000..645d33e42 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testMultipleSnapshots.1.txt @@ -0,0 +1,3 @@ +[ + [0]: 1 +] \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testMultipleSnapshots.2.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testMultipleSnapshots.2.txt new file mode 100644 index 000000000..00072c28a --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testMultipleSnapshots.2.txt @@ -0,0 +1,4 @@ +[ + [0]: 1, + [1]: 2 +] \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testNamedAssertion.named.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testNamedAssertion.named.txt new file mode 100644 index 000000000..36fb0b76f --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testNamedAssertion.named.txt @@ -0,0 +1,5 @@ +CustomDumpTests.User( + id: 1, + name: "Blobby", + bio: "Blobbed around the world." +) \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testRecursion.1.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testRecursion.1.txt new file mode 100644 index 000000000..15e7ed7f2 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testRecursion.1.txt @@ -0,0 +1,5 @@ +CustomDumpTests.Father( + child: CustomDumpTests.Child( + father: CustomDumpTests.Father(↩︎) + ) +) \ No newline at end of file diff --git a/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testRecursion.2.txt b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testRecursion.2.txt new file mode 100644 index 000000000..92a19c1c7 --- /dev/null +++ b/Tests/SnapshotTestingCustomDumpTests/__Snapshots__/CustomDumpTests/testRecursion.2.txt @@ -0,0 +1,5 @@ +CustomDumpTests.Child( + father: CustomDumpTests.Father( + child: CustomDumpTests.Child(↩︎) + ) +) \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift b/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift deleted file mode 100644 index d1889c28e..000000000 --- a/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift +++ /dev/null @@ -1,24 +0,0 @@ -#if canImport(Testing) - import Testing - import Foundation - import SnapshotTesting - - extension BaseSuite { - struct AssertSnapshotTests { - @Test(.snapshots(record: .missing)) func dump() { - struct User { let id: Int, name: String, bio: String } - let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") - assertSnapshot(of: user, as: .dump) - } - } - - @MainActor - struct MainActorTests { - @Test func dump() { - struct User { let id: Int, name: String, bio: String } - let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") - assertSnapshot(of: user, as: .dump) - } - } - } -#endif diff --git a/Tests/SnapshotTestingTests/DeprecationTests.swift b/Tests/SnapshotTestingTests/DeprecationTests.swift deleted file mode 100644 index f3725a65a..000000000 --- a/Tests/SnapshotTestingTests/DeprecationTests.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SnapshotTesting -import XCTest - -final class DeprecationTests: XCTestCase { - @available(*, deprecated) - func testIsRecordingProxy() { - SnapshotTesting.record = true - XCTAssertEqual(isRecording, true) - - SnapshotTesting.record = false - XCTAssertEqual(isRecording, false) - } -} diff --git a/Tests/SnapshotTestingTests/Internal/BaseSuite.swift b/Tests/SnapshotTestingTests/Internal/BaseSuite.swift index 8de25191e..a9cbc5e4d 100644 --- a/Tests/SnapshotTestingTests/Internal/BaseSuite.swift +++ b/Tests/SnapshotTestingTests/Internal/BaseSuite.swift @@ -1,8 +1,8 @@ #if canImport(Testing) - import Testing - import SnapshotTesting +import Testing +import SnapshotTesting - @Suite(.snapshots(record: .failed, diffTool: .ksdiff)) - struct BaseSuite { - } +@Suite(.record(.failed), .diffTool(.ksdiff), .platform(nil)) +struct BaseSuite { +} #endif diff --git a/Tests/SnapshotTestingTests/Internal/BaseTestCase.swift b/Tests/SnapshotTestingTests/Internal/BaseTestCase.swift deleted file mode 100644 index c5edb5f85..000000000 --- a/Tests/SnapshotTestingTests/Internal/BaseTestCase.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SnapshotTesting -import XCTest - -class BaseTestCase: XCTestCase { - override func invokeTest() { - withSnapshotTesting( - record: .failed, - diffTool: .ksdiff - ) { - super.invokeTest() - } - } -} diff --git a/Tests/SnapshotTestingTests/Internal/TestHelpers.swift b/Tests/SnapshotTestingTests/Internal/TestHelpers.swift deleted file mode 100644 index d71c855f1..000000000 --- a/Tests/SnapshotTestingTests/Internal/TestHelpers.swift +++ /dev/null @@ -1,110 +0,0 @@ -import XCTest - -@testable import SnapshotTesting - -#if os(iOS) - let platform = "ios" -#elseif os(tvOS) - let platform = "tvos" -#elseif os(macOS) - let platform = "macos" - extension NSTextField { - var text: String { - get { return self.stringValue } - set { self.stringValue = newValue } - } - } -#endif - -#if os(macOS) || os(iOS) || os(tvOS) - extension CGPath { - /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: - static var heart: CGPath { - let scale: CGFloat = 30.0 - let path = CGMutablePath() - - path.move(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) - path.addLine(to: CGPoint(x: 0.0 * scale, y: 2.0 * scale)) - path.addQuadCurve( - to: CGPoint(x: 1.0 * scale, y: 3.0 * scale), - control: CGPoint(x: 0.125 * scale, y: 2.875 * scale) - ) - path.addQuadCurve( - to: CGPoint(x: 2.0 * scale, y: 2.0 * scale), - control: CGPoint(x: 1.875 * scale, y: 2.875 * scale) - ) - path.addCurve( - to: CGPoint(x: 3.0 * scale, y: 1.0 * scale), - control1: CGPoint(x: 2.5 * scale, y: 2.0 * scale), - control2: CGPoint(x: 3.0 * scale, y: 1.5 * scale) - ) - path.addCurve( - to: CGPoint(x: 2.0 * scale, y: 0.0 * scale), - control1: CGPoint(x: 3.0 * scale, y: 0.5 * scale), - control2: CGPoint(x: 2.5 * scale, y: 0.0 * scale) - ) - path.addLine(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) - path.closeSubpath() - - path.addEllipse( - in: CGRect( - origin: CGPoint(x: 2.0 * scale, y: 2.0 * scale), - size: CGSize(width: scale, height: scale) - )) - - return path - } - } -#endif - -#if os(iOS) || os(tvOS) - extension UIBezierPath { - /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: - static var heart: UIBezierPath { - UIBezierPath(cgPath: .heart) - } - } -#endif - -#if os(macOS) - extension NSBezierPath { - /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: - static var heart: NSBezierPath { - let scale: CGFloat = 30.0 - let path = NSBezierPath() - - path.move(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) - path.line(to: CGPoint(x: 0.0 * scale, y: 2.0 * scale)) - path.curve( - to: CGPoint(x: 1.0 * scale, y: 3.0 * scale), - controlPoint1: CGPoint(x: 0.0 * scale, y: 2.5 * scale), - controlPoint2: CGPoint(x: 0.5 * scale, y: 3.0 * scale) - ) - path.curve( - to: CGPoint(x: 2.0 * scale, y: 2.0 * scale), - controlPoint1: CGPoint(x: 1.5 * scale, y: 3.0 * scale), - controlPoint2: CGPoint(x: 2.0 * scale, y: 2.5 * scale) - ) - path.curve( - to: CGPoint(x: 3.0 * scale, y: 1.0 * scale), - controlPoint1: CGPoint(x: 2.5 * scale, y: 2.0 * scale), - controlPoint2: CGPoint(x: 3.0 * scale, y: 1.5 * scale) - ) - path.curve( - to: CGPoint(x: 2.0 * scale, y: 0.0 * scale), - controlPoint1: CGPoint(x: 3.0 * scale, y: 0.5 * scale), - controlPoint2: CGPoint(x: 2.5 * scale, y: 0.0 * scale) - ) - path.line(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) - path.close() - - path.appendOval( - in: CGRect( - origin: CGPoint(x: 2.0 * scale, y: 2.0 * scale), - size: CGSize(width: scale, height: scale) - )) - - return path - } - } -#endif diff --git a/Tests/SnapshotTestingTests/RecordTests.swift b/Tests/SnapshotTestingTests/RecordTests.swift index c6a54abc1..34d9dc029 100644 --- a/Tests/SnapshotTestingTests/RecordTests.swift +++ b/Tests/SnapshotTestingTests/RecordTests.swift @@ -1,207 +1,269 @@ +import Foundation import SnapshotTesting -import XCTest - -class RecordTests: BaseTestCase { - var snapshotURL: URL! - - override func setUp() { - super.setUp() - - let testName = String( - self.name - .split(separator: " ") - .flatMap { String($0).split(separator: ".") } - .last! - ) - .prefix(while: { $0 != "]" }) - let fileURL = URL(fileURLWithPath: #file, isDirectory: false) - let testClassName = fileURL.deletingPathExtension().lastPathComponent - let testDirectory = - fileURL - .deletingLastPathComponent() - .appendingPathComponent("__Snapshots__") - .appendingPathComponent(testClassName) - snapshotURL = - testDirectory - .appendingPathComponent("\(testName).1.json") - try? FileManager.default - .removeItem(at: snapshotURL.deletingLastPathComponent()) - try? FileManager.default - .createDirectory(at: testDirectory, withIntermediateDirectories: true) - } - - override func tearDown() { - super.tearDown() - try? FileManager.default - .removeItem(at: snapshotURL.deletingLastPathComponent()) - } - - #if canImport(Darwin) - func testRecordNever() { - XCTExpectFailure { - withSnapshotTesting(record: .never) { - assertSnapshot(of: 42, as: .json) +import Testing + +extension BaseSuite { + + final class RecordTests { + + let fileURL: URL = { + let fileURL = URL(fileURLWithPath: #filePath) + .deletingPathExtension() + + return + fileURL + .deletingLastPathComponent() + .appendingPathComponent("__Snapshots__") + .appendingPathComponent(fileURL.lastPathComponent) + }() + + #if canImport(Darwin) + func recordNever() async throws { + try withKnownSnapshotURL { snapshotURL in + try withKnownIssue { + try withTestingEnvironment(record: .never) { + try assert(of: 42, as: .json) + } + } matching: { + $0.comments.first?.rawValue ?? "" == """ + No reference was found on disk. New snapshot was not recorded because recording is disabled + """ + } + + #expect(!FileManager.default.fileExists(atPath: snapshotURL.path)) + } } - } issueMatcher: { - $0.compactDescription == """ - failed - No reference was found on disk. New snapshot was not recorded because recording is disabled - """ - } - - XCTAssertEqual( - FileManager.default.fileExists(atPath: snapshotURL.path), - false - ) - } - #endif + #endif + + #if canImport(Darwin) + @Test + func recordMissing() async throws { + try withKnownSnapshotURL { snapshotURL in + try withKnownIssue { + try withTestingEnvironment(record: .missing) { + try assert(of: 42, as: .json) + } + } matching: { + ($0.comments.first?.rawValue ?? "").hasPrefix( + """ + No reference was found on disk. Automatically recorded snapshot: … + + open "\(snapshotURL.absoluteString)" - #if canImport(Darwin) - func testRecordMissing() { - XCTExpectFailure { - withSnapshotTesting(record: .missing) { - assertSnapshot(of: 42, as: .json) + Re-run "\(#function)" to assert against the newly-recorded snapshot. + """ + ) + } + + try #expect(String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self) == "42") + } } - } issueMatcher: { - $0.compactDescription.hasPrefix( - """ - failed - No reference was found on disk. Automatically recorded snapshot: … - """) - } - - try XCTAssertEqual( - String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), - "42" - ) - } - #endif + #endif - #if canImport(Darwin) - func testRecordMissing_ExistingFile() throws { - try Data("999".utf8).write(to: snapshotURL) + #if canImport(Darwin) + @Test + func recordMissing_ExistingFile() async throws { + try withKnownSnapshotURL { snapshotURL in + try Data("999".utf8).write(to: snapshotURL) - XCTExpectFailure { - withSnapshotTesting(record: .missing) { - assertSnapshot(of: 42, as: .json) + try withKnownIssue { + try withTestingEnvironment(record: .missing) { + try assert(of: 42, as: .json) + } + } matching: { + ($0.comments.first?.rawValue ?? "").hasPrefix( + """ + Snapshot "\(#function)" does not match reference. + + ksdiff "\(snapshotURL.absoluteString)" + """ + ) + } + + try #expect(String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self) == "999") + } } - } issueMatcher: { - $0.compactDescription.hasPrefix( - """ - failed - Snapshot does not match reference. - """) - } - - try XCTAssertEqual( - String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), - "999" - ) - } - #endif + #endif - #if canImport(Darwin) - func testRecordAll_Fresh() throws { - XCTExpectFailure { - withSnapshotTesting(record: .all) { - assertSnapshot(of: 42, as: .json) + #if canImport(Darwin) + @Test + func recordAll_Fresh() async throws { + try withKnownSnapshotURL { snapshotURL in + try withKnownIssue { + try withTestingEnvironment(record: .all) { + try assert(of: 42, as: .json) + } + } matching: { + ($0.comments.first?.rawValue ?? "").hasPrefix( + """ + Record mode is on. Automatically recorded snapshot: … + + open "\(snapshotURL.absoluteString)" + """ + ) + } + + try #expect(String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self) == "42") + } } - } issueMatcher: { - $0.compactDescription.hasPrefix( - """ - failed - Record mode is on. Automatically recorded snapshot: … - """) - } - - try XCTAssertEqual( - String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), - "42" - ) - } - #endif + #endif - #if canImport(Darwin) - func testRecordAll_Overwrite() throws { - try Data("999".utf8).write(to: snapshotURL) + #if canImport(Darwin) + @Test + func recordAll_Overwrite() async throws { + try withKnownSnapshotURL { snapshotURL in + try Data("999".utf8).write(to: snapshotURL) - XCTExpectFailure { - withSnapshotTesting(record: .all) { - assertSnapshot(of: 42, as: .json) + try withKnownIssue { + try withTestingEnvironment(record: .all) { + try assert(of: 42, as: .json) + } + } matching: { + ($0.comments.first?.rawValue ?? "").hasPrefix( + """ + Record mode is on. Automatically recorded snapshot: … + + open "\(snapshotURL.absoluteString)" + """ + ) + } + + try #expect(String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self) == "42") + } } - } issueMatcher: { - $0.compactDescription.hasPrefix( - """ - failed - Record mode is on. Automatically recorded snapshot: … - """) - } - - try XCTAssertEqual( - String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), - "42" - ) - } - #endif + #endif + + #if canImport(Darwin) + @Test + func recordFailed_WhenFailure() async throws { + try withKnownSnapshotURL { snapshotURL in + try Data("999".utf8).write(to: snapshotURL) + + try withKnownIssue { + try withTestingEnvironment(record: .failed) { + try assert(of: 42, as: .json) + } + } matching: { + ($0.comments.first?.rawValue ?? "").hasPrefix( + """ + Snapshot "\(#function)" does not match reference. A new snapshot was automatically recorded. - #if canImport(Darwin) - func testRecordFailed_WhenFailure() throws { - try Data("999".utf8).write(to: snapshotURL) + open "\(snapshotURL.absoluteString)" + """ + ) + } - XCTExpectFailure { - withSnapshotTesting(record: .failed) { - assertSnapshot(of: 42, as: .json) + try #expect(String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self) == "42") + } } - } issueMatcher: { - $0.compactDescription.hasPrefix( - """ - failed - Snapshot does not match reference. A new snapshot was automatically recorded. - """) - } - - try XCTAssertEqual( - String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), - "42" - ) + #endif + + @Test + func recordFailed_NoFailure() async throws { + try withKnownSnapshotURL { snapshotURL in + #if os(Android) + throw XCTSkip("cannot save next to file on Android") + #endif + try Data("42".utf8).write(to: snapshotURL) + let modifiedDate = + try FileManager.default.attributesOfItem( + atPath: snapshotURL.path + )[FileAttributeKey.modificationDate] as! Date + + try withTestingEnvironment(record: .missing) { + try assert(of: 42, as: .json) + } + + try #expect(String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self) == "42") + + try #expect( + FileManager.default.attributesOfItem( + atPath: snapshotURL.path + )[FileAttributeKey.modificationDate] as! Date == modifiedDate + ) + } + } + + #if canImport(Darwin) + @Test + func recordFailed_MissingFile() async throws { + try withKnownSnapshotURL { snapshotURL in + try withKnownIssue { + try withTestingEnvironment(record: .missing) { + try assert(of: 42, as: .json) + } + } matching: { + ($0.comments.first?.rawValue ?? "").hasPrefix( + """ + No reference was found on disk. Automatically recorded snapshot: … + + open "\(snapshotURL.absoluteString)" + """ + ) + } + + try #expect(String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self) == "42") + } + } + #endif } - #endif - - func testRecordFailed_NoFailure() throws { - #if os(Android) - throw XCTSkip("cannot save next to file on Android") - #endif - try Data("42".utf8).write(to: snapshotURL) - let modifiedDate = - try FileManager.default - .attributesOfItem(atPath: snapshotURL.path)[FileAttributeKey.modificationDate] as! Date - - withSnapshotTesting(record: .failed) { - assertSnapshot(of: 42, as: .json) +} + +extension BaseSuite.RecordTests { + + func withKnownSnapshotURL( + _ testName: String = #function, + body: @escaping @Sendable (URL) throws -> Void + ) rethrows { + let snapshotURL = snapshotURL(testName: testName) + try body(snapshotURL) + try? FileManager.default.removeItem(at: snapshotURL) } - try XCTAssertEqual( - String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), - "42" - ) - XCTAssertEqual( - try FileManager.default - .attributesOfItem(atPath: snapshotURL.path)[FileAttributeKey.modificationDate] as! Date, - modifiedDate - ) - } - - #if canImport(Darwin) - func testRecordFailed_MissingFile() throws { - XCTExpectFailure { - withSnapshotTesting(record: .failed) { - assertSnapshot(of: 42, as: .json) + private func snapshotURL( + testName: String + ) -> URL { + let testName = String( + testName + .split(separator: " ") + .flatMap { String($0).split(separator: ".") } + .last! + .prefix(while: { $0 != "]" }) + ) + + var snapshotURL = fileURL + let platform = SnapshotEnvironment.current.platform + + if !platform.isEmpty { + snapshotURL.appendPathComponent(platform) + } + + snapshotURL.appendPathComponent("\(sanitizePathComponent(testName)).1.json") + + if FileManager.default.fileExists(atPath: fileURL.path) { + try? FileManager.default.removeItem(at: snapshotURL) + return snapshotURL } - } issueMatcher: { - $0.compactDescription.hasPrefix( - """ - failed - No reference was found on disk. Automatically recorded snapshot: … - """) - } - - try XCTAssertEqual( - String(decoding: Data(contentsOf: snapshotURL), as: UTF8.self), - "42" - ) + + try? FileManager.default.createDirectory( + at: fileURL, + withIntermediateDirectories: true + ) + + return snapshotURL + } + + private func sanitizePathComponent(_ path: String) -> String { + let invalidCharacters = CharacterSet(charactersIn: "\\/:*?\"<>|") + .union(.newlines) + .union(.illegalCharacters) + .union(.controlCharacters) + + return + path + .replacingOccurrences(of: "()", with: "") + .components(separatedBy: invalidCharacters) + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + .joined(separator: "") } - #endif } diff --git a/Tests/SnapshotTestingTests/SnapshotTestingTests.swift b/Tests/SnapshotTestingTests/SnapshotTestingTests.swift deleted file mode 100644 index 6fa8abeb2..000000000 --- a/Tests/SnapshotTestingTests/SnapshotTestingTests.swift +++ /dev/null @@ -1,1346 +0,0 @@ -import Foundation -import XCTest - -@testable import SnapshotTesting - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif -#if canImport(SceneKit) - import SceneKit -#endif -#if canImport(SpriteKit) - import SpriteKit - import SwiftUI -#endif -#if canImport(WebKit) - @preconcurrency import WebKit -#endif -#if canImport(UIKit) - import UIKit.UIView -#endif - -final class SnapshotTestingTests: BaseTestCase { - func testAny() { - struct User { let id: Int, name: String, bio: String } - let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") - assertSnapshot(of: user, as: .dump) - } - - func testRecursion() { - withSnapshotTesting { - class Father { - var child: Child? - init(_ child: Child? = nil) { self.child = child } - } - class Child { - let father: Father - init(_ father: Father) { - self.father = father - father.child = self - } - } - let father = Father() - let child = Child(father) - assertSnapshot(of: father, as: .dump) - assertSnapshot(of: child, as: .dump) - } - } - - @available(macOS 10.13, tvOS 11.0, *) - func testAnyAsJson() throws { - struct User: Encodable { let id: Int, name: String, bio: String } - let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") - - let data = try JSONEncoder().encode(user) - let any = try JSONSerialization.jsonObject(with: data, options: []) - - assertSnapshot(of: any, as: .json) - } - - func testAnySnapshotStringConvertible() { - assertSnapshot(of: "a" as Character, as: .dump, named: "character") - assertSnapshot(of: Data("Hello, world!".utf8), as: .dump, named: "data") - assertSnapshot(of: Date(timeIntervalSinceReferenceDate: 0), as: .dump, named: "date") - assertSnapshot(of: NSObject(), as: .dump, named: "nsobject") - assertSnapshot(of: "Hello, world!", as: .dump, named: "string") - assertSnapshot(of: "Hello, world!".dropLast(8), as: .dump, named: "substring") - assertSnapshot(of: URL(string: "https://www.pointfree.co")!, as: .dump, named: "url") - } - - func testAutolayout() { - #if os(iOS) - let vc = UIViewController() - vc.view.translatesAutoresizingMaskIntoConstraints = false - let subview = UIView() - subview.translatesAutoresizingMaskIntoConstraints = false - vc.view.addSubview(subview) - NSLayoutConstraint.activate([ - subview.topAnchor.constraint(equalTo: vc.view.topAnchor), - subview.bottomAnchor.constraint(equalTo: vc.view.bottomAnchor), - subview.leftAnchor.constraint(equalTo: vc.view.leftAnchor), - subview.rightAnchor.constraint(equalTo: vc.view.rightAnchor), - ]) - assertSnapshot(of: vc, as: .image) - #endif - } - - func testDeterministicDictionaryAndSetSnapshots() { - struct Person: Hashable { let name: String } - struct DictionarySetContainer { let dict: [String: Int], set: Set } - let set = DictionarySetContainer( - dict: ["c": 3, "a": 1, "b": 2], - set: [.init(name: "Brandon"), .init(name: "Stephen")] - ) - assertSnapshot(of: set, as: .dump) - } - - func testCaseIterable() { - enum Direction: String, CaseIterable { - case up, down, left, right - var rotatedLeft: Direction { - switch self { - case .up: return .left - case .down: return .right - case .left: return .down - case .right: return .up - } - } - } - - assertSnapshot( - of: { $0.rotatedLeft }, - as: Snapshotting.func(into: .description) - ) - } - - func testCGPath() { - #if os(iOS) || os(tvOS) || os(macOS) - let path = CGPath.heart - - let osName: String - #if os(iOS) - osName = "iOS" - #elseif os(tvOS) - osName = "tvOS" - #elseif os(macOS) - osName = "macOS" - #endif - - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot(of: path, as: .image, named: osName) - } - - if #available(iOS 11.0, OSX 10.13, tvOS 11.0, *) { - assertSnapshot(of: path, as: .elementsDescription, named: osName) - } - #endif - } - - func testData() { - let data = Data([0xDE, 0xAD, 0xBE, 0xEF]) - - assertSnapshot(of: data, as: .data) - } - - func testEncodable() { - struct User: Encodable { let id: Int, name: String, bio: String } - let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") - - if #available(iOS 11.0, macOS 10.13, tvOS 11.0, *) { - assertSnapshot(of: user, as: .json) - } - assertSnapshot(of: user, as: .plist) - } - - func testMixedViews() { - // #if os(iOS) || os(macOS) - // // NB: CircleCI crashes while trying to instantiate SKView. - // if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - // let webView = WKWebView(frame: .init(x: 0, y: 0, width: 50, height: 50)) - // webView.loadHTMLString("🌎", baseURL: nil) - // - // let skView = SKView(frame: .init(x: 50, y: 0, width: 50, height: 50)) - // let scene = SKScene(size: .init(width: 50, height: 50)) - // let node = SKShapeNode(circleOfRadius: 15) - // node.fillColor = .red - // node.position = .init(x: 25, y: 25) - // scene.addChild(node) - // skView.presentScene(scene) - // - // let view = View(frame: .init(x: 0, y: 0, width: 100, height: 50)) - // view.addSubview(webView) - // view.addSubview(skView) - // - // assertSnapshot(of: view, as: .image, named: platform) - // } - // #endif - } - - func testMultipleSnapshots() { - assertSnapshot(of: [1], as: .dump) - assertSnapshot(of: [1, 2], as: .dump) - } - - func testNamedAssertion() { - struct User { let id: Int, name: String, bio: String } - let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") - assertSnapshot(of: user, as: .dump, named: "named") - } - - func testNSBezierPath() { - #if os(macOS) - let path = NSBezierPath.heart - - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot(of: path, as: .image, named: "macOS") - } - - assertSnapshot(of: path, as: .elementsDescription, named: "macOS") - #endif - } - - func testNSView() { - #if os(macOS) - let button = NSButton() - button.bezelStyle = .rounded - button.title = "Push Me" - button.sizeToFit() - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot(of: button, as: .image) - assertSnapshot(of: button, as: .recursiveDescription) - } - #endif - } - - func testNSViewWithLayer() { - #if os(macOS) - let view = NSView() - view.frame = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0) - view.wantsLayer = true - view.layer?.backgroundColor = NSColor.green.cgColor - view.layer?.cornerRadius = 5 - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot(of: view, as: .image) - assertSnapshot(of: view, as: .recursiveDescription) - } - #endif - } - - func testPrecision() { - #if os(iOS) || os(macOS) || os(tvOS) - #if os(iOS) || os(tvOS) - let label = UILabel() - #if os(iOS) - label.frame = CGRect(origin: .zero, size: CGSize(width: 43.5, height: 20.5)) - #elseif os(tvOS) - label.frame = CGRect(origin: .zero, size: CGSize(width: 98, height: 46)) - #endif - label.backgroundColor = .white - #elseif os(macOS) - let label = NSTextField() - label.frame = CGRect(origin: .zero, size: CGSize(width: 37, height: 16)) - label.backgroundColor = .white - label.isBezeled = false - label.isEditable = false - #endif - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - label.text = "Hello." - assertSnapshot(of: label, as: .image(precision: 0.9), named: platform) - label.text = "Hello" - assertSnapshot(of: label, as: .image(precision: 0.9), named: platform) - } - #endif - } - - func testImagePrecision() throws { - #if os(iOS) || os(tvOS) || os(macOS) - let imageURL = URL(fileURLWithPath: String(#file), isDirectory: false) - .deletingLastPathComponent() - .appendingPathComponent("__Fixtures__/testImagePrecision.reference.png") - #if os(iOS) || os(tvOS) - let image = try XCTUnwrap(UIImage(contentsOfFile: imageURL.path)) - #elseif os(macOS) - let image = try XCTUnwrap(NSImage(byReferencing: imageURL)) - #endif - - assertSnapshot(of: image, as: .image(precision: 0.995), named: "exact") - if #available(iOS 11.0, tvOS 11.0, macOS 10.13, *) { - assertSnapshot(of: image, as: .image(perceptualPrecision: 0.98), named: "perceptual") - } - #endif - } - - func testSCNView() { - // #if os(iOS) || os(macOS) || os(tvOS) - // // NB: CircleCI crashes while trying to instantiate SCNView. - // if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - // let scene = SCNScene() - // - // let sphereGeometry = SCNSphere(radius: 3) - // sphereGeometry.segmentCount = 200 - // let sphereNode = SCNNode(geometry: sphereGeometry) - // sphereNode.position = SCNVector3Zero - // scene.rootNode.addChildNode(sphereNode) - // - // sphereGeometry.firstMaterial?.diffuse.contents = URL(fileURLWithPath: String(#file), isDirectory: false) - // .deletingLastPathComponent() - // .appendingPathComponent("__Fixtures__/earth.png") - // - // let cameraNode = SCNNode() - // cameraNode.camera = SCNCamera() - // cameraNode.position = SCNVector3Make(0, 0, 8) - // scene.rootNode.addChildNode(cameraNode) - // - // let omniLight = SCNLight() - // omniLight.type = .omni - // let omniLightNode = SCNNode() - // omniLightNode.light = omniLight - // omniLightNode.position = SCNVector3Make(10, 10, 10) - // scene.rootNode.addChildNode(omniLightNode) - // - // assertSnapshot( - // of: scene, - // as: .image(size: .init(width: 500, height: 500)), - // named: platform - // ) - // } - // #endif - } - - func testSKView() { - // #if os(iOS) || os(macOS) || os(tvOS) - // // NB: CircleCI crashes while trying to instantiate SKView. - // if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - // let scene = SKScene(size: .init(width: 50, height: 50)) - // let node = SKShapeNode(circleOfRadius: 15) - // node.fillColor = .red - // node.position = .init(x: 25, y: 25) - // scene.addChild(node) - // - // assertSnapshot( - // of: scene, - // as: .image(size: .init(width: 50, height: 50)), - // named: platform - // ) - // } - // #endif - } - - func testTableViewController() { - #if os(iOS) - class TableViewController: UITableViewController { - override func viewDidLoad() { - super.viewDidLoad() - self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") - } - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int - { - return 10 - } - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) - -> UITableViewCell - { - let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) - cell.textLabel?.text = "\(indexPath.row)" - return cell - } - } - let tableViewController = TableViewController() - assertSnapshot(of: tableViewController, as: .image(on: .iPhoneSe)) - #endif - } - - func testAssertMultipleSnapshot() { - #if os(iOS) - class TableViewController: UITableViewController { - override func viewDidLoad() { - super.viewDidLoad() - self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") - } - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int - { - return 10 - } - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) - -> UITableViewCell - { - let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) - cell.textLabel?.text = "\(indexPath.row)" - return cell - } - } - let tableViewController = TableViewController() - assertSnapshots( - of: tableViewController, - as: ["iPhoneSE-image": .image(on: .iPhoneSe), "iPad-image": .image(on: .iPadMini)]) - assertSnapshots( - of: tableViewController, as: [.image(on: .iPhoneX), .image(on: .iPhoneXsMax)]) - #endif - } - - func testTraits() { - #if os(iOS) || os(tvOS) - if #available(iOS 11.0, tvOS 11.0, *) { - class MyViewController: UIViewController { - let topLabel = UILabel() - let leadingLabel = UILabel() - let trailingLabel = UILabel() - let bottomLabel = UILabel() - - override func viewDidLoad() { - super.viewDidLoad() - - self.navigationItem.leftBarButtonItem = .init( - barButtonSystemItem: .add, target: nil, action: nil) - - self.view.backgroundColor = .white - - self.topLabel.text = "What's" - self.leadingLabel.text = "the" - self.trailingLabel.text = "point" - self.bottomLabel.text = "?" - - self.topLabel.translatesAutoresizingMaskIntoConstraints = false - self.leadingLabel.translatesAutoresizingMaskIntoConstraints = false - self.trailingLabel.translatesAutoresizingMaskIntoConstraints = false - self.bottomLabel.translatesAutoresizingMaskIntoConstraints = false - - self.view.addSubview(self.topLabel) - self.view.addSubview(self.leadingLabel) - self.view.addSubview(self.trailingLabel) - self.view.addSubview(self.bottomLabel) - - NSLayoutConstraint.activate([ - self.topLabel.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), - self.topLabel.centerXAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.centerXAnchor), - self.leadingLabel.leadingAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.leadingAnchor), - self.leadingLabel.trailingAnchor.constraint( - lessThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor), - // self.leadingLabel.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor), - self.leadingLabel.centerYAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.centerYAnchor), - self.trailingLabel.leadingAnchor.constraint( - greaterThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor), - self.trailingLabel.trailingAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.trailingAnchor), - self.trailingLabel.centerYAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.centerYAnchor), - self.bottomLabel.bottomAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.bottomAnchor), - self.bottomLabel.centerXAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.centerXAnchor), - ]) - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - self.topLabel.font = .preferredFont( - forTextStyle: .headline, compatibleWith: self.traitCollection) - self.leadingLabel.font = .preferredFont( - forTextStyle: .body, compatibleWith: self.traitCollection) - self.trailingLabel.font = .preferredFont( - forTextStyle: .body, compatibleWith: self.traitCollection) - self.bottomLabel.font = .preferredFont( - forTextStyle: .subheadline, compatibleWith: self.traitCollection) - self.view.setNeedsUpdateConstraints() - self.view.updateConstraintsIfNeeded() - } - } - - let viewController = MyViewController() - - #if os(iOS) - assertSnapshot(of: viewController, as: .image(on: .iPhoneSe), named: "iphone-se") - assertSnapshot(of: viewController, as: .image(on: .iPhone8), named: "iphone-8") - assertSnapshot(of: viewController, as: .image(on: .iPhone8Plus), named: "iphone-8-plus") - assertSnapshot(of: viewController, as: .image(on: .iPhoneX), named: "iphone-x") - assertSnapshot(of: viewController, as: .image(on: .iPhoneXr), named: "iphone-xr") - assertSnapshot(of: viewController, as: .image(on: .iPhoneXsMax), named: "iphone-xs-max") - assertSnapshot(of: viewController, as: .image(on: .iPadMini), named: "ipad-mini") - assertSnapshot(of: viewController, as: .image(on: .iPad9_7), named: "ipad-9-7") - assertSnapshot(of: viewController, as: .image(on: .iPad10_2), named: "ipad-10-2") - assertSnapshot(of: viewController, as: .image(on: .iPadPro10_5), named: "ipad-pro-10-5") - assertSnapshot(of: viewController, as: .image(on: .iPadPro11), named: "ipad-pro-11") - assertSnapshot(of: viewController, as: .image(on: .iPadPro12_9), named: "ipad-pro-12-9") - - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPhoneSe), named: "iphone-se") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPhone8), named: "iphone-8") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPhone8Plus), named: "iphone-8-plus") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPhoneX), named: "iphone-x") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPhoneXr), named: "iphone-xr") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPhoneXsMax), named: "iphone-xs-max") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPadMini), named: "ipad-mini") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPad9_7), named: "ipad-9-7") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPad10_2), named: "ipad-10-2") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPadPro10_5), named: "ipad-pro-10-5") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPadPro11), named: "ipad-pro-11") - assertSnapshot( - of: viewController, as: .recursiveDescription(on: .iPadPro12_9), named: "ipad-pro-12-9") - - assertSnapshot( - of: viewController, as: .image(on: .iPhoneSe(.portrait)), named: "iphone-se") - assertSnapshot(of: viewController, as: .image(on: .iPhone8(.portrait)), named: "iphone-8") - assertSnapshot( - of: viewController, as: .image(on: .iPhone8Plus(.portrait)), named: "iphone-8-plus") - assertSnapshot(of: viewController, as: .image(on: .iPhoneX(.portrait)), named: "iphone-x") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneXr(.portrait)), named: "iphone-xr") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneXsMax(.portrait)), named: "iphone-xs-max") - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.landscape)), named: "ipad-mini") - assertSnapshot( - of: viewController, as: .image(on: .iPad9_7(.landscape)), named: "ipad-9-7") - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.landscape)), named: "ipad-10-2") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.landscape)), named: "ipad-pro-10-5") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.landscape)), named: "ipad-pro-11") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.landscape)), named: "ipad-pro-12-9") - - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.landscape(splitView: .oneThird))), - named: "ipad-mini-33-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.landscape(splitView: .oneHalf))), - named: "ipad-mini-50-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.landscape(splitView: .twoThirds))), - named: "ipad-mini-66-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.portrait(splitView: .oneThird))), - named: "ipad-mini-33-split-portrait") - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.portrait(splitView: .twoThirds))), - named: "ipad-mini-66-split-portrait") - - assertSnapshot( - of: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .oneThird))), - named: "ipad-9-7-33-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .oneHalf))), - named: "ipad-9-7-50-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPad9_7(.landscape(splitView: .twoThirds))), - named: "ipad-9-7-66-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPad9_7(.portrait(splitView: .oneThird))), - named: "ipad-9-7-33-split-portrait") - assertSnapshot( - of: viewController, as: .image(on: .iPad9_7(.portrait(splitView: .twoThirds))), - named: "ipad-9-7-66-split-portrait") - - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .oneThird))), - named: "ipad-10-2-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .oneHalf))), - named: "ipad-10-2-50-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.landscape(splitView: .twoThirds))), - named: "ipad-10-2-66-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.portrait(splitView: .oneThird))), - named: "ipad-10-2-33-split-portrait") - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.portrait(splitView: .twoThirds))), - named: "ipad-10-2-66-split-portrait") - - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .oneThird))), - named: "ipad-pro-10inch-33-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .oneHalf))), - named: "ipad-pro-10inch-50-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.landscape(splitView: .twoThirds))), - named: "ipad-pro-10inch-66-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.portrait(splitView: .oneThird))), - named: "ipad-pro-10inch-33-split-portrait") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.portrait(splitView: .twoThirds))), - named: "ipad-pro-10inch-66-split-portrait") - - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .oneThird))), - named: "ipad-pro-11inch-33-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .oneHalf))), - named: "ipad-pro-11inch-50-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.landscape(splitView: .twoThirds))), - named: "ipad-pro-11inch-66-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.portrait(splitView: .oneThird))), - named: "ipad-pro-11inch-33-split-portrait") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.portrait(splitView: .twoThirds))), - named: "ipad-pro-11inch-66-split-portrait") - - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .oneThird))), - named: "ipad-pro-12inch-33-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .oneHalf))), - named: "ipad-pro-12inch-50-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.landscape(splitView: .twoThirds))), - named: "ipad-pro-12inch-66-split-landscape") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.portrait(splitView: .oneThird))), - named: "ipad-pro-12inch-33-split-portrait") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.portrait(splitView: .twoThirds))), - named: "ipad-pro-12inch-66-split-portrait") - - assertSnapshot( - of: viewController, as: .image(on: .iPhoneSe(.landscape)), - named: "iphone-se-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhone8(.landscape)), named: "iphone-8-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhone8Plus(.landscape)), - named: "iphone-8-plus-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneX(.landscape)), named: "iphone-x-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneXr(.landscape)), - named: "iphone-xr-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneXsMax(.landscape)), - named: "iphone-xs-max-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.portrait)), named: "ipad-mini-alternative" - ) - assertSnapshot( - of: viewController, as: .image(on: .iPad9_7(.portrait)), named: "ipad-9-7-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.portrait)), named: "ipad-10-2-alternative" - ) - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.portrait)), - named: "ipad-pro-10-5-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.portrait)), - named: "ipad-pro-11-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.portrait)), - named: "ipad-pro-12-9-alternative") - - allContentSizes.forEach { name, contentSize in - assertSnapshot( - of: viewController, - as: .image(on: .iPhoneSe, traits: .init(preferredContentSizeCategory: contentSize)), - named: "iphone-se-\(name)" - ) - } - #elseif os(tvOS) - assertSnapshot( - of: viewController, as: .image(on: .tv), named: "tv") - assertSnapshot( - of: viewController, as: .image(on: .tv4K), named: "tv4k") - #endif - } - #endif - } - - func testTraitsEmbeddedInTabNavigation() { - #if os(iOS) - if #available(iOS 11.0, *) { - class MyViewController: UIViewController { - let topLabel = UILabel() - let leadingLabel = UILabel() - let trailingLabel = UILabel() - let bottomLabel = UILabel() - - override func viewDidLoad() { - super.viewDidLoad() - - self.navigationItem.leftBarButtonItem = .init( - barButtonSystemItem: .add, target: nil, action: nil) - - self.view.backgroundColor = .white - - self.topLabel.text = "What's" - self.leadingLabel.text = "the" - self.trailingLabel.text = "point" - self.bottomLabel.text = "?" - - self.topLabel.translatesAutoresizingMaskIntoConstraints = false - self.leadingLabel.translatesAutoresizingMaskIntoConstraints = false - self.trailingLabel.translatesAutoresizingMaskIntoConstraints = false - self.bottomLabel.translatesAutoresizingMaskIntoConstraints = false - - self.view.addSubview(self.topLabel) - self.view.addSubview(self.leadingLabel) - self.view.addSubview(self.trailingLabel) - self.view.addSubview(self.bottomLabel) - - NSLayoutConstraint.activate([ - self.topLabel.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), - self.topLabel.centerXAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.centerXAnchor), - self.leadingLabel.leadingAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.leadingAnchor), - self.leadingLabel.trailingAnchor.constraint( - lessThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor), - // self.leadingLabel.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor), - self.leadingLabel.centerYAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.centerYAnchor), - self.trailingLabel.leadingAnchor.constraint( - greaterThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor), - self.trailingLabel.trailingAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.trailingAnchor), - self.trailingLabel.centerYAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.centerYAnchor), - self.bottomLabel.bottomAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.bottomAnchor), - self.bottomLabel.centerXAnchor.constraint( - equalTo: self.view.safeAreaLayoutGuide.centerXAnchor), - ]) - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - self.topLabel.font = .preferredFont( - forTextStyle: .headline, compatibleWith: self.traitCollection) - self.leadingLabel.font = .preferredFont( - forTextStyle: .body, compatibleWith: self.traitCollection) - self.trailingLabel.font = .preferredFont( - forTextStyle: .body, compatibleWith: self.traitCollection) - self.bottomLabel.font = .preferredFont( - forTextStyle: .subheadline, compatibleWith: self.traitCollection) - self.view.setNeedsUpdateConstraints() - self.view.updateConstraintsIfNeeded() - } - } - - let myViewController = MyViewController() - let navController = UINavigationController(rootViewController: myViewController) - let viewController = UITabBarController() - viewController.setViewControllers([navController], animated: false) - - assertSnapshot(of: viewController, as: .image(on: .iPhoneSe), named: "iphone-se") - assertSnapshot(of: viewController, as: .image(on: .iPhone8), named: "iphone-8") - assertSnapshot(of: viewController, as: .image(on: .iPhone8Plus), named: "iphone-8-plus") - assertSnapshot(of: viewController, as: .image(on: .iPhoneX), named: "iphone-x") - assertSnapshot(of: viewController, as: .image(on: .iPhoneXr), named: "iphone-xr") - assertSnapshot(of: viewController, as: .image(on: .iPhoneXsMax), named: "iphone-xs-max") - assertSnapshot(of: viewController, as: .image(on: .iPadMini), named: "ipad-mini") - assertSnapshot(of: viewController, as: .image(on: .iPad9_7), named: "ipad-9-7") - assertSnapshot(of: viewController, as: .image(on: .iPad10_2), named: "ipad-10-2") - assertSnapshot(of: viewController, as: .image(on: .iPadPro10_5), named: "ipad-pro-10-5") - assertSnapshot(of: viewController, as: .image(on: .iPadPro11), named: "ipad-pro-11") - assertSnapshot(of: viewController, as: .image(on: .iPadPro12_9), named: "ipad-pro-12-9") - - assertSnapshot(of: viewController, as: .image(on: .iPhoneSe(.portrait)), named: "iphone-se") - assertSnapshot(of: viewController, as: .image(on: .iPhone8(.portrait)), named: "iphone-8") - assertSnapshot( - of: viewController, as: .image(on: .iPhone8Plus(.portrait)), named: "iphone-8-plus") - assertSnapshot(of: viewController, as: .image(on: .iPhoneX(.portrait)), named: "iphone-x") - assertSnapshot(of: viewController, as: .image(on: .iPhoneXr(.portrait)), named: "iphone-xr") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneXsMax(.portrait)), named: "iphone-xs-max") - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.landscape)), named: "ipad-mini") - assertSnapshot(of: viewController, as: .image(on: .iPad9_7(.landscape)), named: "ipad-9-7") - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.landscape)), named: "ipad-10-2") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.landscape)), named: "ipad-pro-10-5") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.landscape)), named: "ipad-pro-11") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.landscape)), named: "ipad-pro-12-9") - - assertSnapshot( - of: viewController, as: .image(on: .iPhoneSe(.landscape)), named: "iphone-se-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhone8(.landscape)), named: "iphone-8-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhone8Plus(.landscape)), - named: "iphone-8-plus-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneX(.landscape)), named: "iphone-x-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneXr(.landscape)), named: "iphone-xr-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPhoneXsMax(.landscape)), - named: "iphone-xs-max-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPadMini(.portrait)), named: "ipad-mini-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPad9_7(.portrait)), named: "ipad-9-7-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPad10_2(.portrait)), named: "ipad-10-2-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro10_5(.portrait)), - named: "ipad-pro-10-5-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro11(.portrait)), - named: "ipad-pro-11-alternative") - assertSnapshot( - of: viewController, as: .image(on: .iPadPro12_9(.portrait)), - named: "ipad-pro-12-9-alternative") - } - #endif - } - - func testCollectionViewsWithMultipleScreenSizes() { - #if os(iOS) - - final class CollectionViewController: UIViewController, UICollectionViewDataSource, - UICollectionViewDelegateFlowLayout - { - - let flowLayout: UICollectionViewFlowLayout = { - let layout = UICollectionViewFlowLayout() - layout.scrollDirection = .horizontal - layout.minimumLineSpacing = 20 - return layout - }() - - lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) - - override func viewDidLoad() { - super.viewDidLoad() - - view.backgroundColor = .white - view.addSubview(collectionView) - - collectionView.backgroundColor = .white - collectionView.dataSource = self - collectionView.delegate = self - collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell") - collectionView.translatesAutoresizingMaskIntoConstraints = false - - NSLayoutConstraint.activate([ - collectionView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), - collectionView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), - collectionView.trailingAnchor.constraint( - equalTo: view.layoutMarginsGuide.trailingAnchor), - collectionView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor), - ]) - - collectionView.reloadData() - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - collectionView.collectionViewLayout.invalidateLayout() - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - collectionView.collectionViewLayout.invalidateLayout() - } - - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) - -> UICollectionViewCell - { - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) - cell.contentView.backgroundColor = .orange - return cell - } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) - -> Int - { - return 20 - } - - func collectionView( - _ collectionView: UICollectionView, - layout collectionViewLayout: UICollectionViewLayout, - sizeForItemAt indexPath: IndexPath - ) -> CGSize { - return CGSize( - width: min(collectionView.frame.width - 50, 300), - height: collectionView.frame.height - ) - } - - } - - let viewController = CollectionViewController() - - assertSnapshots( - of: viewController, - as: [ - "ipad": .image(on: .iPadPro12_9), - "iphoneSe": .image(on: .iPhoneSe), - "iphone8": .image(on: .iPhone8), - "iphoneMax": .image(on: .iPhoneXsMax), - ]) - #endif - } - - func testTraitsWithView() { - #if os(iOS) - if #available(iOS 11.0, *) { - let label = UILabel() - label.font = .preferredFont(forTextStyle: .title1) - label.adjustsFontForContentSizeCategory = true - label.text = "What's the point?" - - allContentSizes.forEach { name, contentSize in - assertSnapshot( - of: label, - as: .image(traits: .init(preferredContentSizeCategory: contentSize)), - named: "label-\(name)" - ) - } - } - #endif - } - - func testTraitsWithViewController() { - #if os(iOS) - let label = UILabel() - label.font = .preferredFont(forTextStyle: .title1) - label.adjustsFontForContentSizeCategory = true - label.text = "What's the point?" - - let viewController = UIViewController() - viewController.view.addSubview(label) - - label.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - label.leadingAnchor.constraint( - equalTo: viewController.view.layoutMarginsGuide.leadingAnchor), - label.topAnchor.constraint(equalTo: viewController.view.layoutMarginsGuide.topAnchor), - label.trailingAnchor.constraint( - equalTo: viewController.view.layoutMarginsGuide.trailingAnchor), - ]) - - allContentSizes.forEach { name, contentSize in - assertSnapshot( - of: viewController, - as: .recursiveDescription( - on: .iPhoneSe, traits: .init(preferredContentSizeCategory: contentSize)), - named: "label-\(name)" - ) - } - #endif - } - - func testUIBezierPath() { - #if os(iOS) || os(tvOS) - let path = UIBezierPath.heart - - let osName: String - #if os(iOS) - osName = "iOS" - #elseif os(tvOS) - osName = "tvOS" - #endif - - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot(of: path, as: .image, named: osName) - } - - if #available(iOS 11.0, tvOS 11.0, *) { - assertSnapshot(of: path, as: .elementsDescription, named: osName) - } - #endif - } - - func testUIView() { - #if os(iOS) - let view = UIButton(type: .contactAdd) - assertSnapshot(of: view, as: .image) - assertSnapshot(of: view, as: .recursiveDescription) - #endif - } - - func testUIViewControllerLifeCycle() { - #if os(iOS) - class ViewController: UIViewController { - let viewDidLoadExpectation: XCTestExpectation - let viewWillAppearExpectation: XCTestExpectation - let viewDidAppearExpectation: XCTestExpectation - let viewWillDisappearExpectation: XCTestExpectation - let viewDidDisappearExpectation: XCTestExpectation - init( - viewDidLoadExpectation: XCTestExpectation, - viewWillAppearExpectation: XCTestExpectation, - viewDidAppearExpectation: XCTestExpectation, - viewWillDisappearExpectation: XCTestExpectation, - viewDidDisappearExpectation: XCTestExpectation - ) { - self.viewDidLoadExpectation = viewDidLoadExpectation - self.viewWillAppearExpectation = viewWillAppearExpectation - self.viewDidAppearExpectation = viewDidAppearExpectation - self.viewWillDisappearExpectation = viewWillDisappearExpectation - self.viewDidDisappearExpectation = viewDidDisappearExpectation - super.init(nibName: nil, bundle: nil) - } - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - override func viewDidLoad() { - super.viewDidLoad() - viewDidLoadExpectation.fulfill() - } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - viewWillAppearExpectation.fulfill() - } - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - viewDidAppearExpectation.fulfill() - } - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - viewWillDisappearExpectation.fulfill() - } - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - viewDidDisappearExpectation.fulfill() - } - } - - let viewDidLoadExpectation = expectation(description: "viewDidLoad") - let viewWillAppearExpectation = expectation(description: "viewWillAppear") - let viewDidAppearExpectation = expectation(description: "viewDidAppear") - let viewWillDisappearExpectation = expectation(description: "viewWillDisappear") - let viewDidDisappearExpectation = expectation(description: "viewDidDisappear") - viewWillAppearExpectation.expectedFulfillmentCount = 4 - viewDidAppearExpectation.expectedFulfillmentCount = 4 - viewWillDisappearExpectation.expectedFulfillmentCount = 4 - viewDidDisappearExpectation.expectedFulfillmentCount = 4 - - let viewController = ViewController( - viewDidLoadExpectation: viewDidLoadExpectation, - viewWillAppearExpectation: viewWillAppearExpectation, - viewDidAppearExpectation: viewDidAppearExpectation, - viewWillDisappearExpectation: viewWillDisappearExpectation, - viewDidDisappearExpectation: viewDidDisappearExpectation - ) - - assertSnapshot(of: viewController, as: .image) - assertSnapshot(of: viewController, as: .image) - - wait( - for: [ - viewDidLoadExpectation, - viewWillAppearExpectation, - viewDidAppearExpectation, - viewWillDisappearExpectation, - viewDidDisappearExpectation, - ], timeout: 1.0, enforceOrder: true) - #endif - } - - func testCALayer() { - #if os(iOS) - let layer = CALayer() - layer.frame = CGRect(x: 0, y: 0, width: 100, height: 100) - layer.backgroundColor = UIColor.red.cgColor - layer.borderWidth = 4.0 - layer.borderColor = UIColor.black.cgColor - assertSnapshot(of: layer, as: .image) - #endif - } - - func testCALayerWithGradient() { - #if os(iOS) - let baseLayer = CALayer() - baseLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100) - let gradientLayer = CAGradientLayer() - gradientLayer.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor] - gradientLayer.frame = baseLayer.frame - baseLayer.addSublayer(gradientLayer) - assertSnapshot(of: baseLayer, as: .image) - #endif - } - - func testViewControllerHierarchy() { - #if os(iOS) - let page = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) - page.setViewControllers([UIViewController()], direction: .forward, animated: false) - let tab = UITabBarController() - tab.viewControllers = [ - UINavigationController(rootViewController: page), - UINavigationController(rootViewController: UIViewController()), - UINavigationController(rootViewController: UIViewController()), - UINavigationController(rootViewController: UIViewController()), - UINavigationController(rootViewController: UIViewController()), - ] - assertSnapshot(of: tab, as: .hierarchy) - #endif - } - - func testURLRequest() { - var get = URLRequest(url: URL(string: "https://www.pointfree.co/")!) - get.addValue("pf_session={}", forHTTPHeaderField: "Cookie") - get.addValue("text/html", forHTTPHeaderField: "Accept") - get.addValue("application/json", forHTTPHeaderField: "Content-Type") - assertSnapshot(of: get, as: .raw, named: "get") - assertSnapshot(of: get, as: .curl, named: "get-curl") - - var getWithQuery = URLRequest( - url: URL(string: "https://www.pointfree.co?key_2=value_2&key_1=value_1&key_3=value_3")!) - getWithQuery.addValue("pf_session={}", forHTTPHeaderField: "Cookie") - getWithQuery.addValue("text/html", forHTTPHeaderField: "Accept") - getWithQuery.addValue("application/json", forHTTPHeaderField: "Content-Type") - assertSnapshot(of: getWithQuery, as: .raw, named: "get-with-query") - assertSnapshot(of: getWithQuery, as: .curl, named: "get-with-query-curl") - - var post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!) - post.httpMethod = "POST" - post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie") - post.addValue("text/html", forHTTPHeaderField: "Accept") - post.httpBody = Data("pricing[billing]=monthly&pricing[lane]=individual".utf8) - assertSnapshot(of: post, as: .raw, named: "post") - assertSnapshot(of: post, as: .curl, named: "post-curl") - - var postWithJSON = URLRequest( - url: URL(string: "http://dummy.restapiexample.com/api/v1/create")!) - postWithJSON.httpMethod = "POST" - postWithJSON.addValue("application/json", forHTTPHeaderField: "Content-Type") - postWithJSON.addValue("application/json", forHTTPHeaderField: "Accept") - postWithJSON.httpBody = Data( - "{\"name\":\"tammy134235345235\", \"salary\":0, \"age\":\"tammy133\"}".utf8) - assertSnapshot(of: postWithJSON, as: .raw, named: "post-with-json") - assertSnapshot(of: postWithJSON, as: .curl, named: "post-with-json-curl") - - var head = URLRequest(url: URL(string: "https://www.pointfree.co/")!) - head.httpMethod = "HEAD" - head.addValue("pf_session={}", forHTTPHeaderField: "Cookie") - assertSnapshot(of: head, as: .raw, named: "head") - assertSnapshot(of: head, as: .curl, named: "head-curl") - - post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!) - post.httpMethod = "POST" - post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie") - post.addValue("application/json", forHTTPHeaderField: "Accept") - post.httpBody = Data( - """ - {"pricing": {"lane": "individual","billing": "monthly"}} - """.utf8) - } - - func testWebView() throws { - #if os(iOS) || os(macOS) - let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false) - .deletingLastPathComponent() - .appendingPathComponent("__Fixtures__/pointfree.html") - let html = try String(contentsOf: fixtureUrl) - let webView = WKWebView() - webView.loadHTMLString(html, baseURL: nil) - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot( - of: webView, - as: .image(size: .init(width: 800, height: 600)), - named: platform - ) - } - #endif - } - - func testViewWithZeroHeightOrWidth() { - #if os(iOS) || os(tvOS) - var rect = CGRect(x: 0, y: 0, width: 350, height: 0) - var view = UIView(frame: rect) - view.backgroundColor = .red - assertSnapshot(of: view, as: .image, named: "noHeight") - - rect = CGRect(x: 0, y: 0, width: 0, height: 350) - view = UIView(frame: rect) - view.backgroundColor = .green - assertSnapshot(of: view, as: .image, named: "noWidth") - - rect = CGRect(x: 0, y: 0, width: 0, height: 0) - view = UIView(frame: rect) - view.backgroundColor = .blue - assertSnapshot(of: view, as: .image, named: "noWidth.noHeight") - #endif - } - - func testViewAgainstEmptyImage() { - #if os(iOS) || os(tvOS) - let rect = CGRect(x: 0, y: 0, width: 0, height: 0) - let view = UIView(frame: rect) - view.backgroundColor = .blue - - let failure = verifySnapshot(of: view, as: .image, named: "notEmptyImage") - XCTAssertNotNil(failure) - #endif - } - - func testEmbeddedWebView() throws { - #if os(iOS) - let label = UILabel() - label.text = "Hello, Blob!" - - let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false) - .deletingLastPathComponent() - .appendingPathComponent("__Fixtures__/pointfree.html") - let html = try String(contentsOf: fixtureUrl) - let webView = WKWebView() - webView.loadHTMLString(html, baseURL: nil) - webView.isHidden = true - - let stackView = UIStackView(arrangedSubviews: [label, webView]) - stackView.axis = .vertical - - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot( - of: stackView, - as: .image(size: .init(width: 800, height: 600)), - named: platform - ) - } - #endif - } - - #if os(iOS) || os(macOS) - final class ManipulatingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate { - func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - webView.evaluateJavaScript("document.body.children[0].classList.remove(\"hero\")") // Change layout - } - } - func testWebViewWithManipulatingNavigationDelegate() throws { - let manipulatingWKWebViewNavigationDelegate = ManipulatingWKWebViewNavigationDelegate() - let webView = WKWebView() - webView.navigationDelegate = manipulatingWKWebViewNavigationDelegate - - let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false) - .deletingLastPathComponent() - .appendingPathComponent("__Fixtures__/pointfree.html") - let html = try String(contentsOf: fixtureUrl) - webView.loadHTMLString(html, baseURL: nil) - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot( - of: webView, - as: .image(size: .init(width: 800, height: 600)), - named: platform - ) - } - _ = manipulatingWKWebViewNavigationDelegate - } - - final class CancellingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate { - func webView( - _ webView: WKWebView, - decidePolicyFor navigationAction: WKNavigationAction, - decisionHandler: @escaping (WKNavigationActionPolicy) -> Void - ) { - decisionHandler(.cancel) - } - } - - func testWebViewWithCancellingNavigationDelegate() throws { - let cancellingWKWebViewNavigationDelegate = CancellingWKWebViewNavigationDelegate() - let webView = WKWebView() - webView.navigationDelegate = cancellingWKWebViewNavigationDelegate - - let fixtureUrl = URL(fileURLWithPath: String(#file), isDirectory: false) - .deletingLastPathComponent() - .appendingPathComponent("__Fixtures__/pointfree.html") - let html = try String(contentsOf: fixtureUrl) - webView.loadHTMLString(html, baseURL: nil) - if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { - assertSnapshot( - of: webView, - as: .image(size: .init(width: 800, height: 600)), - named: platform - ) - } - _ = cancellingWKWebViewNavigationDelegate - } - #endif - - #if os(iOS) - @available(iOS 13.0, *) - func testSwiftUIView_iOS() { - struct MyView: SwiftUI.View { - var body: some SwiftUI.View { - HStack { - Image(systemName: "checkmark.circle.fill") - Text("Checked").fixedSize() - } - .padding(5) - .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue)) - .padding(10) - } - } - - let view = MyView().background(Color.yellow) - - assertSnapshot(of: view, as: .image(traits: .init(userInterfaceStyle: .light))) - assertSnapshot( - of: view, as: .image(layout: .sizeThatFits, traits: .init(userInterfaceStyle: .light)), - named: "size-that-fits") - assertSnapshot( - of: view, - as: .image( - layout: .fixed(width: 200.0, height: 100.0), traits: .init(userInterfaceStyle: .light)), - named: "fixed") - assertSnapshot( - of: view, - as: .image(layout: .device(config: .iPhoneSe), traits: .init(userInterfaceStyle: .light)), - named: "device") - } - #endif - - #if os(tvOS) - @available(tvOS 13.0, *) - func testSwiftUIView_tvOS() { - struct MyView: SwiftUI.View { - var body: some SwiftUI.View { - HStack { - Image(systemName: "checkmark.circle.fill") - Text("Checked").fixedSize() - } - .padding(5) - .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue)) - .padding(10) - } - } - let view = MyView().background(Color.yellow) - - assertSnapshot(of: view, as: .image()) - assertSnapshot(of: view, as: .image(layout: .sizeThatFits), named: "size-that-fits") - assertSnapshot( - of: view, as: .image(layout: .fixed(width: 300.0, height: 100.0)), named: "fixed") - assertSnapshot(of: view, as: .image(layout: .device(config: .tv)), named: "device") - } - #endif -} - -#if os(iOS) - private let allContentSizes = - [ - "extra-small": UIContentSizeCategory.extraSmall, - "small": .small, - "medium": .medium, - "large": .large, - "extra-large": .extraLarge, - "extra-extra-large": .extraExtraLarge, - "extra-extra-extra-large": .extraExtraExtraLarge, - "accessibility-medium": .accessibilityMedium, - "accessibility-large": .accessibilityLarge, - "accessibility-extra-large": .accessibilityExtraLarge, - "accessibility-extra-extra-large": .accessibilityExtraExtraLarge, - "accessibility-extra-extra-extra-large": .accessibilityExtraExtraExtraLarge, - ] -#endif diff --git a/Tests/SnapshotTestingTests/SnapshotsTraitTests.swift b/Tests/SnapshotTestingTests/SnapshotsTraitTests.swift index a830c0cc1..bf7c1f333 100644 --- a/Tests/SnapshotTestingTests/SnapshotsTraitTests.swift +++ b/Tests/SnapshotTestingTests/SnapshotsTraitTests.swift @@ -1,51 +1,63 @@ -#if compiler(>=6) && canImport(Testing) - import Testing - @_spi(Internals) import SnapshotTesting +import SnapshotTesting +import Testing - extension BaseSuite { - struct SnapshotsTraitTests { - @Test(.snapshots(diffTool: "ksdiff")) - func testDiffTool() { - #expect( - _diffTool(currentFilePath: "old.png", failedFilePath: "new.png") - == "ksdiff old.png new.png" - ) - } +extension BaseSuite { - @Suite(.snapshots(diffTool: "ksdiff")) - struct OverrideDiffTool { - @Test(.snapshots(diffTool: "difftool")) - func testDiffToolOverride() { - #expect( - _diffTool(currentFilePath: "old.png", failedFilePath: "new.png") - == "difftool old.png new.png" - ) - } + struct SnapshotsTraitTests { - @Suite(.snapshots(record: .all)) - struct OverrideRecord { - @Test - func config() { + @Test(.diffTool("ksdiff")) + func testDiffTool() { #expect( - _diffTool(currentFilePath: "old.png", failedFilePath: "new.png") - == "ksdiff old.png new.png" + SnapshotEnvironment.current.diffTool( + currentFilePath: "old.png", + failedFilePath: "new.png" + ) + == "ksdiff old.png new.png" ) - #expect(_record == .all) - } + } + + @Suite(.diffTool("ksdiff")) + struct OverrideDiffTool { + @Test(.diffTool("difftool")) + func testDiffToolOverride() { + #expect( + SnapshotEnvironment.current.diffTool( + currentFilePath: "old.png", + failedFilePath: "new.png" + ) + == "difftool old.png new.png" + ) + } + + @Suite(.record(.all)) + struct OverrideRecord { + @Test + func config() { + #expect( + SnapshotEnvironment.current.diffTool( + currentFilePath: "old.png", + failedFilePath: "new.png" + ) + == "ksdiff old.png new.png" + ) + #expect(SnapshotEnvironment.current.recordMode == .all) + } - @Suite(.snapshots(record: .failed, diffTool: "diff")) - struct OverrideDiffToolAndRecord { - @Test - func config() { - #expect( - _diffTool(currentFilePath: "old.png", failedFilePath: "new.png") - == "diff old.png new.png" - ) - #expect(_record == .failed) + @Suite(.record(.failed), .diffTool("diff")) + struct OverrideDiffToolAndRecord { + @Test + func config() { + #expect( + SnapshotEnvironment.current.diffTool( + currentFilePath: "old.png", + failedFilePath: "new.png" + ) + == "diff old.png new.png" + ) + #expect(SnapshotEnvironment.current.recordMode == .failed) + } + } } - } } - } } - } -#endif +} diff --git a/Tests/SnapshotTestingTests/SwiftTestingTests.swift b/Tests/SnapshotTestingTests/SwiftTestingTests.swift deleted file mode 100644 index d031db51a..000000000 --- a/Tests/SnapshotTestingTests/SwiftTestingTests.swift +++ /dev/null @@ -1,25 +0,0 @@ -#if compiler(>=6) && canImport(Testing) - import Testing - import SnapshotTesting - - extension BaseSuite { - @Suite(.serialized, .snapshots(record: .missing)) - struct SwiftTestingTests { - @Test func testSnapshot() { - assertSnapshot(of: ["Hello", "World"], as: .dump, named: "snap") - withKnownIssue { - assertSnapshot(of: ["Goodbye", "World"], as: .dump, named: "snap") - } matching: { issue in - issue.description.hasSuffix( - """ - @@ −1,4 +1,4 @@ -  ▿ 2 elements - − - "Hello" - + - "Goodbye" -   - "World" - """) - } - } - } - } -#endif diff --git a/Tests/SnapshotTestingTests/UITests.swift b/Tests/SnapshotTestingTests/UITests.swift new file mode 100644 index 000000000..23c5ae9d8 --- /dev/null +++ b/Tests/SnapshotTestingTests/UITests.swift @@ -0,0 +1,245 @@ +import SnapshotTesting + +@testable import XCSnapshotTesting + +#if canImport(Testing) +import Testing + +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit + +typealias SDKView = UIView +typealias SDKLabel = UILabel +typealias SDKScrollView = UIScrollView +#elseif os(macOS) +import AppKit + +typealias SDKView = NSView +typealias SDKLabel = NSText +typealias SDKScrollView = NSScrollView +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) || os(macOS) +@MainActor +struct UITests { + + // MARK: - Base Configuration + private func setupViewWithSafeArea() -> SDKView { + let view = SDKView() + let contentView = SDKView() + contentView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(contentView) + + NSLayoutConstraint.activate([ + contentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + contentView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + contentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + contentView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + ]) + + return view + } + + #if !os(macOS) + @Test + func basicElements() async throws { + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + let label: SDKLabel = await MainActor.run { + let label = SDKLabel() + #if os(macOS) + label.string = "Hello World" + #else + label.text = "Hello World" + #endif + label.backgroundColor = .white + label.textColor = .black + return label + } + + try await assert(of: label, as: .image) + } + + group.addTask { + let scrollView: SDKScrollView = await MainActor.run { + let scrollView = SDKScrollView() + let contentView = SDKView() + let rectangleView = SDKView() + + rectangleView.backgroundColor = .red + + contentView.translatesAutoresizingMaskIntoConstraints = false + rectangleView.translatesAutoresizingMaskIntoConstraints = false + + scrollView.addSubview(contentView) + contentView.addSubview(rectangleView) + + NSLayoutConstraint.activate([ + contentView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor), + contentView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor), + contentView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor), + contentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor), + ]) + + NSLayoutConstraint.activate([ + rectangleView.topAnchor.constraint(equalTo: contentView.topAnchor), + rectangleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + rectangleView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + rectangleView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + rectangleView.heightAnchor.constraint(equalToConstant: 200), + ]) + + return scrollView + } + + try await assert(of: scrollView, as: .image(layout: .fixed(width: 400, height: 400))) + } + + for try await _ in group {} + } + } + #endif + + @Test + func dynamicUpdates() async throws { + let view = SDKView() + + let constraint = view.widthAnchor.constraint(equalToConstant: 600) + + NSLayoutConstraint.activate([ + view.heightAnchor.constraint(equalToConstant: 300), + constraint, + ]) + + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + constraint.constant *= 1.5 + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 4) { + constraint.constant /= 2 + } + + try await assert(of: view, as: .image(delay: 6)) + } + + #if !os(macOS) + @Test + func uiTraits() async throws { + try await withTestingEnvironment { + $0.disableInconsistentTraitsChecker = true + } operation: { + let view = SDKView() + #if os(visionOS) + view.backgroundColor = .init(dynamicProvider: { + if $0.userInterfaceStyle == .light { + return .red + } else { + return .blue + } + }) + #elseif !os(tvOS) + view.backgroundColor = .systemBackground + #else + view.backgroundColor = .systemGray + #endif + + let testCases = [ + (UIUserInterfaceStyle.light, "lightMode"), + (.dark, "darkMode"), + ] + + for (style, name) in testCases { + try await assert( + of: view, + as: .image(traits: .init(userInterfaceStyle: style)), + named: name + ) + } + } + } + + @Test + func accessibilityAndContentSize() async throws { + let label = SDKLabel() + label.text = "Hello World" + label.font = UIFont.preferredFont(forTextStyle: .body) + label.adjustsFontForContentSizeCategory = true + + let categories: [UIContentSizeCategory] = [ + .extraSmall, + .large, + .accessibilityExtraExtraExtraLarge, + ] + + try await withTestingEnvironment { + $0.disableInconsistentTraitsChecker = true + } operation: { + for category in categories { + try await assert( + of: label, + as: .image(traits: .init(preferredContentSizeCategory: category)), + named: category.rawValue + ) + } + } + } + + @Test + func deviceSpecificLayouts() async throws { + let view = setupViewWithSafeArea() + view.backgroundColor = .red + + try await assert( + of: view, + as: .image(layout: .device(.iPhone16Pro)), + named: "iPhone16Pro" + ) + } + #endif + + @Test + func complexLayouts() async throws { + let bigView = SDKView() + #if os(macOS) + bigView.wantsLayer = true + bigView.layer?.backgroundColor = NSColor.cyan.cgColor + #else + bigView.backgroundColor = .cyan + #endif + NSLayoutConstraint.activate([ + bigView.heightAnchor.constraint(equalToConstant: 3000), + bigView.widthAnchor.constraint(equalToConstant: 2000), + ]) + + try await assert(of: bigView, as: .image) + } + + #if !os(macOS) + @Test + func concurrentUpdates() async throws { + let view = setupViewWithSafeArea() + + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + try await assert( + of: view, + as: .image(layout: .device(.iPhone16Pro), delay: 1), + named: "1" + ) + } + group.addTask { + try await assert( + of: view, + as: .image(layout: .device(.iPhone16Pro), delay: 2), + named: "2" + ) + } + + for try await _ in group {} + } + } + #endif +} +#endif +#endif diff --git a/Tests/SnapshotTestingTests/WaitTests.swift b/Tests/SnapshotTestingTests/WaitTests.swift deleted file mode 100644 index 8bd4416cf..000000000 --- a/Tests/SnapshotTestingTests/WaitTests.swift +++ /dev/null @@ -1,18 +0,0 @@ -import XCTest - -@testable import SnapshotTesting - -class WaitTests: BaseTestCase { - func testWait() { - var value = "Hello" - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - value = "Goodbye" - } - - let strategy = Snapshotting.lines.pullback { (_: Void) in - value - } - - assertSnapshot(of: (), as: .wait(for: 1.5, on: strategy)) - } -} diff --git a/Tests/SnapshotTestingTests/WithSnapshotTestingTests.swift b/Tests/SnapshotTestingTests/WithSnapshotTestingTests.swift deleted file mode 100644 index 0116964ec..000000000 --- a/Tests/SnapshotTestingTests/WithSnapshotTestingTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -@_spi(Internals) @testable import SnapshotTesting -import XCTest - -class WithSnapshotTestingTests: XCTestCase { - func testNesting() { - withSnapshotTesting(record: .all) { - XCTAssertEqual( - SnapshotTestingConfiguration.current? - .diffTool?(currentFilePath: "old.png", failedFilePath: "new.png"), - """ - @− - "file://old.png" - @+ - "file://new.png" - - To configure output for a custom diff tool, use 'withSnapshotTesting'. For example: - - withSnapshotTesting(diffTool: .ksdiff) { - // ... - } - """ - ) - XCTAssertEqual(SnapshotTestingConfiguration.current?.record, .all) - withSnapshotTesting(diffTool: "ksdiff") { - XCTAssertEqual( - SnapshotTestingConfiguration.current? - .diffTool?(currentFilePath: "old.png", failedFilePath: "new.png"), - "ksdiff old.png new.png" - ) - XCTAssertEqual(SnapshotTestingConfiguration.current?.record, .all) - } - } - } -} diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.1.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.1.png deleted file mode 100644 index f3ba87bb0..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.1.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.2.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.2.png deleted file mode 100644 index 30f20312a..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.2.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPad-image.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPad-image.png deleted file mode 100644 index bc2d22c5b..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPad-image.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPhoneSE-image.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPhoneSE-image.png deleted file mode 100644 index e999e0bd5..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAssertMultipleSnapshot.iPhoneSE-image.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayerWithGradient.1.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayerWithGradient.1.png deleted file mode 100644 index 1e6fd8b4a..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testCALayerWithGradient.1.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.1.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.1.png deleted file mode 100644 index 5c2d5782c..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testNSView.1.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.macos.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.macos.png deleted file mode 100644 index e13d01303..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testPrecision.macos.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.1.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.1.png deleted file mode 100644 index 5f9d09b72..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.1.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.device.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.device.png deleted file mode 100644 index a7a695305..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.device.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.fixed.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.fixed.png deleted file mode 100644 index 90456c32b..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.fixed.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.size-that-fits.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.size-that-fits.png deleted file mode 100644 index 5f9d09b72..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testSwiftUIView_iOS.size-that-fits.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTableViewController.1.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTableViewController.1.png deleted file mode 100644 index e999e0bd5..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTableViewController.1.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-33-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-33-split-portrait.png deleted file mode 100644 index 464334b9a..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-33-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-50-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-50-split-landscape.png deleted file mode 100644 index ecacc5e1a..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-50-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-landscape.png deleted file mode 100644 index 730e87b04..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-portrait.png deleted file mode 100644 index b4e933566..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-66-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-alternative.png deleted file mode 100644 index a9fce4ac9..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-split-landscape.png deleted file mode 100644 index 79f54f158..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.png deleted file mode 100644 index 469fd0f05..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.txt b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.txt deleted file mode 100644 index 19def6c4b..000000000 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-10-2.txt +++ /dev/null @@ -1,5 +0,0 @@ -> - | > - | > - | > - | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-landscape.png deleted file mode 100644 index b2630e76c..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-portrait.png deleted file mode 100644 index 375c1618c..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-33-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-50-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-50-split-landscape.png deleted file mode 100644 index 8f9d26a24..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-50-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-landscape.png deleted file mode 100644 index 38c9e1c85..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-portrait.png deleted file mode 100644 index 07c08e0df..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-66-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-alternative.png deleted file mode 100644 index 80e6bfea3..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.png deleted file mode 100644 index 8ebe8f9ec..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.txt b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.txt deleted file mode 100644 index 5b61a8817..000000000 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-9-7.txt +++ /dev/null @@ -1,5 +0,0 @@ -> - | > - | > - | > - | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-landscape.png deleted file mode 100644 index b2630e76c..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-portrait.png deleted file mode 100644 index 375c1618c..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-33-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-50-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-50-split-landscape.png deleted file mode 100644 index 8f9d26a24..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-50-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-landscape.png deleted file mode 100644 index 38c9e1c85..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-portrait.png deleted file mode 100644 index 07c08e0df..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-66-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-alternative.png deleted file mode 100644 index 80e6bfea3..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.png deleted file mode 100644 index 8ebe8f9ec..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.txt b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.txt deleted file mode 100644 index 5b61a8817..000000000 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-mini.txt +++ /dev/null @@ -1,5 +0,0 @@ -> - | > - | > - | > - | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5-alternative.png deleted file mode 100644 index 395c94c23..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.png deleted file mode 100644 index 6d11d8aa5..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.txt b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.txt deleted file mode 100644 index ea083ba9c..000000000 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10-5.txt +++ /dev/null @@ -1,5 +0,0 @@ -> - | > - | > - | > - | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-landscape.png deleted file mode 100644 index 8c3186503..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-portrait.png deleted file mode 100644 index cde623e86..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-33-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-50-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-50-split-landscape.png deleted file mode 100644 index 24ebf6315..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-50-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-landscape.png deleted file mode 100644 index 51d6a0d63..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-portrait.png deleted file mode 100644 index f43d107b7..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-10inch-66-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11-alternative.png deleted file mode 100644 index 269bfed4d..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.png deleted file mode 100644 index b894bad6e..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.txt b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.txt deleted file mode 100644 index 5c67f05e9..000000000 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11.txt +++ /dev/null @@ -1,5 +0,0 @@ -> - | > - | > - | > - | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-landscape.png deleted file mode 100644 index 75676a9ba..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-portrait.png deleted file mode 100644 index 26a13c36a..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-33-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-50-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-50-split-landscape.png deleted file mode 100644 index 845c1abf3..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-50-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-landscape.png deleted file mode 100644 index ff25ea7b6..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-portrait.png deleted file mode 100644 index 6d78ae7c4..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-11inch-66-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9-alternative.png deleted file mode 100644 index 6b18c4ae4..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.png deleted file mode 100644 index 2d320c78f..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.txt b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.txt deleted file mode 100644 index 90452d45a..000000000 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12-9.txt +++ /dev/null @@ -1,5 +0,0 @@ -> - | > - | > - | > - | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-landscape.png deleted file mode 100644 index 925bd37b9..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-portrait.png deleted file mode 100644 index 07349ff69..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-33-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-50-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-50-split-landscape.png deleted file mode 100644 index db629f9ee..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-50-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-landscape.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-landscape.png deleted file mode 100644 index 4ac32c44b..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-landscape.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-portrait.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-portrait.png deleted file mode 100644 index 37cb825da..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.ipad-pro-12inch-66-split-portrait.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-alternative.png deleted file mode 100644 index 245d12fa4..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus-alternative.png deleted file mode 100644 index 8261256c5..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.png deleted file mode 100644 index cc48733c2..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8-plus.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.png deleted file mode 100644 index 6febd224a..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-8.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png deleted file mode 100644 index a7228d2a1..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-large.png deleted file mode 100644 index f1b7a849b..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-large.png deleted file mode 100644 index de5ebcef1..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-large.png deleted file mode 100644 index c9a1da073..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-medium.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-medium.png deleted file mode 100644 index 4351c4056..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-accessibility-medium.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-alternative.png deleted file mode 100644 index 9f18bd213..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-extra-large.png deleted file mode 100644 index c6d3a4d44..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-large.png deleted file mode 100644 index d83a40f16..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-large.png deleted file mode 100644 index 234c8e6d4..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-small.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-small.png deleted file mode 100644 index 02dd0439c..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-extra-small.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-large.png deleted file mode 100644 index 515b0fd54..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-medium.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-medium.png deleted file mode 100644 index 72bc39183..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-medium.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-small.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-small.png deleted file mode 100644 index 47915415d..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se-small.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.png deleted file mode 100644 index 72bc39183..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-se.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x-alternative.png deleted file mode 100644 index 018db9bc3..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.png deleted file mode 100644 index 1396adbc4..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-x.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr-alternative.png deleted file mode 100644 index 5ab39c339..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.png deleted file mode 100644 index 9df2d8701..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xr.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max-alternative.png deleted file mode 100644 index 5ab39c339..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.png deleted file mode 100644 index 1aa63d5d4..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.txt b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.txt deleted file mode 100644 index 78e7d7163..000000000 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraits.iphone-xs-max.txt +++ /dev/null @@ -1,5 +0,0 @@ -> - | > - | > - | > - | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png deleted file mode 100644 index bf32d2bfd..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png deleted file mode 100644 index 64b564be7..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png deleted file mode 100644 index d10bb98eb..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png deleted file mode 100644 index 815218b48..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png deleted file mode 100644 index d10bb98eb..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini.png deleted file mode 100644 index 815218b48..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-mini.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png deleted file mode 100644 index 93bbee347..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png deleted file mode 100644 index 4bfb737b0..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png deleted file mode 100644 index 32ff6dca6..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png deleted file mode 100644 index 24e37b7c1..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png deleted file mode 100644 index ff496be0d..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png deleted file mode 100644 index ffe569b65..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png deleted file mode 100644 index 892a26aca..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png deleted file mode 100644 index 7b1e2a3d0..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png deleted file mode 100644 index d970a4dbc..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8.png deleted file mode 100644 index aaa6100a8..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-8.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png deleted file mode 100644 index bff182823..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se.png deleted file mode 100644 index 4afb584a0..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-se.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png deleted file mode 100644 index 945b38f3b..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x.png deleted file mode 100644 index e71629cc3..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-x.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png deleted file mode 100644 index 7fce146bc..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr.png deleted file mode 100644 index 9bbb01367..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xr.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png deleted file mode 100644 index 7fce146bc..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png deleted file mode 100644 index b4d173ec9..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png deleted file mode 100644 index c1f778e14..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-large.png deleted file mode 100644 index a05852ed2..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-large.png deleted file mode 100644 index a1f73b045..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-large.png deleted file mode 100644 index 20bce4769..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-medium.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-medium.png deleted file mode 100644 index 11b9743ad..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-accessibility-medium.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-extra-large.png deleted file mode 100644 index 7a4ccf993..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-large.png deleted file mode 100644 index c73af818f..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-large.png deleted file mode 100644 index 0e6f5437e..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-small.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-small.png deleted file mode 100644 index 471e4b3bd..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-extra-small.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-large.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-large.png deleted file mode 100644 index c62fffc62..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-large.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-medium.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-medium.png deleted file mode 100644 index 00594cb89..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-medium.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-small.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-small.png deleted file mode 100644 index 4cc5c055a..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testTraitsWithView.label-small.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.1.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.1.png deleted file mode 100644 index d439f1271..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.1.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.2.txt b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.2.txt deleted file mode 100644 index de44a684f..000000000 --- a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIView.2.txt +++ /dev/null @@ -1,2 +0,0 @@ -> - | > \ No newline at end of file diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewAgainstEmptyImage.notEmptyImage.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewAgainstEmptyImage.notEmptyImage.png deleted file mode 100644 index 341e06ebd..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewAgainstEmptyImage.notEmptyImage.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noHeight.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noHeight.png deleted file mode 100644 index 0cff0cd29..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noHeight.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth-noHeight.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth-noHeight.png deleted file mode 100644 index 0cff0cd29..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth-noHeight.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth.png deleted file mode 100644 index 0cff0cd29..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testViewWithZeroHeightOrWidth.noWidth.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.ios.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.ios.png deleted file mode 100644 index 86f89027d..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebView.ios.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.ios.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.ios.png deleted file mode 100644 index 228b58c74..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithCancellingNavigationDelegate.ios.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.ios.png b/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.ios.png deleted file mode 100644 index 2e82b0cff..000000000 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testWebViewWithManipulatingNavigationDelegate.ios.png and /dev/null differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png new file mode 100644 index 000000000..0cecafc2e Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png new file mode 100644 index 000000000..0374f0b05 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png new file mode 100644 index 000000000..064825a29 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/basicElements.1.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/basicElements.1.png new file mode 100644 index 000000000..1bc29c843 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/basicElements.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/basicElements.2.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/basicElements.2.png new file mode 100644 index 000000000..c686d5890 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/basicElements.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/complexLayouts.1.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/complexLayouts.1.png new file mode 100644 index 000000000..14221058d Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/complexLayouts.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/concurrentUpdates.1.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/concurrentUpdates.1.png new file mode 100644 index 000000000..f687a8eb2 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/concurrentUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/concurrentUpdates.2.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/concurrentUpdates.2.png new file mode 100644 index 000000000..f687a8eb2 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/concurrentUpdates.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/deviceSpecificLayouts.iPhone16Pro.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/deviceSpecificLayouts.iPhone16Pro.png new file mode 100644 index 000000000..f9d7f854b Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/deviceSpecificLayouts.iPhone16Pro.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/dynamicUpdates.1.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/dynamicUpdates.1.png new file mode 100644 index 000000000..9cf306e5f Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/dynamicUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/uiTraits.darkMode.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/uiTraits.darkMode.png new file mode 100644 index 000000000..fccffb6e4 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/uiTraits.darkMode.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/uiTraits.lightMode.png b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/uiTraits.lightMode.png new file mode 100644 index 000000000..2724f9e27 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iOS/UITests/uiTraits.lightMode.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png new file mode 100644 index 000000000..9aad0e8fe Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png new file mode 100644 index 000000000..6b2bc65bb Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png new file mode 100644 index 000000000..013c5933c Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/basicElements.1.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/basicElements.1.png new file mode 100644 index 000000000..9f07678ed Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/basicElements.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/basicElements.2.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/basicElements.2.png new file mode 100644 index 000000000..2d4bae452 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/basicElements.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/complexLayouts.1.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/complexLayouts.1.png new file mode 100644 index 000000000..d5a5d4604 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/complexLayouts.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/concurrentUpdates.1.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/concurrentUpdates.1.png new file mode 100644 index 000000000..0bf5722fb Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/concurrentUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/concurrentUpdates.2.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/concurrentUpdates.2.png new file mode 100644 index 000000000..0bf5722fb Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/concurrentUpdates.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/deviceSpecificLayouts.iPhone16Pro.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/deviceSpecificLayouts.iPhone16Pro.png new file mode 100644 index 000000000..a35b62508 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/deviceSpecificLayouts.iPhone16Pro.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/dynamicUpdates.1.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/dynamicUpdates.1.png new file mode 100644 index 000000000..2a2e4bf45 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/dynamicUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/uiTraits.darkMode.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/uiTraits.darkMode.png new file mode 100644 index 000000000..f5ea06124 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/uiTraits.darkMode.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/uiTraits.lightMode.png b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/uiTraits.lightMode.png new file mode 100644 index 000000000..dd722ee21 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/iPadOS/UITests/uiTraits.lightMode.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/macOS/UITests/complexLayouts.1.png b/Tests/SnapshotTestingTests/__Snapshots__/macOS/UITests/complexLayouts.1.png new file mode 100644 index 000000000..1faab24f5 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/macOS/UITests/complexLayouts.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/macOS/UITests/dynamicUpdates.1.png b/Tests/SnapshotTestingTests/__Snapshots__/macOS/UITests/dynamicUpdates.1.png new file mode 100644 index 000000000..aa831ed5c Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/macOS/UITests/dynamicUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png new file mode 100644 index 000000000..aa75d7f02 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png new file mode 100644 index 000000000..aa75d7f02 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png new file mode 100644 index 000000000..aa75d7f02 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/basicElements.1.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/basicElements.1.png new file mode 100644 index 000000000..f640d6279 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/basicElements.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/basicElements.2.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/basicElements.2.png new file mode 100644 index 000000000..2d4bae452 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/basicElements.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/complexLayouts.1.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/complexLayouts.1.png new file mode 100644 index 000000000..3ad38f665 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/complexLayouts.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/concurrentUpdates.1.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/concurrentUpdates.1.png new file mode 100644 index 000000000..0bf5722fb Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/concurrentUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/concurrentUpdates.2.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/concurrentUpdates.2.png new file mode 100644 index 000000000..0bf5722fb Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/concurrentUpdates.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/deviceSpecificLayouts.iPhone16Pro.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/deviceSpecificLayouts.iPhone16Pro.png new file mode 100644 index 000000000..a35b62508 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/deviceSpecificLayouts.iPhone16Pro.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/dynamicUpdates.1.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/dynamicUpdates.1.png new file mode 100644 index 000000000..2a2e4bf45 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/dynamicUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/uiTraits.darkMode.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/uiTraits.darkMode.png new file mode 100644 index 000000000..4c9f8e955 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/uiTraits.darkMode.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/uiTraits.lightMode.png b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/uiTraits.lightMode.png new file mode 100644 index 000000000..4c9f8e955 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/tvOS/UITests/uiTraits.lightMode.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png new file mode 100644 index 000000000..04a90c546 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryAccessibilityXXXL.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png new file mode 100644 index 000000000..9dcab76a5 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryL.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png new file mode 100644 index 000000000..b8ab5fce4 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/accessibilityAndContentSize.UICTContentSizeCategoryXS.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/basicElements.1.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/basicElements.1.png new file mode 100644 index 000000000..7feb0b24e Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/basicElements.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/basicElements.2.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/basicElements.2.png new file mode 100644 index 000000000..4dafc25b0 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/basicElements.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/complexLayouts.1.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/complexLayouts.1.png new file mode 100644 index 000000000..0bfd7ec49 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/complexLayouts.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.1.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/concurrentUpdates.1.png similarity index 84% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.1.png rename to Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/concurrentUpdates.1.png index 0e3c83371..47bb2d351 100644 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.1.png and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/concurrentUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.2.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/concurrentUpdates.2.png similarity index 84% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.2.png rename to Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/concurrentUpdates.2.png index 3e0f6e0ad..47bb2d351 100644 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testUIViewControllerLifeCycle.2.png and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/concurrentUpdates.2.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/deviceSpecificLayouts.iPhone16Pro.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/deviceSpecificLayouts.iPhone16Pro.png new file mode 100644 index 000000000..2f4aae89f Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/deviceSpecificLayouts.iPhone16Pro.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/dynamicUpdates.1.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/dynamicUpdates.1.png new file mode 100644 index 000000000..40debe96e Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/dynamicUpdates.1.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/uiTraits.darkMode.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/uiTraits.darkMode.png new file mode 100644 index 000000000..163819a2c Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/uiTraits.darkMode.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/uiTraits.lightMode.png b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/uiTraits.lightMode.png new file mode 100644 index 000000000..601a90286 Binary files /dev/null and b/Tests/SnapshotTestingTests/__Snapshots__/visionOS/UITests/uiTraits.lightMode.png differ diff --git a/Tests/XCSnapshotTestingTests/FoundationTests.swift b/Tests/XCSnapshotTestingTests/FoundationTests.swift new file mode 100644 index 000000000..74e988af6 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/FoundationTests.swift @@ -0,0 +1,137 @@ +@preconcurrency import Foundation +import XCSnapshotTesting + +#if canImport(XCTest) +@preconcurrency import XCTest + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +final class FoundationTests: BaseTestCase { + + @available(macOS 10.13, tvOS 11.0, *) + func testAnyAsJson() throws { + struct User: Encodable { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + + let data = try JSONEncoder().encode(user) + let any = try JSONSerialization.jsonObject(with: data, options: []) + + try assert(of: any, as: .json) + } + + func testCaseIterable() throws { + enum Direction: String, CaseIterable { + case up, down, left, right + var rotatedLeft: Direction { + switch self { + case .up: return .left + case .down: return .right + case .left: return .down + case .right: return .up + } + } + } + + try assert( + of: { $0.rotatedLeft }, + as: SyncSnapshot.func(into: .description) + ) + } + + func testData() async throws { + let data = Data([0xDE, 0xAD, 0xBE, 0xEF]) + + try assert(of: data, as: .data) + } + + func testEncodable() throws { + struct User: Encodable { let id: Int, name: String, bio: String } + let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.") + + if #available(iOS 11.0, macOS 10.13, tvOS 11.0, *) { + try assert(of: user, as: .json) + } + try assert(of: user, as: .plist) + } + + func testURLRequest() throws { + var get = URLRequest(url: URL(string: "https://www.pointfree.co/")!) + get.addValue("pf_session={}", forHTTPHeaderField: "Cookie") + get.addValue("text/html", forHTTPHeaderField: "Accept") + get.addValue("application/json", forHTTPHeaderField: "Content-Type") + try assert(of: get, as: .raw, named: "get") + try assert(of: get, as: .curl, named: "get-curl") + + var getWithQuery = URLRequest( + url: URL(string: "https://www.pointfree.co?key_2=value_2&key_1=value_1&key_3=value_3")! + ) + getWithQuery.addValue("pf_session={}", forHTTPHeaderField: "Cookie") + getWithQuery.addValue("text/html", forHTTPHeaderField: "Accept") + getWithQuery.addValue("application/json", forHTTPHeaderField: "Content-Type") + try assert( + of: getWithQuery, + as: .raw, + named: "get-with-query" + ) + try assert( + of: getWithQuery, + as: .curl, + named: "get-with-query-curl" + ) + + var post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!) + post.httpMethod = "POST" + post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie") + post.addValue("text/html", forHTTPHeaderField: "Accept") + post.httpBody = Data("pricing[billing]=monthly&pricing[lane]=individual".utf8) + try assert( + of: post, + as: .raw, + named: "post" + ) + try assert( + of: post, + as: .curl, + named: "post-curl" + ) + + var postWithJSON = URLRequest( + url: URL(string: "http://dummy.restapiexample.com/api/v1/create")! + ) + postWithJSON.httpMethod = "POST" + postWithJSON.addValue("application/json", forHTTPHeaderField: "Content-Type") + postWithJSON.addValue("application/json", forHTTPHeaderField: "Accept") + postWithJSON.httpBody = Data( + "{\"name\":\"tammy134235345235\", \"salary\":0, \"age\":\"tammy133\"}".utf8 + ) + try assert( + of: postWithJSON, + as: .raw, + named: "post-with-json" + ) + try assert( + of: postWithJSON, + as: .curl, + named: "post-with-json-curl" + ) + + var head = URLRequest(url: URL(string: "https://www.pointfree.co/")!) + head.httpMethod = "HEAD" + head.addValue("pf_session={}", forHTTPHeaderField: "Cookie") + try assert(of: head, as: .raw, named: "head") + try assert(of: head, as: .curl, named: "head-curl") + + post = URLRequest(url: URL(string: "https://www.pointfree.co/subscribe")!) + post.httpMethod = "POST" + post.addValue("pf_session={\"user_id\":\"0\"}", forHTTPHeaderField: "Cookie") + post.addValue("application/json", forHTTPHeaderField: "Accept") + post.httpBody = Data( + """ + {"pricing": {"lane": "individual","billing": "monthly"}} + """.utf8 + ) + } +} +#endif diff --git a/Tests/XCSnapshotTestingTests/Internal/BaseTestCase.swift b/Tests/XCSnapshotTestingTests/Internal/BaseTestCase.swift new file mode 100644 index 000000000..e4061638e --- /dev/null +++ b/Tests/XCSnapshotTestingTests/Internal/BaseTestCase.swift @@ -0,0 +1,26 @@ +@_spi(Internals) import XCSnapshotTesting + +#if canImport(XCTest) +@preconcurrency import XCTest + +class BaseTestCase: XCTestCase { + + var platform: String? { + "" + } + + var record: RecordMode { + .failed + } + + override func invokeTest() { + withTestingEnvironment( + record: record, + diffTool: .ksdiff, + platform: platform + ) { + super.invokeTest() + } + } +} +#endif diff --git a/Tests/XCSnapshotTestingTests/Internal/TestHelpers.swift b/Tests/XCSnapshotTestingTests/Internal/TestHelpers.swift new file mode 100644 index 000000000..5e9098110 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/Internal/TestHelpers.swift @@ -0,0 +1,109 @@ +@testable import XCSnapshotTesting + +#if os(iOS) || os(tvOS) || os(visionOS) +import UIKit +#elseif os(macOS) +import AppKit +#endif + +#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) +extension CGPath { + /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: + static var heart: CGPath { + let scale: CGFloat = 30.0 + let path = CGMutablePath() + + path.move(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) + path.addLine(to: CGPoint(x: 0.0 * scale, y: 2.0 * scale)) + path.addQuadCurve( + to: CGPoint(x: 1.0 * scale, y: 3.0 * scale), + control: CGPoint(x: 0.125 * scale, y: 2.875 * scale) + ) + path.addQuadCurve( + to: CGPoint(x: 2.0 * scale, y: 2.0 * scale), + control: CGPoint(x: 1.875 * scale, y: 2.875 * scale) + ) + path.addCurve( + to: CGPoint(x: 3.0 * scale, y: 1.0 * scale), + control1: CGPoint(x: 2.5 * scale, y: 2.0 * scale), + control2: CGPoint(x: 3.0 * scale, y: 1.5 * scale) + ) + path.addCurve( + to: CGPoint(x: 2.0 * scale, y: 0.0 * scale), + control1: CGPoint(x: 3.0 * scale, y: 0.5 * scale), + control2: CGPoint(x: 2.5 * scale, y: 0.0 * scale) + ) + path.addLine(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) + path.closeSubpath() + + path.addEllipse( + in: CGRect( + origin: CGPoint(x: 2.0 * scale, y: 2.0 * scale), + size: CGSize(width: scale, height: scale) + ) + ) + + return path + } +} +#endif + +#if os(iOS) || os(tvOS) || os(visionOS) +extension UIBezierPath { + /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: + static var heart: UIBezierPath { + UIBezierPath(cgPath: .heart) + } +} +#endif + +#if os(macOS) +extension NSTextField { + var text: String { + get { self.stringValue } + set { self.stringValue = newValue } + } +} + +extension NSBezierPath { + /// Creates an approximation of a heart at a 45º angle with a circle above, using all available element types: + static var heart: NSBezierPath { + let scale: CGFloat = 30.0 + let path = NSBezierPath() + + path.move(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) + path.line(to: CGPoint(x: 0.0 * scale, y: 2.0 * scale)) + path.curve( + to: CGPoint(x: 1.0 * scale, y: 3.0 * scale), + controlPoint1: CGPoint(x: 0.0 * scale, y: 2.5 * scale), + controlPoint2: CGPoint(x: 0.5 * scale, y: 3.0 * scale) + ) + path.curve( + to: CGPoint(x: 2.0 * scale, y: 2.0 * scale), + controlPoint1: CGPoint(x: 1.5 * scale, y: 3.0 * scale), + controlPoint2: CGPoint(x: 2.0 * scale, y: 2.5 * scale) + ) + path.curve( + to: CGPoint(x: 3.0 * scale, y: 1.0 * scale), + controlPoint1: CGPoint(x: 2.5 * scale, y: 2.0 * scale), + controlPoint2: CGPoint(x: 3.0 * scale, y: 1.5 * scale) + ) + path.curve( + to: CGPoint(x: 2.0 * scale, y: 0.0 * scale), + controlPoint1: CGPoint(x: 3.0 * scale, y: 0.5 * scale), + controlPoint2: CGPoint(x: 2.5 * scale, y: 0.0 * scale) + ) + path.line(to: CGPoint(x: 0.0 * scale, y: 0.0 * scale)) + path.close() + + path.appendOval( + in: CGRect( + origin: CGPoint(x: 2.0 * scale, y: 2.0 * scale), + size: CGSize(width: scale, height: scale) + ) + ) + + return path + } +} +#endif diff --git a/Tests/XCSnapshotTestingTests/UITests.swift b/Tests/XCSnapshotTestingTests/UITests.swift new file mode 100644 index 000000000..af297668d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/UITests.swift @@ -0,0 +1,1587 @@ +#if os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) || os(macOS) +import XCTest +import SwiftUI +import SceneKit +import SpriteKit +#if !os(tvOS) && !os(watchOS) +@preconcurrency import WebKit +#endif +#if os(macOS) +@preconcurrency import AppKit +#else +import UIKit +#endif +@testable import XCSnapshotTesting + +@MainActor +final class UITests: BaseTestCase { + + override var platform: String? { + nil + } + + #if os(iOS) || os(tvOS) || os(visionOS) + @MainActor + func testAutolayout() async throws { + let vc = UIViewController() + vc.view.translatesAutoresizingMaskIntoConstraints = false + let subview = UIView() + subview.translatesAutoresizingMaskIntoConstraints = false + vc.view.addSubview(subview) + NSLayoutConstraint.activate([ + subview.topAnchor.constraint(equalTo: vc.view.topAnchor), + subview.bottomAnchor.constraint(equalTo: vc.view.bottomAnchor), + subview.leftAnchor.constraint(equalTo: vc.view.leftAnchor), + subview.rightAnchor.constraint(equalTo: vc.view.rightAnchor), + ]) + try await assert(of: vc, as: .image) + } + + @MainActor + func testTableViewController() async throws { + class TableViewController: UITableViewController { + override func viewDidLoad() { + super.viewDidLoad() + self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") + } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + 10 + } + override func tableView( + _ tableView: UITableView, + cellForRowAt indexPath: IndexPath + ) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) + cell.textLabel?.text = "\(indexPath.row)" + return cell + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + tableView.reloadData() + } + } + try await assert( + of: await TableViewController(), + as: .image(layout: .device(.iPhoneSE)) + ) + } + + @MainActor + func testAssertMultipleSnapshot() async throws { + class TableViewController: UITableViewController { + override func viewDidLoad() { + super.viewDidLoad() + self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") + } + + override func tableView( + _ tableView: UITableView, + numberOfRowsInSection section: Int + ) -> Int { + 10 + } + + override func tableView( + _ tableView: UITableView, + cellForRowAt indexPath: IndexPath + ) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) + cell.textLabel?.text = "\(indexPath.row)" + return cell + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + tableView.reloadData() + } + } + + try await assert( + of: await TableViewController(), + as: [ + "iPad-image": .image(layout: .device(.iPadMini8_3)), + "iPhoneSe-image": .image(layout: .device(.iPhoneSE)), + ] + ) + try await assert( + of: await TableViewController(), + as: [ + .image(layout: .device(.iPhoneX)), + .image(layout: .device(.iPhoneXSMax)), + ] + ) + } + + @MainActor + func testTraits() async throws { + class MyViewController: UIViewController { + let topLabel = UILabel() + let leadingLabel = UILabel() + let trailingLabel = UILabel() + let bottomLabel = UILabel() + + override func viewDidLoad() { + super.viewDidLoad() + + self.navigationItem.leftBarButtonItem = .init( + barButtonSystemItem: .add, + target: nil, + action: nil + ) + + self.view.backgroundColor = .white + + self.topLabel.text = "What's" + self.leadingLabel.text = "the" + self.trailingLabel.text = "point" + self.bottomLabel.text = "?" + + topLabel.font = .preferredFont(forTextStyle: .headline) + leadingLabel.font = .preferredFont(forTextStyle: .body) + trailingLabel.font = .preferredFont(forTextStyle: .body) + bottomLabel.font = .preferredFont(forTextStyle: .subheadline) + + [topLabel, leadingLabel, trailingLabel, bottomLabel].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.adjustsFontForContentSizeCategory = true + + self.view.addSubview($0) + + $0.setContentHuggingPriority(.required, for: .vertical) + $0.setContentHuggingPriority(.required, for: .horizontal) + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentCompressionResistancePriority(.required, for: .horizontal) + } + + NSLayoutConstraint.activate([ + self.topLabel.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), + self.topLabel.centerXAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + self.leadingLabel.leadingAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.leadingAnchor + ), + self.leadingLabel.trailingAnchor.constraint( + lessThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + self.leadingLabel.centerYAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerYAnchor + ), + self.trailingLabel.leadingAnchor.constraint( + greaterThanOrEqualTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + self.trailingLabel.trailingAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.trailingAnchor + ), + self.trailingLabel.centerYAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerYAnchor + ), + self.bottomLabel.bottomAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.bottomAnchor + ), + self.bottomLabel.centerXAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.centerXAnchor + ), + ]) + } + } + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneSE)), + named: "iphone-se" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhone8)), + named: "iphone-8" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhone8Plus)), + named: "iphone-8-plus" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneX)), + named: "iphone-x" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneXR)), + named: "iphone-xr" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneXSMax)), + named: "iphone-xs-max" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadMini8_3)), + named: "ipad-mini" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad9_7)), + named: "ipad-9-7" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad10_2)), + named: "ipad-10-2" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro10_5)), + named: "ipad-pro-10-5" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro11)), + named: "ipad-pro-11" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro12_9)), + named: "ipad-pro-12-9" + ) + + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPhoneSE)), + named: "iphone-se" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPhone8)), + named: "iphone-8" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPhone8Plus)), + named: "iphone-8-plus" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPhoneX)), + named: "iphone-x" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPhoneXR)), + named: "iphone-xr" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPhoneXSMax)), + named: "iphone-xs-max" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPadMini8_3)), + named: "ipad-mini" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPad9_7)), + named: "ipad-9-7" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPad10_2)), + named: "ipad-10-2" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPadPro10_5)), + named: "ipad-pro-10-5" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPadPro11)), + named: "ipad-pro-11" + ) + try await assert( + of: await MyViewController(), + as: .recursiveDescription(layout: .device(.iPadPro12_9)), + named: "ipad-pro-12-9" + ) + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneSE(.portrait))), + named: "iphone-se" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhone8(.portrait))), + named: "iphone-8" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhone8Plus(.portrait))), + named: "iphone-8-plus" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneX(.portrait))), + named: "iphone-x" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneXR(.portrait))), + named: "iphone-xr" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneXSMax(.portrait))), + named: "iphone-xs-max" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadMini8_3(.landscape))), + named: "ipad-mini" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad9_7(.landscape))), + named: "ipad-9-7" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad10_2(.landscape))), + named: "ipad-10-2" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro10_5(.landscape))), + named: "ipad-pro-10-5" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro11(.landscape))), + named: "ipad-pro-11" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro12_9(.landscape))), + named: "ipad-pro-12-9" + ) + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadMini8_3(.landscape(.compact)))), + named: "ipad-mini-33-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadMini8_3(.landscape(.medium)))), + named: "ipad-mini-50-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadMini8_3(.landscape(.regular)))), + named: "ipad-mini-66-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadMini8_3(.portrait(.compact)))), + named: "ipad-mini-33-split-portrait" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadMini8_3(.portrait(.regular)))), + named: "ipad-mini-66-split-portrait" + ) + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad9_7(.landscape(.compact)))), + named: "ipad-9-7-33-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad9_7(.landscape(.medium)))), + named: "ipad-9-7-50-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad9_7(.landscape(.regular)))), + named: "ipad-9-7-66-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad9_7(.portrait(.compact)))), + named: "ipad-9-7-33-split-portrait" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad9_7(.portrait(.regular)))), + named: "ipad-9-7-66-split-portrait" + ) + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad10_2(.landscape(.compact)))), + named: "ipad-10-2-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad10_2(.landscape(.medium)))), + named: "ipad-10-2-50-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad10_2(.landscape(.regular)))), + named: "ipad-10-2-66-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad10_2(.portrait(.compact)))), + named: "ipad-10-2-33-split-portrait" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad10_2(.portrait(.regular)))), + named: "ipad-10-2-66-split-portrait" + ) + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro10_5(.landscape(.compact)))), + named: "ipad-pro-10inch-33-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro10_5(.landscape(.medium)))), + named: "ipad-pro-10inch-50-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro10_5(.landscape(.regular)))), + named: "ipad-pro-10inch-66-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro10_5(.portrait(.compact)))), + named: "ipad-pro-10inch-33-split-portrait" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro10_5(.portrait(.regular)))), + named: "ipad-pro-10inch-66-split-portrait" + ) + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro11(.landscape(.compact)))), + named: "ipad-pro-11inch-33-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro11(.landscape(.medium)))), + named: "ipad-pro-11inch-50-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro11(.landscape(.regular)))), + named: "ipad-pro-11inch-66-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro11(.portrait(.compact)))), + named: "ipad-pro-11inch-33-split-portrait" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro11(.portrait(.regular)))), + named: "ipad-pro-11inch-66-split-portrait" + ) + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro12_9(.landscape(.compact)))), + named: "ipad-pro-12inch-33-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro12_9(.landscape(.medium)))), + named: "ipad-pro-12inch-50-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro12_9(.landscape(.regular)))), + named: "ipad-pro-12inch-66-split-landscape" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro12_9(.portrait(.compact)))), + named: "ipad-pro-12inch-33-split-portrait" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro12_9(.portrait(.regular)))), + named: "ipad-pro-12inch-66-split-portrait" + ) + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneSE(.landscape))), + named: "iphone-se-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhone8(.landscape))), + named: "iphone-8-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhone8Plus(.landscape))), + named: "iphone-8-plus-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneX(.landscape))), + named: "iphone-x-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneXR(.landscape))), + named: "iphone-xr-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPhoneXSMax(.landscape))), + named: "iphone-xs-max-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadMini8_3(.portrait))), + named: "ipad-mini-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad9_7(.portrait))), + named: "ipad-9-7-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPad10_2(.portrait))), + named: "ipad-10-2-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro10_5(.portrait))), + named: "ipad-pro-10-5-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro11(.portrait))), + named: "ipad-pro-11-alternative" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.iPadPro12_9(.portrait))), + named: "ipad-pro-12-9-alternative" + ) + + for (name, contentSize) in allContentSizes { + try await assert( + of: await MyViewController(), + as: .image( + layout: .device(.iPhoneSE), + traits: .init(preferredContentSizeCategory: contentSize) + ), + named: "iphone-se-\(name)" + ) + } + + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.tv)), + named: "tv" + ) + try await assert( + of: await MyViewController(), + as: .image(layout: .device(.tv4K)), + named: "tv4k" + ) + } + + @MainActor + func testTraitsEmbeddedInTabNavigation() async throws { + class MyViewController: UIViewController { + let topLabel = UILabel() + let leadingLabel = UILabel() + let trailingLabel = UILabel() + let bottomLabel = UILabel() + + override func viewDidLoad() { + super.viewDidLoad() + + let safeAreaView = UIView() + safeAreaView.backgroundColor = .green + safeAreaView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(safeAreaView) + + NSLayoutConstraint.activate([ + safeAreaView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + safeAreaView.leadingAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.leadingAnchor + ), + safeAreaView.trailingAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.trailingAnchor + ), + safeAreaView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + ]) + + self.navigationItem.leftBarButtonItem = .init( + barButtonSystemItem: .add, + target: nil, + action: nil + ) + + self.view.backgroundColor = .yellow + + topLabel.text = "What's" + leadingLabel.text = "the" + trailingLabel.text = "point" + bottomLabel.text = "?" + + [topLabel, leadingLabel, trailingLabel, bottomLabel].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + view.addSubview($0) + + $0.setContentHuggingPriority(.required, for: .vertical) + $0.setContentHuggingPriority(.required, for: .horizontal) + } + + NSLayoutConstraint.activate([ + topLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + topLabel.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor), + + leadingLabel.leadingAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.leadingAnchor + ), + leadingLabel.trailingAnchor.constraint( + lessThanOrEqualTo: view.safeAreaLayoutGuide.centerXAnchor + ), + leadingLabel.centerYAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.centerYAnchor + ), + + trailingLabel.leadingAnchor.constraint( + greaterThanOrEqualTo: view.safeAreaLayoutGuide.centerXAnchor + ), + trailingLabel.trailingAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.trailingAnchor + ), + trailingLabel.centerYAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.centerYAnchor + ), + + bottomLabel.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + bottomLabel.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor), + ]) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + self.topLabel.font = .preferredFont( + forTextStyle: .headline, + compatibleWith: self.traitCollection + ) + self.leadingLabel.font = .preferredFont( + forTextStyle: .body, + compatibleWith: self.traitCollection + ) + self.trailingLabel.font = .preferredFont( + forTextStyle: .body, + compatibleWith: self.traitCollection + ) + self.bottomLabel.font = .preferredFont( + forTextStyle: .subheadline, + compatibleWith: self.traitCollection + ) + self.view.setNeedsUpdateConstraints() + self.view.updateConstraintsIfNeeded() + } + } + + class NoRecycleViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + let myViewController = MyViewController() + let navController = UINavigationController(rootViewController: myViewController) + let tabViewController = UITabBarController() + tabViewController.setViewControllers([navController], animated: false) + + let viewController = self + viewController.addChild(tabViewController) + tabViewController.view.translatesAutoresizingMaskIntoConstraints = false + viewController.view.addSubview(tabViewController.view) + tabViewController.didMove(toParent: viewController) + + NSLayoutConstraint.activate([ + tabViewController.view.topAnchor.constraint(equalTo: viewController.view.topAnchor), + tabViewController.view.leadingAnchor.constraint( + equalTo: viewController.view.leadingAnchor + ), + tabViewController.view.trailingAnchor.constraint( + equalTo: viewController.view.trailingAnchor + ), + tabViewController.view.bottomAnchor.constraint( + equalTo: viewController.view.bottomAnchor + ), + ]) + + navController.navigationBar.isTranslucent = false + navController.navigationBar.tintColor = .red + navController.navigationBar.backgroundColor = .red + navController.navigationBar.barTintColor = .red + navController.view.backgroundColor = .blue + } + } + + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneSE)), + named: "iphone-se" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhone8)), + named: "iphone-8" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhone8Plus)), + named: "iphone-8-plus" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneX)), + named: "iphone-x" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneXR)), + named: "iphone-xr" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneXSMax)), + named: "iphone-xs-max" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadMini8_3)), + named: "ipad-mini" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPad9_7)), + named: "ipad-9-7" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPad10_2)), + named: "ipad-10-2" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro10_5)), + named: "ipad-pro-10-5" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro11)), + named: "ipad-pro-11" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro12_9)), + named: "ipad-pro-12-9" + ) + + try await withTestingEnvironment(record: .never) { + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneSE(.portrait))), + named: "iphone-se" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhone8(.portrait))), + named: "iphone-8" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhone8Plus(.portrait))), + named: "iphone-8-plus" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneX(.portrait))), + named: "iphone-x" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneXR(.portrait))), + named: "iphone-xr" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneXSMax(.portrait))), + named: "iphone-xs-max" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadMini8_3(.landscape))), + named: "ipad-mini" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPad9_7(.landscape))), + named: "ipad-9-7" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPad10_2(.landscape))), + named: "ipad-10-2" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro10_5(.landscape))), + named: "ipad-pro-10-5" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro11(.landscape))), + named: "ipad-pro-11" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro12_9(.landscape))), + named: "ipad-pro-12-9" + ) + } + + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneSE(.landscape))), + named: "iphone-se-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhone8(.landscape))), + named: "iphone-8-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhone8Plus(.landscape))), + named: "iphone-8-plus-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneX(.landscape))), + named: "iphone-x-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneXR(.landscape))), + named: "iphone-xr-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPhoneXSMax(.landscape))), + named: "iphone-xs-max-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadMini8_3(.portrait))), + named: "ipad-mini-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPad9_7(.portrait))), + named: "ipad-9-7-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPad10_2(.portrait))), + named: "ipad-10-2-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro10_5(.portrait))), + named: "ipad-pro-10-5-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro11(.portrait))), + named: "ipad-pro-11-alternative" + ) + try await assert( + of: await NoRecycleViewController(), + as: .image(layout: .device(.iPadPro12_9(.portrait))), + named: "ipad-pro-12-9-alternative" + ) + } + + @MainActor + func testCollectionViewsWithMultipleScreenSizes() async throws { + final class CollectionViewController: UIViewController, UICollectionViewDataSource, + UICollectionViewDelegateFlowLayout + { + + let flowLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.minimumLineSpacing = 20 + return layout + }() + + lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + view.addSubview(collectionView) + + collectionView.backgroundColor = .white + collectionView.dataSource = self + collectionView.delegate = self + collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell") + collectionView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + collectionView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), + collectionView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), + collectionView.trailingAnchor.constraint( + equalTo: view.layoutMarginsGuide.trailingAnchor + ), + collectionView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor), + ]) + + collectionView.reloadData() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + collectionView.collectionViewLayout.invalidateLayout() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + collectionView.collectionViewLayout.invalidateLayout() + } + + func collectionView( + _ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath + ) + -> UICollectionViewCell + { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) + cell.contentView.backgroundColor = .orange + return cell + } + + func collectionView( + _ collectionView: UICollectionView, + numberOfItemsInSection section: Int + ) + -> Int + { + 20 + } + + func collectionView( + _ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + sizeForItemAt indexPath: IndexPath + ) -> CGSize { + CGSize( + width: min(collectionView.frame.width - 50, 300), + height: collectionView.frame.height + ) + } + } + + try await assert( + of: await CollectionViewController(), + as: [ + "ipad": .image(layout: .device(.iPadPro12_9)), + "iphoneSe": .image(layout: .device(.iPhoneSE)), + "iphone8": .image(layout: .device(.iPhone8)), + "iphoneMax": .image(layout: .device(.iPhoneXSMax)), + ] + ) + } + + @MainActor + func testTraitsWithView() async throws { + + var label: UILabel { + get async { + await MainActor.run { + let label = UILabel() + label.font = .preferredFont(forTextStyle: .title1) + label.adjustsFontForContentSizeCategory = true + label.text = "What's the point?" + return label + } + } + } + + for (name, contentSize) in allContentSizes { + try await assert( + of: await label, + as: .image(traits: .init(preferredContentSizeCategory: contentSize)), + named: "label-\(name)" + ) + } + } + + @MainActor + func testTraitsWithViewController() async throws { + class MyViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + let label = UILabel() + label.font = .preferredFont(forTextStyle: .title1) + label.adjustsFontForContentSizeCategory = true + label.text = "What's the point?" + + label.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(label) + + label.setContentHuggingPriority(.required, for: .vertical) + label.setContentCompressionResistancePriority(.required, for: .vertical) + label.setContentCompressionResistancePriority(.required, for: .horizontal) + + NSLayoutConstraint.activate([ + label.leadingAnchor.constraint( + equalTo: view.layoutMarginsGuide.leadingAnchor + ), + label.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), + label.trailingAnchor.constraint( + equalTo: view.layoutMarginsGuide.trailingAnchor + ), + ]) + } + } + + for (name, contentSize) in allContentSizes { + try await assert( + of: await MyViewController(), + as: .recursiveDescription( + layout: .device(.iPhoneSE), + traits: .init(preferredContentSizeCategory: contentSize) + ), + named: "label-\(name)", + record: .never + ) + } + } + + @MainActor + func testUIView() async throws { + let view = UIButton(type: .contactAdd) + try await assert(of: view, as: .image) + try await assert(of: view, as: .recursiveDescription) + } + + @MainActor + func testUIViewControllerLifeCycle() async throws { + class ViewController: UIViewController { + + let viewDidLoadExpectation: XCTestExpectation + let viewWillAppearExpectation: XCTestExpectation + let viewDidAppearExpectation: XCTestExpectation + let viewWillDisappearExpectation: XCTestExpectation + let viewDidDisappearExpectation: XCTestExpectation + + init( + viewDidLoadExpectation: XCTestExpectation, + viewWillAppearExpectation: XCTestExpectation, + viewDidAppearExpectation: XCTestExpectation, + viewWillDisappearExpectation: XCTestExpectation, + viewDidDisappearExpectation: XCTestExpectation + ) { + self.viewDidLoadExpectation = viewDidLoadExpectation + self.viewWillAppearExpectation = viewWillAppearExpectation + self.viewDidAppearExpectation = viewDidAppearExpectation + self.viewWillDisappearExpectation = viewWillDisappearExpectation + self.viewDidDisappearExpectation = viewDidDisappearExpectation + super.init(nibName: nil, bundle: nil) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { + super.viewDidLoad() + viewDidLoadExpectation.fulfill() + } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + viewWillAppearExpectation.fulfill() + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + viewDidAppearExpectation.fulfill() + } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + viewWillDisappearExpectation.fulfill() + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + viewDidDisappearExpectation.fulfill() + } + } + + let viewDidLoadExpectation = expectation(description: "viewDidLoad") + let viewWillAppearExpectation = expectation(description: "viewWillAppear") + let viewDidAppearExpectation = expectation(description: "viewDidAppear") + let viewWillDisappearExpectation = expectation(description: "viewWillDisappear") + let viewDidDisappearExpectation = expectation(description: "viewDidDisappear") + + viewDidLoadExpectation.expectedFulfillmentCount = 1 + viewWillAppearExpectation.expectedFulfillmentCount = 2 + viewDidAppearExpectation.expectedFulfillmentCount = 2 + viewWillDisappearExpectation.expectedFulfillmentCount = 2 + viewDidDisappearExpectation.expectedFulfillmentCount = 2 + + let viewController = ViewController( + viewDidLoadExpectation: viewDidLoadExpectation, + viewWillAppearExpectation: viewWillAppearExpectation, + viewDidAppearExpectation: viewDidAppearExpectation, + viewWillDisappearExpectation: viewWillDisappearExpectation, + viewDidDisappearExpectation: viewDidDisappearExpectation + ) + + try await assert(of: viewController, as: .image) + try await assert(of: viewController, as: .image) + + await fulfillment( + of: [ + viewDidLoadExpectation, + viewWillAppearExpectation, + viewDidAppearExpectation, + viewWillDisappearExpectation, + viewDidDisappearExpectation, + ], + timeout: 1, + enforceOrder: true + ) + } + + @MainActor + func testViewControllerHierarchy() async throws { + let page = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) + page.setViewControllers([UIViewController()], direction: .forward, animated: false) + let tab = UITabBarController() + tab.viewControllers = [ + UINavigationController(rootViewController: page), + UINavigationController(rootViewController: UIViewController()), + UINavigationController(rootViewController: UIViewController()), + UINavigationController(rootViewController: UIViewController()), + UINavigationController(rootViewController: UIViewController()), + ] + try await assert(of: tab, as: .hierarchy) + } + + @MainActor + func testViewWithZeroHeightOrWidth() async throws { + var rect = CGRect(x: 0, y: 0, width: 350, height: 0) + let redView = UIView(frame: rect) + redView.backgroundColor = .red + try await assert(of: redView, as: .image, named: "noHeight") + + rect = CGRect(x: 0, y: 0, width: 0, height: 350) + let greenView = UIView(frame: rect) + greenView.backgroundColor = .green + try await assert(of: greenView, as: .image, named: "noWidth") + + rect = CGRect(x: 0, y: 0, width: 0, height: 0) + let blueView = UIView(frame: rect) + blueView.backgroundColor = .blue + try await assert(of: blueView, as: .image, named: "noWidth.noHeight") + } + + @MainActor + func testViewAgainstEmptyImage() async throws { + let rect = CGRect(x: 0, y: 0, width: 0, height: 0) + let view = UIView(frame: rect) + view.backgroundColor = .blue + + let failure = try await verify( + of: view, + as: .image, + named: "notEmptyImage" + ) + + XCTAssertNotNil(failure) + } + + #if !os(tvOS) + @MainActor + func testEmbeddedWebView() async throws { + let label = UILabel() + label.text = "Hello, Blob!" + + let fixtureUrl = URL(fileURLWithPath: #filePath, isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/pointfree.html") + let html = try String(contentsOf: fixtureUrl) + let webView = WKWebView() + webView.loadHTMLString(html, baseURL: nil) + webView.isHidden = true + + let stackView = UIStackView(arrangedSubviews: [label, webView]) + stackView.axis = .vertical + + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + try await assert( + of: stackView, + as: .image(layout: .fixed(width: 800, height: 600)) + ) + } + } + #endif + + func testSwiftUIView_tvOS() async throws { + struct MyView: SwiftUI.View { + var body: some SwiftUI.View { + HStack { + Image(systemName: "checkmark.circle.fill") + Text("Checked").fixedSize() + } + .padding(5) + .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue)) + .padding(10) + .background(Color.yellow) + } + } + + let view = MyView() + + try await assert(of: view, as: .image()) + try await assert(of: view, as: .image(layout: .sizeThatFits), named: "size-that-fits") + try await assert( + of: view, + as: .image(layout: .fixed(width: 300.0, height: 100.0)), + named: "fixed" + ) + try await assert(of: view, as: .image(layout: .device(.tv)), named: "device") + } + #endif + + #if os(iOS) || os(macOS) || os(visionOS) + @MainActor + func testWebView() async throws { + let fixtureUrl = URL(fileURLWithPath: #filePath, isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/pointfree.html") + let html = try String(contentsOf: fixtureUrl) + let webView = WKWebView() + webView.loadHTMLString(html, baseURL: nil) + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + try await assert( + of: webView, + as: .image( + precision: 0.95, + layout: .fixed(width: 800, height: 600) + ) + ) + } + } + final class ManipulatingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate { + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + webView.evaluateJavaScript("document.body.children[0].classList.remove(\"hero\")") // Change layout + } + } + + @MainActor + func testWebViewWithManipulatingNavigationDelegate() async throws { + let manipulatingWKWebViewNavigationDelegate = ManipulatingWKWebViewNavigationDelegate() + let webView = WKWebView() + webView.navigationDelegate = manipulatingWKWebViewNavigationDelegate + + let fixtureUrl = URL(fileURLWithPath: #filePath, isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/pointfree.html") + let html = try String(contentsOf: fixtureUrl) + webView.loadHTMLString(html, baseURL: nil) + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + try await assert( + of: webView, + as: .image(layout: .fixed(width: 800, height: 600)) + ) + } + _ = manipulatingWKWebViewNavigationDelegate + } + + final class CancellingWKWebViewNavigationDelegate: NSObject, WKNavigationDelegate { + func webView( + _ webView: WKWebView, + decidePolicyFor navigationAction: WKNavigationAction, + decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void + ) { + decisionHandler(.cancel) + } + } + + @MainActor + func testWebViewWithCancellingNavigationDelegate() async throws { + let cancellingWKWebViewNavigationDelegate = CancellingWKWebViewNavigationDelegate() + let webView = WKWebView() + webView.navigationDelegate = cancellingWKWebViewNavigationDelegate + + let fixtureUrl = URL(fileURLWithPath: #filePath, isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/pointfree.html") + let html = try String(contentsOf: fixtureUrl) + webView.loadHTMLString(html, baseURL: nil) + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + try await assert( + of: webView, + as: .image(precision: 0.95, layout: .fixed(width: 800, height: 600)) + ) + } + _ = cancellingWKWebViewNavigationDelegate + } + #endif + + #if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) + func testCGPath() throws { + let path = CGPath.heart + + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + try assert(of: path, as: .image) + } + + if #available(iOS 11.0, OSX 10.13, tvOS 11.0, *) { + try assert(of: path, as: .elementsDescription) + } + } + + @MainActor + func testPrecision() async throws { + #if os(iOS) || os(tvOS) || os(visionOS) + let label = UILabel() + #if os(iOS) + label.frame = CGRect(origin: .zero, size: CGSize(width: 43.5, height: 20.5)) + #elseif os(tvOS) || os(visionOS) + label.frame = CGRect(origin: .zero, size: CGSize(width: 98, height: 46)) + #endif + label.backgroundColor = .white + #elseif os(macOS) + let label = NSTextField() + label.frame = CGRect(origin: .zero, size: CGSize(width: 37, height: 16)) + label.backgroundColor = .white + label.isBezeled = false + label.isEditable = false + #endif + + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + label.text = "Hello." + try await assert(of: label, as: .image(precision: 0.9)) + label.text = "Hello" + try await assert(of: label, as: .image(precision: 0.9)) + } + } + + func testImagePrecision() throws { + let imageURL = URL(fileURLWithPath: #filePath, isDirectory: false) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/testImagePrecision.reference.png") + #if os(iOS) || os(tvOS) || os(visionOS) + let image = try XCTUnwrap(UIImage(contentsOfFile: imageURL.path)) + #elseif os(macOS) + let image = try XCTUnwrap(NSImage(byReferencing: imageURL)) + #endif + + try assert(of: image, as: .image(precision: 0.995), named: "exact") + if #available(iOS 11.0, tvOS 11.0, macOS 10.13, *) { + try assert(of: image, as: .image(perceptualPrecision: 0.98), named: "perceptual") + } + } + @MainActor + func testSCNView() async throws { + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + @MainActor + class Scene: SCNScene, Sendable {} + let scene = Scene() + + let sphereGeometry = SCNSphere(radius: 3) + sphereGeometry.segmentCount = 200 + let sphereNode = SCNNode(geometry: sphereGeometry) + sphereNode.position = SCNVector3Zero + scene.rootNode.addChildNode(sphereNode) + + sphereGeometry.firstMaterial?.diffuse.contents = URL( + fileURLWithPath: #filePath, + isDirectory: false + ) + .deletingLastPathComponent() + .appendingPathComponent("__Fixtures__/earth.png") + + let cameraNode = SCNNode() + cameraNode.camera = SCNCamera() + cameraNode.position = SCNVector3Make(0, 0, 8) + scene.rootNode.addChildNode(cameraNode) + + let omniLight = SCNLight() + omniLight.type = .omni + let omniLightNode = SCNNode() + omniLightNode.light = omniLight + omniLightNode.position = SCNVector3Make(10, 10, 10) + scene.rootNode.addChildNode(omniLightNode) + + try await assert( + of: scene, + as: .image(size: .init(width: 500, height: 500)) + ) + } + } + func testSKView() async throws { + @MainActor + class Scene: SKScene, Sendable {} + // NB: CircleCI crashes while trying to instantiate SKView. + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + let scene = Scene(size: .init(width: 50, height: 50)) + let node = SKShapeNode(circleOfRadius: 15) + node.fillColor = .red + node.position = .init(x: 25, y: 25) + scene.addChild(node) + + try await assert( + of: scene, + as: .image(size: .init(width: 50, height: 50)) + ) + } + } + #endif + + #if os(macOS) + func testNSBezierPath() async throws { + let path = NSBezierPath.heart + + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + try assert(of: path, as: .image, named: "macOS") + } + + try assert(of: path, as: .elementsDescription, named: "macOS") + } + + @MainActor + func testNSView() async throws { + let button = NSButton() + button.bezelStyle = .rounded + button.title = "Push Me" + button.sizeToFit() + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + try await assert(of: button, as: .image) + print("Finished") + try await assert(of: button, as: .recursiveDescription) + } + print("Finished") + } + + @MainActor + func testNSViewWithLayer() async throws { + let view = NSView() + view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + view.heightAnchor.constraint(equalToConstant: 10), + view.widthAnchor.constraint(equalToConstant: 10), + ]) + view.wantsLayer = true + view.layer?.backgroundColor = NSColor.green.cgColor + view.layer?.cornerRadius = 5 + if !ProcessInfo.processInfo.environment.keys.contains("GITHUB_WORKFLOW") { + try await assert(of: view, as: .image) + try await assert(of: view, as: .recursiveDescription) + } + } + #endif + + #if os(iOS) || os(tvOS) || os(macOS) || os(visionOS) || os(watchOS) + @available(watchOS 9.0, *) + @MainActor + func testSwiftUIView_iOS() async throws { + @MainActor + struct MyView: SwiftUI.View { + var body: some SwiftUI.View { + HStack { + Image(systemName: "checkmark.circle.fill") + Text("Checked").fixedSize() + } + .padding(5) + .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue)) + .padding(10) + .background(Color.yellow) + } + } + + let view = MyView() + + try await withTestingEnvironment { + #if os(iOS) || os(tvOS) || os(visionOS) + $0.traits = .init(userInterfaceStyle: .light) + #endif + } operation: { + try await assert( + of: view, + as: .image + ) + try await assert( + of: view, + as: .image(layout: .sizeThatFits), + named: "size-that-fits" + ) + try await assert( + of: view, + as: .image(layout: .fixed(width: 200.0, height: 100.0)), + named: "fixed" + ) + #if !os(macOS) && !os(watchOS) + try await assert( + of: view, + as: .image(layout: .device(.iPhoneSE)), + named: "device" + ) + #endif + } + } + #endif +} + +#if os(iOS) || os(tvOS) || os(visionOS) +private let allContentSizes = + [ + "extra-small": UIContentSizeCategory.extraSmall, + "small": .small, + "medium": .medium, + "large": .large, + "extra-large": .extraLarge, + "extra-extra-large": .extraExtraLarge, + "extra-extra-extra-large": .extraExtraExtraLarge, + "accessibility-medium": .accessibilityMedium, + "accessibility-large": .accessibilityLarge, + "accessibility-extra-large": .accessibilityExtraLarge, + "accessibility-extra-extra-large": .accessibilityExtraExtraLarge, + "accessibility-extra-extra-extra-large": .accessibilityExtraExtraExtraLarge, + ] +#endif +#endif diff --git a/Tests/XCSnapshotTestingTests/WithSnapshotTestingTests.swift b/Tests/XCSnapshotTestingTests/WithSnapshotTestingTests.swift new file mode 100644 index 000000000..05769bfcd --- /dev/null +++ b/Tests/XCSnapshotTestingTests/WithSnapshotTestingTests.swift @@ -0,0 +1,42 @@ +@_spi(Internals) @testable import XCSnapshotTesting + +#if canImport(XCTest) +import XCTest + +class WithSnapshotTestingTests: XCTestCase { + func testNesting() { + withTestingEnvironment(record: .all) { + XCTAssertEqual( + SnapshotEnvironment.current.diffTool( + currentFilePath: "file://old.png", + failedFilePath: "file://new.png" + ), + """ + @− + "file://old.png" + @+ + "file://new.png" + + To configure output for a custom diff tool, use 'withTestingEnvironment'. For example: + + withTestingEnvironment(diffTool: .ksdiff) { + // ... + } + """ + ) + XCTAssertEqual(SnapshotEnvironment.current.recordMode, .all) + + withTestingEnvironment(diffTool: "ksdiff") { + XCTAssertEqual( + SnapshotEnvironment.current.diffTool( + currentFilePath: "old.png", + failedFilePath: "new.png" + ), + "ksdiff old.png new.png" + ) + XCTAssertEqual(SnapshotEnvironment.current.recordMode, .all) + } + } + } +} +#endif diff --git a/Tests/XCSnapshotTestingTests/__Fixtures__/earth.png b/Tests/XCSnapshotTestingTests/__Fixtures__/earth.png new file mode 100644 index 000000000..1071a14dd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Fixtures__/earth.png differ diff --git a/Tests/XCSnapshotTestingTests/__Fixtures__/pointfree.html b/Tests/XCSnapshotTestingTests/__Fixtures__/pointfree.html new file mode 100644 index 000000000..5fcc5b039 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Fixtures__/pointfree.html @@ -0,0 +1 @@ +Point-Free — A weekly video series on Swift and functional programming.

A new weekly Swift video series exploring functional programming and more.

Coming really, really soon.

diff --git a/Tests/XCSnapshotTestingTests/__Fixtures__/testImagePrecision.reference.png b/Tests/XCSnapshotTestingTests/__Fixtures__/testImagePrecision.reference.png new file mode 100644 index 000000000..7f3ac46bf Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Fixtures__/testImagePrecision.reference.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testAnyAsJson.1.json b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testAnyAsJson.1.json new file mode 100644 index 000000000..629c2132f --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testAnyAsJson.1.json @@ -0,0 +1,5 @@ +{ + "bio" : "Blobbed around the world.", + "id" : 1, + "name" : "Blobby" +} \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testCaseIterable.1.csv b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testCaseIterable.1.csv new file mode 100644 index 000000000..20f36f7c1 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testCaseIterable.1.csv @@ -0,0 +1,4 @@ +"up","StringBytes(rawValue: "left")" +"down","StringBytes(rawValue: "right")" +"left","StringBytes(rawValue: "down")" +"right","StringBytes(rawValue: "up")" \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testData.1 b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testData.1 new file mode 100644 index 000000000..7d174b13e --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testData.1 @@ -0,0 +1 @@ +ޭ \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testEncodable.1.json b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testEncodable.1.json new file mode 100644 index 000000000..629c2132f --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testEncodable.1.json @@ -0,0 +1,5 @@ +{ + "bio" : "Blobbed around the world.", + "id" : 1, + "name" : "Blobby" +} \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testEncodable.2.plist b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testEncodable.2.plist new file mode 100644 index 000000000..916a73cca --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testEncodable.2.plist @@ -0,0 +1,12 @@ + + + + + bio + Blobbed around the world. + id + 1 + name + Blobby + + diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-curl.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-curl.txt new file mode 100644 index 000000000..f83d4caa2 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-curl.txt @@ -0,0 +1,5 @@ +curl \ + --header "Accept: text/html" \ + --header "Content-Type: application/json" \ + --cookie "pf_session={}" \ + "https://www.pointfree.co/" \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-with-query-curl.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-with-query-curl.txt new file mode 100644 index 000000000..bf9b3c0dd --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-with-query-curl.txt @@ -0,0 +1,5 @@ +curl \ + --header "Accept: text/html" \ + --header "Content-Type: application/json" \ + --cookie "pf_session={}" \ + "https://www.pointfree.co?key_1=value_1&key_2=value_2&key_3=value_3" \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-with-query.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-with-query.txt new file mode 100644 index 000000000..42337e6f9 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get-with-query.txt @@ -0,0 +1,4 @@ +GET https://www.pointfree.co?key_1=value_1&key_2=value_2&key_3=value_3 +Accept: text/html +Content-Type: application/json +Cookie: pf_session={} \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get.txt new file mode 100644 index 000000000..06243d825 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.get.txt @@ -0,0 +1,4 @@ +GET https://www.pointfree.co/ +Accept: text/html +Content-Type: application/json +Cookie: pf_session={} \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.head-curl.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.head-curl.txt new file mode 100644 index 000000000..79640f154 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.head-curl.txt @@ -0,0 +1,4 @@ +curl \ + --head \ + --cookie "pf_session={}" \ + "https://www.pointfree.co/" \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.head.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.head.txt new file mode 100644 index 000000000..a8c48c495 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.head.txt @@ -0,0 +1,2 @@ +HEAD https://www.pointfree.co/ +Cookie: pf_session={} \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-curl.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-curl.txt new file mode 100644 index 000000000..3cfaa933d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-curl.txt @@ -0,0 +1,6 @@ +curl \ + --request POST \ + --header "Accept: text/html" \ + --data "pricing[billing]=monthly&pricing[lane]=individual" \ + --cookie "pf_session={\"user_id\":\"0\"}" \ + "https://www.pointfree.co/subscribe" \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-with-json-curl.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-with-json-curl.txt new file mode 100644 index 000000000..17757678d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-with-json-curl.txt @@ -0,0 +1,6 @@ +curl \ + --request POST \ + --header "Accept: application/json" \ + --header "Content-Type: application/json" \ + --data "{\"name\":\"tammy134235345235\", \"salary\":0, \"age\":\"tammy133\"}" \ + "http://dummy.restapiexample.com/api/v1/create" \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-with-json.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-with-json.txt new file mode 100644 index 000000000..146593528 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post-with-json.txt @@ -0,0 +1,5 @@ +POST http://dummy.restapiexample.com/api/v1/create +Accept: application/json +Content-Type: application/json + +{"name":"tammy134235345235", "salary":0, "age":"tammy133"} \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post.txt new file mode 100644 index 000000000..7db5046f8 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/FoundationTests/testURLRequest.post.txt @@ -0,0 +1,5 @@ +POST https://www.pointfree.co/subscribe +Accept: text/html +Cookie: pf_session={"user_id":"0"} + +pricing[billing]=monthly&pricing[lane]=individual \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.1@1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.1@1.png new file mode 100644 index 000000000..c27c46685 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.1@1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.1@2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.1@2.png new file mode 100644 index 000000000..a87bf46c0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.1@2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.iPad-image.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.iPad-image.png new file mode 100644 index 000000000..b075a973c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.iPad-image.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png new file mode 100644 index 000000000..87006baf4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png differ diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAutolayout.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAutolayout.1.png similarity index 77% rename from Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAutolayout.1.png rename to Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAutolayout.1.png index fc0afa382..1f6839b9c 100644 Binary files a/Tests/SnapshotTestingTests/__Snapshots__/SnapshotTestingTests/testAutolayout.1.png and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testAutolayout.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCGPath.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCGPath.1.png new file mode 100644 index 000000000..97706342d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCGPath.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCGPath.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCGPath.2.txt new file mode 100644 index 000000000..c169fc6bf --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCGPath.2.txt @@ -0,0 +1,15 @@ +MoveTo (0.0, 0.0) +LineTo (0.0, 60.0) +QuadCurveTo (3.75, 86.25) (30.0, 90.0) +QuadCurveTo (56.25, 86.25) (60.0, 60.0) +CurveTo (75.0, 60.0) (90.0, 45.0) (90.0, 30.0) +CurveTo (90.0, 15.0) (75.0, 0.0) (60.0, 0.0) +LineTo (0.0, 0.0) +Close + +MoveTo (90.0, 75.0) +CurveTo (90.0, 83.284) (83.284, 90.0) (75.0, 90.0) +CurveTo (66.716, 90.0) (60.0, 83.284) (60.0, 75.0) +CurveTo (60.0, 66.716) (66.716, 60.0) (75.0, 60.0) +CurveTo (83.284, 60.0) (90.0, 66.716) (90.0, 75.0) +Close diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png new file mode 100644 index 000000000..da126e454 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png new file mode 100644 index 000000000..6b9f458ea Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png new file mode 100644 index 000000000..9566ffe83 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png new file mode 100644 index 000000000..6b9f458ea Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testEmbeddedWebView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testEmbeddedWebView.1.png new file mode 100644 index 000000000..270a47c09 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testEmbeddedWebView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testImagePrecision.exact.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testImagePrecision.exact.png new file mode 100644 index 000000000..dc1d02086 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testImagePrecision.exact.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testImagePrecision.perceptual.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testImagePrecision.perceptual.png new file mode 100644 index 000000000..dc1d02086 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testImagePrecision.perceptual.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testPrecision.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testPrecision.1.png new file mode 100644 index 000000000..82b0d959c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testPrecision.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testPrecision.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testPrecision.2.png new file mode 100644 index 000000000..eaa202948 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testPrecision.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSCNView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSCNView.1.png new file mode 100644 index 000000000..0c6d1954d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSCNView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSKView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSKView.1.png new file mode 100644 index 000000000..e76873569 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSKView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.1.png new file mode 100644 index 000000000..d3bb0b0b5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.device.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.device.png new file mode 100644 index 000000000..f06f090aa Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.device.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.fixed.png new file mode 100644 index 000000000..b991f4946 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.size-that-fits.png new file mode 100644 index 000000000..d3bb0b0b5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_iOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.1.png new file mode 100644 index 000000000..d3bb0b0b5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.device.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.device.png new file mode 100644 index 000000000..69bd4e591 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.device.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.fixed.png new file mode 100644 index 000000000..ac5005d0a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.size-that-fits.png new file mode 100644 index 000000000..d3bb0b0b5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testSwiftUIView_tvOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTableViewController.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTableViewController.1.png new file mode 100644 index 000000000..87006baf4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTableViewController.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-33-split-portrait.png new file mode 100644 index 000000000..af29d5fc1 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-50-split-landscape.png new file mode 100644 index 000000000..9be85a51a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-66-split-landscape.png new file mode 100644 index 000000000..05c3b4764 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-66-split-portrait.png new file mode 100644 index 000000000..ef8c3b29f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-alternative.png new file mode 100644 index 000000000..b5d9477a2 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-split-landscape.png new file mode 100644 index 000000000..ab20c3563 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2.png new file mode 100644 index 000000000..3d0c10c9a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2.txt new file mode 100644 index 000000000..5a7018b2d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-10-2.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1080 766); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-33-split-landscape.png new file mode 100644 index 000000000..1409c32cb Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-33-split-portrait.png new file mode 100644 index 000000000..e4a7e4359 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-50-split-landscape.png new file mode 100644 index 000000000..b3d58dc07 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-66-split-landscape.png new file mode 100644 index 000000000..1caef6781 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-66-split-portrait.png new file mode 100644 index 000000000..6fb16f347 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-alternative.png new file mode 100644 index 000000000..aba565381 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7.png new file mode 100644 index 000000000..b42c1f522 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7.txt new file mode 100644 index 000000000..4490337ef --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-9-7.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1024 724); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-33-split-landscape.png new file mode 100644 index 000000000..77c2b1dcf Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-33-split-portrait.png new file mode 100644 index 000000000..70dd57f86 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-50-split-landscape.png new file mode 100644 index 000000000..09db72221 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-66-split-landscape.png new file mode 100644 index 000000000..a7bd17013 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-66-split-portrait.png new file mode 100644 index 000000000..16957934d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-alternative.png new file mode 100644 index 000000000..173815bb9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini.png new file mode 100644 index 000000000..f065ad95f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini.txt new file mode 100644 index 000000000..0687355ce --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-mini.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1133 700); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..751b15c46 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5.png new file mode 100644 index 000000000..81ec4f3a5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5.txt new file mode 100644 index 000000000..34b0c71d4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10-5.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1194 790); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png new file mode 100644 index 000000000..207c9e9a9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png new file mode 100644 index 000000000..fb6a8f3e9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png new file mode 100644 index 000000000..4e1c91d81 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png new file mode 100644 index 000000000..f1e6968a7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png new file mode 100644 index 000000000..df3433826 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11-alternative.png new file mode 100644 index 000000000..751b15c46 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11.png new file mode 100644 index 000000000..81ec4f3a5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11.txt new file mode 100644 index 000000000..34b0c71d4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1194 790); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png new file mode 100644 index 000000000..207c9e9a9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png new file mode 100644 index 000000000..fb6a8f3e9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png new file mode 100644 index 000000000..4e1c91d81 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png new file mode 100644 index 000000000..f1e6968a7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png new file mode 100644 index 000000000..df3433826 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..69176b756 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9.png new file mode 100644 index 000000000..4ea3a8292 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9.txt new file mode 100644 index 000000000..40a9a3ee6 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12-9.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1366 980); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png new file mode 100644 index 000000000..9d4d883ba Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png new file mode 100644 index 000000000..4dc5a2d51 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png new file mode 100644 index 000000000..6e56ca6a4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png new file mode 100644 index 000000000..fe87d48d7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png new file mode 100644 index 000000000..8e7169f9a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-alternative.png new file mode 100644 index 000000000..ec264c3b7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus-alternative.png new file mode 100644 index 000000000..64f1f6a45 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus.png new file mode 100644 index 000000000..e6286045f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus.txt new file mode 100644 index 000000000..81c19ee48 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8-plus.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 414.333 716); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8.png new file mode 100644 index 000000000..3c3659308 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8.txt new file mode 100644 index 000000000..44cc41a56 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-8.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..cdc68d78a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png new file mode 100644 index 000000000..f884ffab8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-large.png new file mode 100644 index 000000000..a3c849180 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-large.png new file mode 100644 index 000000000..6ebdf148e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-medium.png new file mode 100644 index 000000000..bfbe6af88 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-accessibility-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-alternative.png new file mode 100644 index 000000000..ec264c3b7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png new file mode 100644 index 000000000..e6371ed58 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-extra-large.png new file mode 100644 index 000000000..48b6711c5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-large.png new file mode 100644 index 000000000..43dfbb28b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-small.png new file mode 100644 index 000000000..ed6108cef Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-extra-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-large.png new file mode 100644 index 000000000..3c3659308 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-medium.png new file mode 100644 index 000000000..3ca220678 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-small.png new file mode 100644 index 000000000..36828476f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se.png new file mode 100644 index 000000000..3c3659308 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se.txt new file mode 100644 index 000000000..44cc41a56 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-se.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x-alternative.png new file mode 100644 index 000000000..a15d18527 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x.png new file mode 100644 index 000000000..8bce45940 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x.txt new file mode 100644 index 000000000..6365bdc9a --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-x.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 83; 375 681); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr-alternative.png new file mode 100644 index 000000000..ef286d78d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr.png new file mode 100644 index 000000000..86df00a61 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr.txt new file mode 100644 index 000000000..40c5f801d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xr.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 66.3333; 414.333 795.667); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max-alternative.png new file mode 100644 index 000000000..ef286d78d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max.png new file mode 100644 index 000000000..86df00a61 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max.txt new file mode 100644 index 000000000..40c5f801d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.iphone-xs-max.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 66.3333; 414.333 795.667); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.tv.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.tv.png new file mode 100644 index 000000000..b3dc7a577 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.tv.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.tv4k.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.tv4k.png new file mode 100644 index 000000000..ae6dea3fe Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraits.tv4k.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png new file mode 100644 index 000000000..6352d8805 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png new file mode 100644 index 000000000..99dd4d539 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png new file mode 100644 index 000000000..c69a4dc83 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png new file mode 100644 index 000000000..59498e975 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png new file mode 100644 index 000000000..7d002f1fc Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png new file mode 100644 index 000000000..68f277122 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..0222a66b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png new file mode 100644 index 000000000..8050776d8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png new file mode 100644 index 000000000..0222a66b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png new file mode 100644 index 000000000..8050776d8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..fdee6850d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png new file mode 100644 index 000000000..70056c8f9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png new file mode 100644 index 000000000..3ac5c2b58 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png new file mode 100644 index 000000000..0c14d513a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png new file mode 100644 index 000000000..95fff7d1e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png new file mode 100644 index 000000000..dc65b3122 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png new file mode 100644 index 000000000..3ac5c2b58 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png new file mode 100644 index 000000000..dc65b3122 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png new file mode 100644 index 000000000..cacbbd4ab Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png new file mode 100644 index 000000000..7a1351495 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png new file mode 100644 index 000000000..0d7dc5678 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png new file mode 100644 index 000000000..87f445805 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png new file mode 100644 index 000000000..0d7dc5678 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png new file mode 100644 index 000000000..87f445805 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..d698d02a2 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png new file mode 100644 index 000000000..73f10dd48 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-large.png new file mode 100644 index 000000000..9cbb1aea9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-large.png new file mode 100644 index 000000000..43c8bcef6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-medium.png new file mode 100644 index 000000000..f37272b48 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-accessibility-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png new file mode 100644 index 000000000..2a682d347 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-extra-large.png new file mode 100644 index 000000000..03af704e8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-large.png new file mode 100644 index 000000000..d69693b50 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-small.png new file mode 100644 index 000000000..f780c1758 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-extra-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-large.png new file mode 100644 index 000000000..127293b32 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-medium.png new file mode 100644 index 000000000..2d9037106 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-small.png new file mode 100644 index 000000000..699b83b20 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithView.label-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt new file mode 100644 index 000000000..ab262a8b6 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt new file mode 100644 index 000000000..7b021a6b6 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt new file mode 100644 index 000000000..e9b434c9e --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-large.txt new file mode 100644 index 000000000..b37ef8089 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt new file mode 100644 index 000000000..fa489a77b --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt new file mode 100644 index 000000000..ac50168ad --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt new file mode 100644 index 000000000..7887024c2 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-large.txt new file mode 100644 index 000000000..9632cbfbf --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-small.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-small.txt new file mode 100644 index 000000000..067d25c91 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-extra-small.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-large.txt new file mode 100644 index 000000000..25bb6dc80 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-medium.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-medium.txt new file mode 100644 index 000000000..4702d081a --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-medium.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-small.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-small.txt new file mode 100644 index 000000000..2e0da2c25 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testTraitsWithViewController.label-small.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIView.1.png new file mode 100644 index 000000000..0c005fb76 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIView.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIView.2.txt new file mode 100644 index 000000000..805cdd999 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIView.2.txt @@ -0,0 +1,3 @@ +; layer = > + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 0; 25.6667 25.6667); layer = > + | ; layer = > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIViewControllerLifeCycle.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIViewControllerLifeCycle.1.png new file mode 100644 index 000000000..1f6839b9c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIViewControllerLifeCycle.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIViewControllerLifeCycle.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIViewControllerLifeCycle.2.png new file mode 100644 index 000000000..1f6839b9c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testUIViewControllerLifeCycle.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewControllerHierarchy.1.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewControllerHierarchy.1.txt new file mode 100644 index 000000000..7619b9813 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewControllerHierarchy.1.txt @@ -0,0 +1,12 @@ +, state: appeared, view: + | , state: appeared, view: + | | , state: appeared, view: <_UIPageViewControllerContentView> + | | | , state: appeared, view: + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png new file mode 100644 index 000000000..c7a1cedfa Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png new file mode 100644 index 000000000..f4a519e39 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png new file mode 100644 index 000000000..6ca15da4d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebView.1.png new file mode 100644 index 000000000..3850fa19b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png new file mode 100644 index 000000000..1a2d805b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png new file mode 100644 index 000000000..ddc2390b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.1@1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.1@1.png new file mode 100644 index 000000000..07a44a529 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.1@1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.1@2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.1@2.png new file mode 100644 index 000000000..2a3bfc025 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.1@2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.iPad-image.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.iPad-image.png new file mode 100644 index 000000000..bcb9d1208 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.iPad-image.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png new file mode 100644 index 000000000..69f15565a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAutolayout.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAutolayout.1.png new file mode 100644 index 000000000..4f5902207 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testAutolayout.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCGPath.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCGPath.1.png new file mode 100644 index 000000000..97706342d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCGPath.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCGPath.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCGPath.2.txt new file mode 100644 index 000000000..c169fc6bf --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCGPath.2.txt @@ -0,0 +1,15 @@ +MoveTo (0.0, 0.0) +LineTo (0.0, 60.0) +QuadCurveTo (3.75, 86.25) (30.0, 90.0) +QuadCurveTo (56.25, 86.25) (60.0, 60.0) +CurveTo (75.0, 60.0) (90.0, 45.0) (90.0, 30.0) +CurveTo (90.0, 15.0) (75.0, 0.0) (60.0, 0.0) +LineTo (0.0, 0.0) +Close + +MoveTo (90.0, 75.0) +CurveTo (90.0, 83.284) (83.284, 90.0) (75.0, 90.0) +CurveTo (66.716, 90.0) (60.0, 83.284) (60.0, 75.0) +CurveTo (60.0, 66.716) (66.716, 60.0) (75.0, 60.0) +CurveTo (83.284, 60.0) (90.0, 66.716) (90.0, 75.0) +Close diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png new file mode 100644 index 000000000..f69164402 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png new file mode 100644 index 000000000..d7c6cf401 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png new file mode 100644 index 000000000..2d83de434 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png new file mode 100644 index 000000000..d7c6cf401 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testEmbeddedWebView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testEmbeddedWebView.1.png new file mode 100644 index 000000000..e8dfa6db3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testEmbeddedWebView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testImagePrecision.exact.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testImagePrecision.exact.png new file mode 100644 index 000000000..dc1d02086 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testImagePrecision.exact.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testImagePrecision.perceptual.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testImagePrecision.perceptual.png new file mode 100644 index 000000000..dc1d02086 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testImagePrecision.perceptual.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testPrecision.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testPrecision.1.png new file mode 100644 index 000000000..4d2425e80 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testPrecision.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testPrecision.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testPrecision.2.png new file mode 100644 index 000000000..6a81645d6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testPrecision.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSCNView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSCNView.1.png new file mode 100644 index 000000000..938df70ae Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSCNView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSKView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSKView.1.png new file mode 100644 index 000000000..2033a2b4f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSKView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.1.png new file mode 100644 index 000000000..f74ace355 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.device.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.device.png new file mode 100644 index 000000000..a55f18993 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.device.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.fixed.png new file mode 100644 index 000000000..2db97dfee Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.size-that-fits.png new file mode 100644 index 000000000..f74ace355 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_iOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.1.png new file mode 100644 index 000000000..f74ace355 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.device.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.device.png new file mode 100644 index 000000000..3d3281690 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.device.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.fixed.png new file mode 100644 index 000000000..cdec5a530 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.size-that-fits.png new file mode 100644 index 000000000..f74ace355 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testSwiftUIView_tvOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTableViewController.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTableViewController.1.png new file mode 100644 index 000000000..69f15565a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTableViewController.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-33-split-portrait.png new file mode 100644 index 000000000..65a1fda7c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-50-split-landscape.png new file mode 100644 index 000000000..5ed377c27 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-66-split-landscape.png new file mode 100644 index 000000000..300a7fe16 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-66-split-portrait.png new file mode 100644 index 000000000..e511eb6d7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-alternative.png new file mode 100644 index 000000000..8e1051a32 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-split-landscape.png new file mode 100644 index 000000000..27a1cb93c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2.png new file mode 100644 index 000000000..2a517b12c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2.txt new file mode 100644 index 000000000..5a7018b2d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-10-2.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1080 766); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-33-split-landscape.png new file mode 100644 index 000000000..306501f90 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-33-split-portrait.png new file mode 100644 index 000000000..dd3c9f3eb Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-50-split-landscape.png new file mode 100644 index 000000000..c04a29808 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-66-split-landscape.png new file mode 100644 index 000000000..ffc7c8681 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-66-split-portrait.png new file mode 100644 index 000000000..dd11aaa70 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-alternative.png new file mode 100644 index 000000000..10c1533e7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7.png new file mode 100644 index 000000000..284013d18 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7.txt new file mode 100644 index 000000000..4490337ef --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-9-7.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1024 724); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-33-split-landscape.png new file mode 100644 index 000000000..3dcb9c4cc Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-33-split-portrait.png new file mode 100644 index 000000000..af261804e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-50-split-landscape.png new file mode 100644 index 000000000..02defd2fd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-66-split-landscape.png new file mode 100644 index 000000000..be52a78db Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-66-split-portrait.png new file mode 100644 index 000000000..7f4633913 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-alternative.png new file mode 100644 index 000000000..392f63f96 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini.png new file mode 100644 index 000000000..099ed0ada Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini.txt new file mode 100644 index 000000000..0687355ce --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-mini.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1133 700); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..87708c811 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5.png new file mode 100644 index 000000000..c688b936a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5.txt new file mode 100644 index 000000000..34b0c71d4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10-5.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1194 790); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png new file mode 100644 index 000000000..7f53d09ce Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png new file mode 100644 index 000000000..daf212c8a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png new file mode 100644 index 000000000..e88e581ac Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png new file mode 100644 index 000000000..57c9a122c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png new file mode 100644 index 000000000..3888a77d2 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11-alternative.png new file mode 100644 index 000000000..87708c811 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11.png new file mode 100644 index 000000000..c688b936a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11.txt new file mode 100644 index 000000000..34b0c71d4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1194 790); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png new file mode 100644 index 000000000..7f53d09ce Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png new file mode 100644 index 000000000..daf212c8a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png new file mode 100644 index 000000000..e88e581ac Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png new file mode 100644 index 000000000..57c9a122c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png new file mode 100644 index 000000000..3888a77d2 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..d91d5880e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9.png new file mode 100644 index 000000000..c647933b6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9.txt new file mode 100644 index 000000000..40a9a3ee6 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12-9.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1366 980); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png new file mode 100644 index 000000000..4d0b36f1e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png new file mode 100644 index 000000000..a43df37a4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png new file mode 100644 index 000000000..8a3a3dc56 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png new file mode 100644 index 000000000..3ea6cbccf Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png new file mode 100644 index 000000000..eb0ea18f8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-alternative.png new file mode 100644 index 000000000..69baf1805 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus-alternative.png new file mode 100644 index 000000000..a2f9cea6a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus.png new file mode 100644 index 000000000..a453b61dd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus.txt new file mode 100644 index 000000000..c48cbf0e3 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8-plus.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 414 716); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8.png new file mode 100644 index 000000000..ff7c5af3c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8.txt new file mode 100644 index 000000000..44cc41a56 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-8.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..24306d183 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png new file mode 100644 index 000000000..c818398ae Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-large.png new file mode 100644 index 000000000..29d725c32 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-large.png new file mode 100644 index 000000000..69f7c7246 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-medium.png new file mode 100644 index 000000000..bdf99f455 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-accessibility-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-alternative.png new file mode 100644 index 000000000..69baf1805 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png new file mode 100644 index 000000000..39efec5be Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-extra-large.png new file mode 100644 index 000000000..75e772a0a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-large.png new file mode 100644 index 000000000..b10b43dc4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-small.png new file mode 100644 index 000000000..85a1dc7c0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-extra-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-large.png new file mode 100644 index 000000000..ff7c5af3c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-medium.png new file mode 100644 index 000000000..962f876f6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-small.png new file mode 100644 index 000000000..905805288 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se.png new file mode 100644 index 000000000..ff7c5af3c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se.txt new file mode 100644 index 000000000..44cc41a56 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-se.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x-alternative.png new file mode 100644 index 000000000..3393914aa Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x.png new file mode 100644 index 000000000..a4529e473 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x.txt new file mode 100644 index 000000000..d606f199c --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-x.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 375 734); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr-alternative.png new file mode 100644 index 000000000..d7fe99de3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr.png new file mode 100644 index 000000000..45b098e77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr.txt new file mode 100644 index 000000000..7255128b8 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xr.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 414 818); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max-alternative.png new file mode 100644 index 000000000..d7fe99de3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max.png new file mode 100644 index 000000000..45b098e77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max.txt new file mode 100644 index 000000000..7255128b8 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.iphone-xs-max.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 414 818); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.tv.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.tv.png new file mode 100644 index 000000000..cda049034 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.tv.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.tv4k.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.tv4k.png new file mode 100644 index 000000000..5d4be53c1 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraits.tv4k.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png new file mode 100644 index 000000000..b600a6ad4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png new file mode 100644 index 000000000..c23dc70cd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png new file mode 100644 index 000000000..8b152337d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png new file mode 100644 index 000000000..f10168e6f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png new file mode 100644 index 000000000..d8216d790 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png new file mode 100644 index 000000000..37d91f3e6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..2a9efd6e4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png new file mode 100644 index 000000000..f3d907e0c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png new file mode 100644 index 000000000..2a9efd6e4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png new file mode 100644 index 000000000..f3d907e0c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..3225bd95c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png new file mode 100644 index 000000000..f4356e71d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png new file mode 100644 index 000000000..926b95c55 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png new file mode 100644 index 000000000..227ae49f6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png new file mode 100644 index 000000000..02c03fe90 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png new file mode 100644 index 000000000..be121d767 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png new file mode 100644 index 000000000..926b95c55 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png new file mode 100644 index 000000000..be121d767 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png new file mode 100644 index 000000000..c5efb377d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png new file mode 100644 index 000000000..1d978e8c0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png new file mode 100644 index 000000000..566e07eac Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png new file mode 100644 index 000000000..8ce9f7782 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png new file mode 100644 index 000000000..566e07eac Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png new file mode 100644 index 000000000..8ce9f7782 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..3c2a55bd9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png new file mode 100644 index 000000000..45840a48c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-large.png new file mode 100644 index 000000000..efcb72450 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-large.png new file mode 100644 index 000000000..c85c73451 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-medium.png new file mode 100644 index 000000000..74695bb62 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-accessibility-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png new file mode 100644 index 000000000..835df5a7b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-extra-large.png new file mode 100644 index 000000000..02b230416 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-large.png new file mode 100644 index 000000000..560833713 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-small.png new file mode 100644 index 000000000..0ef5b2bbb Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-extra-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-large.png new file mode 100644 index 000000000..4c16a89b0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-medium.png new file mode 100644 index 000000000..3ee9b7e1f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-small.png new file mode 100644 index 000000000..67658bd44 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithView.label-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt new file mode 100644 index 000000000..46eb4eb5e --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt new file mode 100644 index 000000000..2d669b66b --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt new file mode 100644 index 000000000..ed05e935f --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-large.txt new file mode 100644 index 000000000..de2c46fdf --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt new file mode 100644 index 000000000..d5fac325f --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt new file mode 100644 index 000000000..4739458f6 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt new file mode 100644 index 000000000..5059f1c52 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-large.txt new file mode 100644 index 000000000..875ac4d18 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-small.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-small.txt new file mode 100644 index 000000000..8f5851079 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-extra-small.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-large.txt new file mode 100644 index 000000000..2a361a5b4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-medium.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-medium.txt new file mode 100644 index 000000000..a46219f06 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-medium.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-small.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-small.txt new file mode 100644 index 000000000..c2669cfa8 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testTraitsWithViewController.label-small.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIView.1.png new file mode 100644 index 000000000..99ed264bb Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIView.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIView.2.txt new file mode 100644 index 000000000..d195de6a4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIView.2.txt @@ -0,0 +1,3 @@ +; layer = > + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 0; 26 26); layer = > + | ; layer = > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIViewControllerLifeCycle.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIViewControllerLifeCycle.1.png new file mode 100644 index 000000000..4f5902207 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIViewControllerLifeCycle.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIViewControllerLifeCycle.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIViewControllerLifeCycle.2.png new file mode 100644 index 000000000..4f5902207 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testUIViewControllerLifeCycle.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewControllerHierarchy.1.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewControllerHierarchy.1.txt new file mode 100644 index 000000000..7619b9813 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewControllerHierarchy.1.txt @@ -0,0 +1,12 @@ +, state: appeared, view: + | , state: appeared, view: + | | , state: appeared, view: <_UIPageViewControllerContentView> + | | | , state: appeared, view: + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png new file mode 100644 index 000000000..51ad3d3c0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png new file mode 100644 index 000000000..995c02dca Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png new file mode 100644 index 000000000..de2622fdb Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebView.1.png new file mode 100644 index 000000000..205c081d5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png new file mode 100644 index 000000000..175663618 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png new file mode 100644 index 000000000..b7eada627 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/iPadOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testCGPath.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testCGPath.1.png new file mode 100644 index 000000000..ecb2118d5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testCGPath.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testCGPath.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testCGPath.2.txt new file mode 100644 index 000000000..c169fc6bf --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testCGPath.2.txt @@ -0,0 +1,15 @@ +MoveTo (0.0, 0.0) +LineTo (0.0, 60.0) +QuadCurveTo (3.75, 86.25) (30.0, 90.0) +QuadCurveTo (56.25, 86.25) (60.0, 60.0) +CurveTo (75.0, 60.0) (90.0, 45.0) (90.0, 30.0) +CurveTo (90.0, 15.0) (75.0, 0.0) (60.0, 0.0) +LineTo (0.0, 0.0) +Close + +MoveTo (90.0, 75.0) +CurveTo (90.0, 83.284) (83.284, 90.0) (75.0, 90.0) +CurveTo (66.716, 90.0) (60.0, 83.284) (60.0, 75.0) +CurveTo (60.0, 66.716) (66.716, 60.0) (75.0, 60.0) +CurveTo (83.284, 60.0) (90.0, 66.716) (90.0, 75.0) +Close diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testImagePrecision.exact.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testImagePrecision.exact.png new file mode 100644 index 000000000..3b13d77b9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testImagePrecision.exact.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testImagePrecision.perceptual.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testImagePrecision.perceptual.png new file mode 100644 index 000000000..3b13d77b9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testImagePrecision.perceptual.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSBezierPath.macOS.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSBezierPath.macOS.png new file mode 100644 index 000000000..536429368 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSBezierPath.macOS.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSBezierPath.macOS.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSBezierPath.macOS.txt new file mode 100644 index 000000000..7b6103e19 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSBezierPath.macOS.txt @@ -0,0 +1,14 @@ +MoveTo (0.0, 0.0) +LineTo (0.0, 60.0) +CurveTo (0.0, 75.0) (15.0, 90.0) (30.0, 90.0) +CurveTo (45.0, 90.0) (60.0, 75.0) (60.0, 60.0) +CurveTo (75.0, 60.0) (90.0, 45.0) (90.0, 30.0) +CurveTo (90.0, 15.0) (75.0, 0.0) (60.0, 0.0) +LineTo (0.0, 0.0) +Close + +MoveTo (85.607, 64.393) +CurveTo (91.464, 70.251) (91.464, 79.749) (85.607, 85.607) +CurveTo (79.749, 91.464) (70.251, 91.464) (64.393, 85.607) +CurveTo (58.536, 79.749) (58.536, 70.251) (64.393, 64.393) +CurveTo (70.251, 58.536) (79.749, 58.536) (85.607, 64.393) diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSView.1.png new file mode 100644 index 000000000..7ba9fd4c9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSView.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSView.2.txt new file mode 100644 index 000000000..8affba6db --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSView.2.txt @@ -0,0 +1,5 @@ +[ AF ! w ] h=--- v=--- NSButton "Push Me" f=(-7,-7,87,32) b=(-) => + [ A ! ] h=--- v=--- _TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener f=(7,5,73,20) b=(-) + [ A ! w ] h=--- v=--- NSButtonBezelView f=(0,0,87,32) b=(-) => + [ AF ! w ] h=--- v=--- NSButtonTextField "Push Me" f=(11,6,65,16) b=(-) => +A=autoresizesSubviews, C=canDrawConcurrently, D=needsDisplay, F=flipped, G=gstate, H=hidden (h=by ancestor), L=needsLayout (l=child needsLayout), U=needsUpdateConstraints (u=child needsUpdateConstraints), O=opaque, P=preservesContentDuringLiveResize, S=scaled/rotated, W=wantsLayer (w=ancestor wantsLayer), V=needsVibrancy (v=allowsVibrancy), #=has surface diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSViewWithLayer.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSViewWithLayer.1.png new file mode 100644 index 000000000..c095b7c6a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSViewWithLayer.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSViewWithLayer.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSViewWithLayer.2.txt new file mode 100644 index 000000000..8adf10aaa --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testNSViewWithLayer.2.txt @@ -0,0 +1,3 @@ +[ A ! W ] h=--- v=--- NSView f=(0,0,10,10) b=(-) => + [ A ! ] h=--- v=--- _TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener f=(0,0,10,10) b=(-) +A=autoresizesSubviews, C=canDrawConcurrently, D=needsDisplay, F=flipped, G=gstate, H=hidden (h=by ancestor), L=needsLayout (l=child needsLayout), U=needsUpdateConstraints (u=child needsUpdateConstraints), O=opaque, P=preservesContentDuringLiveResize, S=scaled/rotated, W=wantsLayer (w=ancestor wantsLayer), V=needsVibrancy (v=allowsVibrancy), #=has surface diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testPrecision.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testPrecision.1.png new file mode 100644 index 000000000..fb7f10089 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testPrecision.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testPrecision.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testPrecision.2.png new file mode 100644 index 000000000..4a6a933b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testPrecision.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSCNView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSCNView.1.png new file mode 100644 index 000000000..602aed5ca Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSCNView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSKView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSKView.1.png new file mode 100644 index 000000000..88a99a5ec Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSKView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.1.png new file mode 100644 index 000000000..cccd01672 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.fixed.png new file mode 100644 index 000000000..121e0c64a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.size-that-fits.png new file mode 100644 index 000000000..cccd01672 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testSwiftUIView_iOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebView.1.png new file mode 100644 index 000000000..e5ad63af2 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png new file mode 100644 index 000000000..3e2e77a67 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png new file mode 100644 index 000000000..ce42cef50 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/macOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.1@1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.1@1.png new file mode 100644 index 000000000..13a85e2e1 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.1@1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.1@2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.1@2.png new file mode 100644 index 000000000..a7376ade4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.1@2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.iPad-image.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.iPad-image.png new file mode 100644 index 000000000..2b3b9698b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.iPad-image.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png new file mode 100644 index 000000000..4ebae157a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAutolayout.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAutolayout.1.png new file mode 100644 index 000000000..51f26a68a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testAutolayout.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCGPath.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCGPath.1.png new file mode 100644 index 000000000..97706342d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCGPath.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCGPath.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCGPath.2.txt new file mode 100644 index 000000000..c169fc6bf --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCGPath.2.txt @@ -0,0 +1,15 @@ +MoveTo (0.0, 0.0) +LineTo (0.0, 60.0) +QuadCurveTo (3.75, 86.25) (30.0, 90.0) +QuadCurveTo (56.25, 86.25) (60.0, 60.0) +CurveTo (75.0, 60.0) (90.0, 45.0) (90.0, 30.0) +CurveTo (90.0, 15.0) (75.0, 0.0) (60.0, 0.0) +LineTo (0.0, 0.0) +Close + +MoveTo (90.0, 75.0) +CurveTo (90.0, 83.284) (83.284, 90.0) (75.0, 90.0) +CurveTo (66.716, 90.0) (60.0, 83.284) (60.0, 75.0) +CurveTo (60.0, 66.716) (66.716, 60.0) (75.0, 60.0) +CurveTo (83.284, 60.0) (90.0, 66.716) (90.0, 75.0) +Close diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png new file mode 100644 index 000000000..e1b035981 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png new file mode 100644 index 000000000..d7c6cf401 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png new file mode 100644 index 000000000..2d83de434 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png new file mode 100644 index 000000000..d7c6cf401 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testImagePrecision.exact.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testImagePrecision.exact.png new file mode 100644 index 000000000..dc1d02086 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testImagePrecision.exact.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testImagePrecision.perceptual.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testImagePrecision.perceptual.png new file mode 100644 index 000000000..dc1d02086 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testImagePrecision.perceptual.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testPrecision.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testPrecision.1.png new file mode 100644 index 000000000..8e33a40cd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testPrecision.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testPrecision.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testPrecision.2.png new file mode 100644 index 000000000..d606fac89 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testPrecision.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSCNView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSCNView.1.png new file mode 100644 index 000000000..938df70ae Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSCNView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSKView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSKView.1.png new file mode 100644 index 000000000..2033a2b4f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSKView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.1.png new file mode 100644 index 000000000..f770b5345 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.device.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.device.png new file mode 100644 index 000000000..0893939f2 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.device.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.fixed.png new file mode 100644 index 000000000..533192797 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.size-that-fits.png new file mode 100644 index 000000000..f770b5345 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_iOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.1.png new file mode 100644 index 000000000..f770b5345 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.device.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.device.png new file mode 100644 index 000000000..147ec8ad7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.device.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.fixed.png new file mode 100644 index 000000000..fa6a14784 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.size-that-fits.png new file mode 100644 index 000000000..f770b5345 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testSwiftUIView_tvOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTableViewController.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTableViewController.1.png new file mode 100644 index 000000000..4ebae157a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTableViewController.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-33-split-portrait.png new file mode 100644 index 000000000..ca10ccadb Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-50-split-landscape.png new file mode 100644 index 000000000..b3f40a04a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-66-split-landscape.png new file mode 100644 index 000000000..e4a13d9f8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-66-split-portrait.png new file mode 100644 index 000000000..2c1a503e9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-alternative.png new file mode 100644 index 000000000..0adef2d28 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-split-landscape.png new file mode 100644 index 000000000..36d1c4876 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2.png new file mode 100644 index 000000000..31cdfa4be Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2.txt new file mode 100644 index 000000000..2c7989002 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-10-2.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1080 766); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-33-split-landscape.png new file mode 100644 index 000000000..9f7037095 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-33-split-portrait.png new file mode 100644 index 000000000..d5a63d81c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-50-split-landscape.png new file mode 100644 index 000000000..9e5a843a0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-66-split-landscape.png new file mode 100644 index 000000000..71f69112b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-66-split-portrait.png new file mode 100644 index 000000000..b88c9cd60 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-alternative.png new file mode 100644 index 000000000..071dbfab9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7.png new file mode 100644 index 000000000..44a65a2c9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7.txt new file mode 100644 index 000000000..75580a085 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-9-7.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1024 724); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-33-split-landscape.png new file mode 100644 index 000000000..4ca62c1b9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-33-split-portrait.png new file mode 100644 index 000000000..3127c6d13 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-50-split-landscape.png new file mode 100644 index 000000000..10c715a87 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-66-split-landscape.png new file mode 100644 index 000000000..0b7a5a85f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-66-split-portrait.png new file mode 100644 index 000000000..d9d6a63ac Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-alternative.png new file mode 100644 index 000000000..7f41d896a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini.png new file mode 100644 index 000000000..7ef10f4f5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini.txt new file mode 100644 index 000000000..5c93e9e6d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-mini.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1133 700); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..03077a717 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5.png new file mode 100644 index 000000000..413417124 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5.txt new file mode 100644 index 000000000..feadfe037 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10-5.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1194 790); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png new file mode 100644 index 000000000..1c0fde842 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png new file mode 100644 index 000000000..9f0d92c24 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png new file mode 100644 index 000000000..c881636bd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png new file mode 100644 index 000000000..767952656 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png new file mode 100644 index 000000000..a5d3cd3df Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11-alternative.png new file mode 100644 index 000000000..03077a717 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11.png new file mode 100644 index 000000000..413417124 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11.txt new file mode 100644 index 000000000..feadfe037 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1194 790); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png new file mode 100644 index 000000000..1c0fde842 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png new file mode 100644 index 000000000..9f0d92c24 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png new file mode 100644 index 000000000..c881636bd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png new file mode 100644 index 000000000..767952656 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png new file mode 100644 index 000000000..a5d3cd3df Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..ff5c7ef3b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9.png new file mode 100644 index 000000000..fc34ce750 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9.txt new file mode 100644 index 000000000..30560e25c --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12-9.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 54; 1366 920); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png new file mode 100644 index 000000000..1c1f9e8ed Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png new file mode 100644 index 000000000..45c84eb7f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png new file mode 100644 index 000000000..64bfd896c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png new file mode 100644 index 000000000..6b2ad5ea3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png new file mode 100644 index 000000000..d6da31865 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-alternative.png new file mode 100644 index 000000000..56b9d9894 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus-alternative.png new file mode 100644 index 000000000..e46e02998 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus.png new file mode 100644 index 000000000..477d99145 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus.txt new file mode 100644 index 000000000..4a8277ffe --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8-plus.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 414 716); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8.txt new file mode 100644 index 000000000..ee01fb164 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-8.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-large.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-large.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-medium.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-accessibility-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-alternative.png new file mode 100644 index 000000000..56b9d9894 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-extra-large.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-large.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-small.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-extra-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-large.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-medium.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-small.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se.png new file mode 100644 index 000000000..b4864ec77 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se.txt new file mode 100644 index 000000000..ee01fb164 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-se.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x-alternative.png new file mode 100644 index 000000000..a239b8431 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x.png new file mode 100644 index 000000000..c94afc0e4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x.txt new file mode 100644 index 000000000..1703f957b --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-x.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 375 734); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr-alternative.png new file mode 100644 index 000000000..6a21b22a0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr.png new file mode 100644 index 000000000..78d74347f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr.txt new file mode 100644 index 000000000..38b2c8cf3 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xr.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 414 818); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max-alternative.png new file mode 100644 index 000000000..6a21b22a0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max.png new file mode 100644 index 000000000..78d74347f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max.txt new file mode 100644 index 000000000..38b2c8cf3 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.iphone-xs-max.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 414 818); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.tv.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.tv.png new file mode 100644 index 000000000..7b36419e4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.tv.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.tv4k.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.tv4k.png new file mode 100644 index 000000000..586534100 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraits.tv4k.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png new file mode 100644 index 000000000..298667b0f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png new file mode 100644 index 000000000..5a9f55884 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png new file mode 100644 index 000000000..15630382c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png new file mode 100644 index 000000000..893ee385a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png new file mode 100644 index 000000000..e3fdb2962 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png new file mode 100644 index 000000000..85b32ebc9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..0e35a5312 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png new file mode 100644 index 000000000..5058115ab Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png new file mode 100644 index 000000000..0e35a5312 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png new file mode 100644 index 000000000..5058115ab Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..07d9e8cd0 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png new file mode 100644 index 000000000..4550ed9c9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png new file mode 100644 index 000000000..f7b434fb7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png new file mode 100644 index 000000000..3ac018276 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png new file mode 100644 index 000000000..e15e63487 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png new file mode 100644 index 000000000..d1be20da5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png new file mode 100644 index 000000000..f7b434fb7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png new file mode 100644 index 000000000..d1be20da5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png new file mode 100644 index 000000000..fb6ccf0d5 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png new file mode 100644 index 000000000..58f2821fa Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png new file mode 100644 index 000000000..d2e771083 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png new file mode 100644 index 000000000..9488502cf Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png new file mode 100644 index 000000000..d2e771083 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png new file mode 100644 index 000000000..9488502cf Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-large.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-large.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-medium.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-accessibility-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-extra-large.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-large.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-small.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-extra-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-large.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-medium.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-small.png new file mode 100644 index 000000000..d18565167 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithView.label-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-large.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-large.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-small.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-small.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-extra-small.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-large.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-medium.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-medium.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-medium.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-small.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-small.txt new file mode 100644 index 000000000..043c6da68 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testTraitsWithViewController.label-small.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIView.1.png new file mode 100644 index 000000000..f230ebda7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIView.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIView.2.txt new file mode 100644 index 000000000..f30690024 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIView.2.txt @@ -0,0 +1,13 @@ +; layer = > + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 0; 124.5 84.5); layer = > + | <_UIFloatingContentView; frame = (0 0; 124.5 84.5); opaque = NO; layer = > + | | > + | | <_UIFloatingContentTransformView; frame = (0 0; 124.5 84.5); layer = > + | | | <_UIFloatingContentCornerRadiusAnimatingView; frame = (0 0; 124.5 84.5); opaque = NO; layer = > + | | | | ; }; layer = > effect= style=UIBlurEffectStyleATVSemiAutomatic invertedAutomaticStyle + | | | | | <_UIVisualEffectBackdropView; frame = (0 0; 124.5 84.5); autoresize = W+H; userInteractionEnabled = NO; layer = > + | | | | | <_UIVisualEffectSubview; frame = (0 0; 124.5 84.5); alpha = 0.4; autoresize = W+H; userInteractionEnabled = NO; backgroundColor = UIExtendedSRGBColorSpace 0.117647 0.117647 0.117647 1; layer = > + | | | <_UIFloatingContentCornerRadiusAnimatingScreenScaleInheritingView; frame = (0 0; 124.5 84.5); clipsToBounds = YES; opaque = NO; layer = > + | | | | <_UIFloatingContentCornerRadiusAnimatingView; frame = (0 0; 124.5 84.5); layer = > + | | | | > + | | | | | ; layer = > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIViewControllerLifeCycle.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIViewControllerLifeCycle.1.png new file mode 100644 index 000000000..51f26a68a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIViewControllerLifeCycle.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIViewControllerLifeCycle.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIViewControllerLifeCycle.2.png new file mode 100644 index 000000000..51f26a68a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testUIViewControllerLifeCycle.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewControllerHierarchy.1.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewControllerHierarchy.1.txt new file mode 100644 index 000000000..7619b9813 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewControllerHierarchy.1.txt @@ -0,0 +1,12 @@ +, state: appeared, view: + | , state: appeared, view: + | | , state: appeared, view: <_UIPageViewControllerContentView> + | | | , state: appeared, view: + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png new file mode 100644 index 000000000..dacfa9b39 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png new file mode 100644 index 000000000..c3721613c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png new file mode 100644 index 000000000..32bcbb82d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/tvOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.1@1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.1@1.png new file mode 100644 index 000000000..87f38cccd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.1@1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.1@2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.1@2.png new file mode 100644 index 000000000..41454f9ec Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.1@2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.iPad-image.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.iPad-image.png new file mode 100644 index 000000000..ee9a8ae89 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.iPad-image.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png new file mode 100644 index 000000000..f4bb189cc Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAssertMultipleSnapshot.iPhoneSe-image.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAutolayout.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAutolayout.1.png new file mode 100644 index 000000000..472e3182e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testAutolayout.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCGPath.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCGPath.1.png new file mode 100644 index 000000000..97706342d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCGPath.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCGPath.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCGPath.2.txt new file mode 100644 index 000000000..c169fc6bf --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCGPath.2.txt @@ -0,0 +1,15 @@ +MoveTo (0.0, 0.0) +LineTo (0.0, 60.0) +QuadCurveTo (3.75, 86.25) (30.0, 90.0) +QuadCurveTo (56.25, 86.25) (60.0, 60.0) +CurveTo (75.0, 60.0) (90.0, 45.0) (90.0, 30.0) +CurveTo (90.0, 15.0) (75.0, 0.0) (60.0, 0.0) +LineTo (0.0, 0.0) +Close + +MoveTo (90.0, 75.0) +CurveTo (90.0, 83.284) (83.284, 90.0) (75.0, 90.0) +CurveTo (66.716, 90.0) (60.0, 83.284) (60.0, 75.0) +CurveTo (60.0, 66.716) (66.716, 60.0) (75.0, 60.0) +CurveTo (83.284, 60.0) (90.0, 66.716) (90.0, 75.0) +Close diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png new file mode 100644 index 000000000..16aa18f65 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.ipad.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png new file mode 100644 index 000000000..d7c6cf401 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphone8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png new file mode 100644 index 000000000..6c5d5a085 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneMax.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png new file mode 100644 index 000000000..d7c6cf401 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testCollectionViewsWithMultipleScreenSizes.iphoneSe.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testEmbeddedWebView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testEmbeddedWebView.1.png new file mode 100644 index 000000000..682e246c7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testEmbeddedWebView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testImagePrecision.exact.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testImagePrecision.exact.png new file mode 100644 index 000000000..dc1d02086 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testImagePrecision.exact.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testImagePrecision.perceptual.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testImagePrecision.perceptual.png new file mode 100644 index 000000000..dc1d02086 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testImagePrecision.perceptual.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testPrecision.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testPrecision.1.png new file mode 100644 index 000000000..087c20e30 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testPrecision.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testPrecision.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testPrecision.2.png new file mode 100644 index 000000000..45939e9d9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testPrecision.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSCNView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSCNView.1.png new file mode 100644 index 000000000..938df70ae Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSCNView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSKView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSKView.1.png new file mode 100644 index 000000000..2033a2b4f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSKView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.1.png new file mode 100644 index 000000000..f152bda20 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.device.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.device.png new file mode 100644 index 000000000..a050cb216 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.device.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.fixed.png new file mode 100644 index 000000000..057f3ef63 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.size-that-fits.png new file mode 100644 index 000000000..f152bda20 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_iOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.1.png new file mode 100644 index 000000000..7aceafd42 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.device.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.device.png new file mode 100644 index 000000000..a780020be Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.device.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.fixed.png new file mode 100644 index 000000000..f6ca577a1 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.size-that-fits.png new file mode 100644 index 000000000..7aceafd42 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testSwiftUIView_tvOS.size-that-fits.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTableViewController.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTableViewController.1.png new file mode 100644 index 000000000..f4bb189cc Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTableViewController.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-33-split-portrait.png new file mode 100644 index 000000000..d2174470f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-50-split-landscape.png new file mode 100644 index 000000000..5a88939a9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-66-split-landscape.png new file mode 100644 index 000000000..9c5b7b38f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-66-split-portrait.png new file mode 100644 index 000000000..cd0d69ef6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-alternative.png new file mode 100644 index 000000000..c61af9318 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-split-landscape.png new file mode 100644 index 000000000..3df93a89d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2.png new file mode 100644 index 000000000..4e717d97c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2.txt new file mode 100644 index 000000000..cde6f2e26 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-10-2.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1080 766); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-33-split-landscape.png new file mode 100644 index 000000000..1917871f8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-33-split-portrait.png new file mode 100644 index 000000000..d2174470f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-50-split-landscape.png new file mode 100644 index 000000000..6fbbc67cb Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-66-split-landscape.png new file mode 100644 index 000000000..ffcdec793 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-66-split-portrait.png new file mode 100644 index 000000000..cd0d69ef6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-alternative.png new file mode 100644 index 000000000..c61af9318 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7.png new file mode 100644 index 000000000..6cbd710c2 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7.txt new file mode 100644 index 000000000..136e0ae26 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-9-7.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1024 724); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-33-split-landscape.png new file mode 100644 index 000000000..1b0fef24a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-33-split-portrait.png new file mode 100644 index 000000000..9de24e4e1 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-50-split-landscape.png new file mode 100644 index 000000000..eb0bfc225 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-66-split-landscape.png new file mode 100644 index 000000000..1699c0a33 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-66-split-portrait.png new file mode 100644 index 000000000..47ef6b3d4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-alternative.png new file mode 100644 index 000000000..ca4e4b041 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini.png new file mode 100644 index 000000000..1e32c4f9c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini.txt new file mode 100644 index 000000000..ac2132f0e --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-mini.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1133 700); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..fecbefa7a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5.png new file mode 100644 index 000000000..d4fcd9756 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5.txt new file mode 100644 index 000000000..5140062e5 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10-5.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1194 790); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png new file mode 100644 index 000000000..7ea31c83c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png new file mode 100644 index 000000000..cace56c55 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png new file mode 100644 index 000000000..497a5d85b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png new file mode 100644 index 000000000..02d194d85 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png new file mode 100644 index 000000000..42af90c7a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-10inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11-alternative.png new file mode 100644 index 000000000..fecbefa7a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11.png new file mode 100644 index 000000000..d4fcd9756 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11.txt new file mode 100644 index 000000000..5140062e5 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1194 790); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png new file mode 100644 index 000000000..7ea31c83c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png new file mode 100644 index 000000000..cace56c55 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png new file mode 100644 index 000000000..497a5d85b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png new file mode 100644 index 000000000..02d194d85 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png new file mode 100644 index 000000000..42af90c7a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-11inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..ed2042dca Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9.png new file mode 100644 index 000000000..7cbb6e9d9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9.txt new file mode 100644 index 000000000..8af5146f8 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12-9.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 24; 1366 980); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png new file mode 100644 index 000000000..f87f6a22a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-33-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png new file mode 100644 index 000000000..4233b4fa8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-33-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png new file mode 100644 index 000000000..079c21f38 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-50-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png new file mode 100644 index 000000000..1715c4984 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-66-split-landscape.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png new file mode 100644 index 000000000..cd0d69ef6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.ipad-pro-12inch-66-split-portrait.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-alternative.png new file mode 100644 index 000000000..3f73adbfd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus-alternative.png new file mode 100644 index 000000000..5c1db6e4e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus.png new file mode 100644 index 000000000..eb0cc0cdd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus.txt new file mode 100644 index 000000000..2b01a666b --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8-plus.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 414 716); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8.txt new file mode 100644 index 000000000..77dfebbc4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-8.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-large.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-large.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-medium.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-accessibility-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-alternative.png new file mode 100644 index 000000000..3f73adbfd Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-extra-large.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-large.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-small.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-extra-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-large.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-medium.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-small.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se.png new file mode 100644 index 000000000..71a4165b8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se.txt new file mode 100644 index 000000000..77dfebbc4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-se.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x-alternative.png new file mode 100644 index 000000000..be48f80f8 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x.png new file mode 100644 index 000000000..0a79fc46f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x.txt new file mode 100644 index 000000000..e20adf8a9 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-x.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 375 734); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr-alternative.png new file mode 100644 index 000000000..913c9a899 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr.png new file mode 100644 index 000000000..50366d8d9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr.txt new file mode 100644 index 000000000..896497867 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xr.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 414 818); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max-alternative.png new file mode 100644 index 000000000..913c9a899 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max.png new file mode 100644 index 000000000..50366d8d9 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max.txt new file mode 100644 index 000000000..896497867 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.iphone-xs-max.txt @@ -0,0 +1,6 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 44; 414 818); layer = > + | > + | > + | > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.tv.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.tv.png new file mode 100644 index 000000000..4a9363889 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.tv.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.tv4k.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.tv4k.png new file mode 100644 index 000000000..32f2aba5e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraits.tv4k.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png new file mode 100644 index 000000000..53cfbf322 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png new file mode 100644 index 000000000..a861abf24 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-10-2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png new file mode 100644 index 000000000..670d27aea Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png new file mode 100644 index 000000000..ba8e950ae Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-9-7.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png new file mode 100644 index 000000000..a4f7f6740 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png new file mode 100644 index 000000000..80506fd8f Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-mini.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png new file mode 100644 index 000000000..a8458d3c3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png new file mode 100644 index 000000000..563505cb6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-10-5.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png new file mode 100644 index 000000000..a8458d3c3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png new file mode 100644 index 000000000..563505cb6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-11.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png new file mode 100644 index 000000000..748eaf770 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png new file mode 100644 index 000000000..aaf0a1332 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.ipad-pro-12-9.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png new file mode 100644 index 000000000..0be9a3aa3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png new file mode 100644 index 000000000..66b60554d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png new file mode 100644 index 000000000..d52ac080d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8-plus.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png new file mode 100644 index 000000000..08b8ea829 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-8.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png new file mode 100644 index 000000000..0be9a3aa3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png new file mode 100644 index 000000000..08b8ea829 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-se.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png new file mode 100644 index 000000000..fdec7baed Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png new file mode 100644 index 000000000..b6371ca03 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-x.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png new file mode 100644 index 000000000..165d922e7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png new file mode 100644 index 000000000..30a766a93 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xr.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png new file mode 100644 index 000000000..165d922e7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max-alternative.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png new file mode 100644 index 000000000..30a766a93 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsEmbeddedInTabNavigation.iphone-xs-max.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png new file mode 100644 index 000000000..f99b53ee4 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png new file mode 100644 index 000000000..9ca80e479 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-large.png new file mode 100644 index 000000000..d84d32c64 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-large.png new file mode 100644 index 000000000..92d467e0b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-medium.png new file mode 100644 index 000000000..c4e31d591 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-accessibility-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png new file mode 100644 index 000000000..43b3b5e9b Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-extra-large.png new file mode 100644 index 000000000..62f26e863 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-large.png new file mode 100644 index 000000000..0be9813c7 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-small.png new file mode 100644 index 000000000..55548fbce Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-extra-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-large.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-large.png new file mode 100644 index 000000000..24b766e2d Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-large.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-medium.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-medium.png new file mode 100644 index 000000000..ca791fa60 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-medium.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-small.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-small.png new file mode 100644 index 000000000..b54cf27e6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithView.label-small.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt new file mode 100644 index 000000000..09fe5f611 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt new file mode 100644 index 000000000..7a596e789 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt new file mode 100644 index 000000000..bd945125d --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-large.txt new file mode 100644 index 000000000..4739458f6 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt new file mode 100644 index 000000000..5059f1c52 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-accessibility-medium.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt new file mode 100644 index 000000000..875ac4d18 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt new file mode 100644 index 000000000..2a361a5b4 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-large.txt new file mode 100644 index 000000000..c2669cfa8 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-small.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-small.txt new file mode 100644 index 000000000..ab5d9911a --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-extra-small.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-large.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-large.txt new file mode 100644 index 000000000..ac5e567f9 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-large.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-medium.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-medium.txt new file mode 100644 index 000000000..d7083e56a --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-medium.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-small.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-small.txt new file mode 100644 index 000000000..48ed251e0 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testTraitsWithViewController.label-small.txt @@ -0,0 +1,3 @@ +> + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 20; 375 647); layer = > + | > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIView.1.png new file mode 100644 index 000000000..28b1dbd6a Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIView.2.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIView.2.txt new file mode 100644 index 000000000..a3fdb4fbf --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIView.2.txt @@ -0,0 +1,3 @@ +; layer = > + | <_TtC11XCTSnapshotP33_CD07B10D49DF51CB5D5D85C591EE4D9618UIViewSizeListener; frame = (0 0; 26 26); layer = > + | ; layer = > \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIViewControllerLifeCycle.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIViewControllerLifeCycle.1.png new file mode 100644 index 000000000..472e3182e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIViewControllerLifeCycle.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIViewControllerLifeCycle.2.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIViewControllerLifeCycle.2.png new file mode 100644 index 000000000..472e3182e Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testUIViewControllerLifeCycle.2.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewControllerHierarchy.1.txt b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewControllerHierarchy.1.txt new file mode 100644 index 000000000..7619b9813 --- /dev/null +++ b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewControllerHierarchy.1.txt @@ -0,0 +1,12 @@ +, state: appeared, view: + | , state: appeared, view: + | | , state: appeared, view: <_UIPageViewControllerContentView> + | | | , state: appeared, view: + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) + | , state: disappeared, view: not in the window + | | , state: disappeared, view: (view not loaded) \ No newline at end of file diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png new file mode 100644 index 000000000..601a90286 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noHeight.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png new file mode 100644 index 000000000..163819a2c Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noWidth.noHeight.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png new file mode 100644 index 000000000..3f1f55dd6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testViewWithZeroHeightOrWidth.noWidth.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebView.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebView.1.png new file mode 100644 index 000000000..b724bca39 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebView.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png new file mode 100644 index 000000000..8612781b6 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebViewWithCancellingNavigationDelegate.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png new file mode 100644 index 000000000..2b234e4aa Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/visionOS/UITests/testWebViewWithManipulatingNavigationDelegate.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.1.png b/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.1.png new file mode 100644 index 000000000..1ee3d34c3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.1.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.fixed.png b/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.fixed.png new file mode 100644 index 000000000..1ee3d34c3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.fixed.png differ diff --git a/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.size-that-fits.png b/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.size-that-fits.png new file mode 100644 index 000000000..1ee3d34c3 Binary files /dev/null and b/Tests/XCSnapshotTestingTests/__Snapshots__/watchOS/UITests/testSwiftUIView_iOS.size-that-fits.png differ