From 100b90aaac67a4d518596b5974ad581e17323069 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 6 Oct 2024 11:13:30 -0400 Subject: [PATCH 1/3] Fix assertSnapshot for Swift Testing tests. --- Sources/SnapshotTesting/AssertSnapshot.swift | 9 ++++--- .../Articles/IntegratingWithTestFrameworks.md | 13 +++------- .../MigrationGuides/MigratingTo1.17.md | 17 ++++--------- .../Internal/RecordIssue.swift | 4 +-- .../SnapshotTesting/SnapshotsTestTrait.swift | 6 +---- .../AssertInlineSnapshotSwiftTests.swift | 24 +++++++++++++++++- .../AssertSnapshotSwiftTests.swift | 2 +- .../SnapshotsTraitTests.swift | 2 +- .../SwiftTestingTests.swift | 25 +++++++++++++++++++ .../SwiftTestingTests/testSnapshot.1.txt | 3 +++ .../testSnapshotFailure.1.txt | 3 +++ 11 files changed, 71 insertions(+), 37 deletions(-) create mode 100644 Tests/SnapshotTestingTests/SwiftTestingTests.swift create mode 100644 Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.1.txt create mode 100644 Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshotFailure.1.txt diff --git a/Sources/SnapshotTesting/AssertSnapshot.swift b/Sources/SnapshotTesting/AssertSnapshot.swift index 8837fd9d..85165a58 100644 --- a/Sources/SnapshotTesting/AssertSnapshot.swift +++ b/Sources/SnapshotTesting/AssertSnapshot.swift @@ -1,9 +1,7 @@ import XCTest #if canImport(Testing) - // NB: We are importing only the implementation of Testing because that framework is not available - // in Xcode UI test targets. - @_implementationOnly import Testing + import Testing #endif /// Enhances failure messages with a command line diff tool expression that can be copied and pasted @@ -421,7 +419,10 @@ public func verifySnapshot( if !attachments.isEmpty { #if !os(Linux) && !os(Windows) - if ProcessInfo.processInfo.environment.keys.contains("__XCODE_BUILT_PRODUCTS_DIR_PATHS") { + if + ProcessInfo.processInfo.environment.keys.contains("__XCODE_BUILT_PRODUCTS_DIR_PATHS"), + Test.current == nil + { XCTContext.runActivity(named: "Attached Failure Diff") { activity in attachments.forEach { activity.add($0) diff --git a/Sources/SnapshotTesting/Documentation.docc/Articles/IntegratingWithTestFrameworks.md b/Sources/SnapshotTesting/Documentation.docc/Articles/IntegratingWithTestFrameworks.md index 2f702077..b11b3437 100644 --- a/Sources/SnapshotTesting/Documentation.docc/Articles/IntegratingWithTestFrameworks.md +++ b/Sources/SnapshotTesting/Documentation.docc/Articles/IntegratingWithTestFrameworks.md @@ -67,21 +67,14 @@ class FeatureTests: XCTestCase { This will override the `diffTool` and `record` properties for each test function. -Swift's new testing framework does not currently have a public API for this kind of customization. -There is an experimental feature, called `CustomExecutionTrait`, that does gives us this ability, -and the library provides such a trait called ``Testing/Trait/snapshots(diffTool:record:)``. It can -be attached to any `@Test` or `@Suite` to configure snapshot testing: +Swift's new testing framework also allows for this kind of configuration, both for a single test +and an entire test suite. This is done via what are known as "test traits": ```swift -@_spi(Experimental) import SnapshotTesting +import SnapshotTesting @Suite(.snapshots(record: .all, diffTool: .ksdiff)) struct FeatureTests { … } ``` - -> Important: You must import SnapshotTesting with the `@_spi(Experimental)` attribute to get access -to this functionality because Swift Testing's own `CustomExecutionTrait` is hidden behind the same -SPI flag. This means this API is subject to change in the future, but hopefully Apple will -publicize this tool soon. diff --git a/Sources/SnapshotTesting/Documentation.docc/Articles/MigrationGuides/MigratingTo1.17.md b/Sources/SnapshotTesting/Documentation.docc/Articles/MigrationGuides/MigratingTo1.17.md index 48951630..feefdcae 100644 --- a/Sources/SnapshotTesting/Documentation.docc/Articles/MigrationGuides/MigratingTo1.17.md +++ b/Sources/SnapshotTesting/Documentation.docc/Articles/MigrationGuides/MigratingTo1.17.md @@ -151,16 +151,12 @@ in an XCTest context or a Swift Testing context, and will determine if it should `Issue.record` to trigger a test failure. For the most part you can write tests for Swift Testing exactly as you would for XCTest. However, -there is one major difference. Swift Testing does not (yet) have a substitute for `invokeTest`, -which we used alongside `withSnapshotTesting` to customize snapshotting for a full test class. - -There is an experimental version of this tool in Swift Testing, called `CustomExecutionTrait`, and -this library provides such a trait called ``Testing/Trait/snapshots(diffTool:record:)``. It allows -you to customize snapshots for a `@Test` or `@Suite`, but to get access to it you must perform an -`@_spi(Experimental)` import of snapshot testing: +there is one major difference. In order to override a snapshot's +[configuration]() for a particular test or an entire suite you +must use what are known as "test traits": ```swift -@_spi(Experimental) import SnapshotTesting +import SnapshotTesting @Suite(.snapshots(record: .all, diffTool: .ksdiff)) struct FeatureTests { @@ -169,7 +165,4 @@ struct FeatureTests { ``` That will override the `diffTool` and `record` options for the entire `FeatureTests` suite. - -> Important: As evident by the usage of `@_spi(Experimental)` this API is subject to change. As -soon as the Swift Testing library finalizes its API for `CustomExecutionTrait` we will update -the library accordingly and remove the `@_spi` annotation. +These traits can also be used on individual `@Test`s too. diff --git a/Sources/SnapshotTesting/Internal/RecordIssue.swift b/Sources/SnapshotTesting/Internal/RecordIssue.swift index a8dad29b..21476118 100644 --- a/Sources/SnapshotTesting/Internal/RecordIssue.swift +++ b/Sources/SnapshotTesting/Internal/RecordIssue.swift @@ -1,9 +1,7 @@ import XCTest #if canImport(Testing) - // NB: We are importing only the implementation of Testing because that framework is not available - // in Xcode UI test targets. - @_implementationOnly import Testing + import Testing #endif var isSwiftTesting: Bool { diff --git a/Sources/SnapshotTesting/SnapshotsTestTrait.swift b/Sources/SnapshotTesting/SnapshotsTestTrait.swift index 95c4b791..4b53db67 100644 --- a/Sources/SnapshotTesting/SnapshotsTestTrait.swift +++ b/Sources/SnapshotTesting/SnapshotsTestTrait.swift @@ -1,9 +1,6 @@ #if canImport(Testing) - // NB: We are importing only the implementation of Testing because that framework is not available - // in Xcode UI test targets. - @_implementationOnly import Testing + import Testing - @_spi(Experimental) extension Trait where Self == _SnapshotsTestTrait { /// Configure snapshot testing in a suite or test. /// @@ -33,7 +30,6 @@ } /// A type representing the configuration of snapshot testing. - @_spi(Experimental) public struct _SnapshotsTestTrait: SuiteTrait, TestTrait { public let isRecursive = true let configuration: SnapshotTestingConfiguration diff --git a/Tests/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift b/Tests/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift index 0a193452..382db962 100644 --- a/Tests/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift +++ b/Tests/InlineSnapshotTestingTests/AssertInlineSnapshotSwiftTests.swift @@ -2,7 +2,7 @@ import Testing import Foundation import InlineSnapshotTesting - @_spi(Experimental) import SnapshotTesting + import SnapshotTesting @Suite( .snapshots( @@ -21,6 +21,28 @@ } } + @Test 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 func inlineSnapshot_NamedTrailingClosure() { assertInlineSnapshot( of: ["Hello", "World"], as: .dump, diff --git a/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift b/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift index 385b0f2b..aee524af 100644 --- a/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift +++ b/Tests/SnapshotTestingTests/AssertSnapshotSwiftTests.swift @@ -1,7 +1,7 @@ #if canImport(Testing) import Testing import Foundation - @_spi(Experimental) import SnapshotTesting + import SnapshotTesting @Suite( .snapshots( diff --git a/Tests/SnapshotTestingTests/SnapshotsTraitTests.swift b/Tests/SnapshotTestingTests/SnapshotsTraitTests.swift index 369e1c70..3ac72600 100644 --- a/Tests/SnapshotTestingTests/SnapshotsTraitTests.swift +++ b/Tests/SnapshotTestingTests/SnapshotsTraitTests.swift @@ -1,6 +1,6 @@ #if compiler(>=6) && canImport(Testing) import Testing - @_spi(Experimental) @_spi(Internals) import SnapshotTesting + @_spi(Internals) import SnapshotTesting struct SnapshotsTraitTests { @Test(.snapshots(diffTool: "ksdiff")) diff --git a/Tests/SnapshotTestingTests/SwiftTestingTests.swift b/Tests/SnapshotTestingTests/SwiftTestingTests.swift new file mode 100644 index 00000000..7127981c --- /dev/null +++ b/Tests/SnapshotTestingTests/SwiftTestingTests.swift @@ -0,0 +1,25 @@ +#if compiler(>=6) && canImport(Testing) +import Testing +import SnapshotTesting + +@Suite(.snapshots(diffTool: "ksdiff")) +struct SwiftTestingTests { + @Test func testSnapshot() { + assertSnapshot(of: ["Hello", "World"], as: .dump) + } + + @Test func testSnapshotFailure() { + withKnownIssue { + assertSnapshot(of: ["Goodbye", "World"], as: .dump) + } matching: { issue in + issue.description.hasSuffix(""" + @@ −1,4 +1,4 @@ +  ▿ 2 elements + − - "Hello" + + - "Goodbye" +   - "World" + """) + } + } +} +#endif diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.1.txt b/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.1.txt new file mode 100644 index 00000000..3b0fb714 --- /dev/null +++ b/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshot.1.txt @@ -0,0 +1,3 @@ +▿ 2 elements + - "Hello" + - "World" diff --git a/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshotFailure.1.txt b/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshotFailure.1.txt new file mode 100644 index 00000000..3b0fb714 --- /dev/null +++ b/Tests/SnapshotTestingTests/__Snapshots__/SwiftTestingTests/testSnapshotFailure.1.txt @@ -0,0 +1,3 @@ +▿ 2 elements + - "Hello" + - "World" From da41df7eb92de9e313ef882d990c39a815399bf3 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 6 Oct 2024 11:15:48 -0400 Subject: [PATCH 2/3] wip --- Sources/SnapshotTesting/AssertSnapshot.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SnapshotTesting/AssertSnapshot.swift b/Sources/SnapshotTesting/AssertSnapshot.swift index 85165a58..25aed26f 100644 --- a/Sources/SnapshotTesting/AssertSnapshot.swift +++ b/Sources/SnapshotTesting/AssertSnapshot.swift @@ -421,7 +421,7 @@ public func verifySnapshot( #if !os(Linux) && !os(Windows) if ProcessInfo.processInfo.environment.keys.contains("__XCODE_BUILT_PRODUCTS_DIR_PATHS"), - Test.current == nil + !isSwiftTesting { XCTContext.runActivity(named: "Attached Failure Diff") { activity in attachments.forEach { From 1836b8ce908d1744477f0ac7aaea05a3527e4a87 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 6 Oct 2024 11:58:22 -0400 Subject: [PATCH 3/3] Update CI --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 719b259d..27815d44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,11 @@ jobs: strategy: matrix: xcode: - - "14.3.1" + - 15.4 + - '16.0' - name: macOS 13 (Xcode ${{ matrix.xcode }}) - runs-on: macos-13 + name: macOS + runs-on: macos-14 steps: - uses: actions/checkout@v3 - name: Select Xcode ${{ matrix.xcode }}