Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
eugenebokhan committed Jun 13, 2020
1 parent 8af1cdc commit 5ef5ef3
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 30 deletions.
Binary file added Media/snapshot.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 22 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# SwiftSnapshotTesting

<p align="left">
<img src="Media/snapshot.jpeg", width="100">
</p>

This project's purpose is to simplify UI testing on iOS.

`SwiftSnapshotTesting` helps to check application's UI with a few lines of code. No need to manually manage reference images any more.
Expand Down Expand Up @@ -40,18 +44,21 @@ pod 'SwiftSnapshotTesting'
}
```

* Assert any UI element

* Assert UI element

```Swift
assert(element: XCUIElement,
testName: String,
threshold: Float = 10,
recording: Bool = false) throws
ignore rects: Set<CGRect>,
configuration: Configuration,
recording: Bool) throws
```

* `element` - element to compare
* `element` - element to compare.
* `testName` - name of the test. It will be used in the name of the reference image file
* `threshold` - the threshold used to compare element with its reference image
* `rects` - rects (possible subviews' frames) to ignore.
* `configuration` current test configuration.
* `recording` - by setting `true` this argument you will record the reference snapshot. By setting `false` you will compare the element with previously recorded snapshot.


Expand All @@ -60,23 +67,21 @@ pod 'SwiftSnapshotTesting'
```Swift
assert(screenshot: XCUIScreenshot,
testName: String,
ignoreStatusBar: Bool = true,
threshold: Float = 10,
recording: Bool = false) throws
ignore ignorables: Set<Ignorable>],
configuration: Configuration,
recording: Bool) throws
```

* Assert any `MTLTexture`
* `screenshot` - screenshot to test.
* `ignorables` - UI elements to ignore. `Ignorable` can be `XCUIElement`, custom `CGRect` or predefined `.statusBar`.

```Swift
assert(texture: MTLTexture,
testName: String,
threshold: Float = 10,
recording: Bool = false) throws
```
# XCTAttachment

After each assertion test `SnapshotTestCase` provides an attachment containing per-pixel L2 distance between snapshot and the corresponding reference and `MTLTexture` with highlighted difference. You are able to look at the diff using [`MTLTextureViewer`](https://github.com/eugenebokhan/MTLTextureViewer/).

## Example
# Example

Your can find a small [example](https://github.com/eugenebokhan/Image-Flip/blob/master/ImageFlip/ImageFlipUITests/ImageFlipUITests.swift) of usage of `SwiftSnapshotTesting` in my [`ImageFlip`](https://github.com/eugenebokhan/Image-Flip) repo.
Your can find a small [example](https://github.com/eugenebokhan/ImageFlip/blob/master/ImageFlipUITests/ImageFlipUITests.swift) of usage of `SwiftSnapshotTesting` in my [`ImageFlip`](https://github.com/eugenebokhan/ImageFlip/) repo.

# [License](LICENSE)

Expand Down
77 changes: 64 additions & 13 deletions Sources/SnapshotTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,25 @@ import ResourcesBridge

open class SnapshotTestCase: XCTestCase {

/// Snapshot configuration.
public struct Configuration {

/// The way of comparing snapshots with reference images.
public enum ComparisonPolicy {
/// Per-pixel L2 distance.
case eucledean(Float)

func toEucledean() -> Float {
fileprivate func toEucledean() -> Float {
switch self {
case let .eucledean(threshold):
return threshold
}
}
}

/// The way of comparing snapshots with reference images.
public var comparisonPolicy: ComparisonPolicy
/// The color of the highligted difference in the test attachment.
public var diffHighlightColor: SIMD4<Float>

public init(comparisonPolicy: ComparisonPolicy = .eucledean(10),
Expand All @@ -31,6 +36,7 @@ open class SnapshotTestCase: XCTestCase {
public static let `default` = Configuration()
}

/// Element to ignore while asserting snapshot.
public enum Ignorable: Hashable {
case element(XCUIElement)
case rect(CGRect)
Expand All @@ -47,11 +53,27 @@ open class SnapshotTestCase: XCTestCase {

public enum Error: Swift.Error {
case resourceSendingFailed
case snaphotAssertingFailed
case cgImageCreeationFailed
case recordModeIsOn

var localizedDescription: String {
switch self {
case .resourceSendingFailed:
return "Failed to send reference snapshot."
case .cgImageCreeationFailed:
return "Failed to create a `CGImage` of current snapshot."
case .recordModeIsOn:
return """
Recording mode is on.
Turn recording mode off and re-run the test with the newly-recorded reference.
"""
}
}
}

// MARK: - Public Properties


/// The directory in Mac to save reference snapshots.
open var snapshotsReferencesFolder: String { "/" }

// MARK: - Private Properties
Expand All @@ -75,12 +97,25 @@ open class SnapshotTestCase: XCTestCase {
}()

// MARK: - Public


/// Default test name used to give a snapshpot a unique name.
/// - Parameters:
/// - funcName: Current test function.
/// - line: The line of the assertion call.
/// - Returns: String including function name, line and device description.
public func testName(funcName: String = #function,
line: Int = #line) -> String {
return "\(funcName)-\(line)-\(UIDevice.modelName)"
}


/// Test `XCUIElement`.
/// - Parameters:
/// - element: Element to test.
/// - testName: Current test's name.
/// - rects: Rects (possible subviews' frames) to ignore.
/// - configuration: Current test configuration.
/// - recording: Recording mode.
/// - Throws: Error on test fail.
public func assert(element: XCUIElement,
testName: String,
ignore rects: Set<CGRect> = [],
Expand All @@ -91,7 +126,7 @@ open class SnapshotTestCase: XCTestCase {

let screenshot = XCUIApplication().screenshot()
guard let cgImage = screenshot.image.cgImage
else { throw Error.snaphotAssertingFailed }
else { throw Error.cgImageCreeationFailed }

let appFrame = XCUIApplication().frame
let elementFrame = element.frame
Expand Down Expand Up @@ -124,14 +159,22 @@ open class SnapshotTestCase: XCTestCase {
configuration: configuration,
recording: recording)
}


/// Test `XCUIScreenshot`.
/// - Parameters:
/// - screenshot: Screenshot to test.
/// - testName: Current test's name.
/// - ignorables: UI elements to ignore.
/// - configuration: Current test configurartion.
/// - recording: Recording mode.
/// - Throws: Error on test fail.
public func assert(screenshot: XCUIScreenshot,
testName: String,
ignore ignorables: Set<Ignorable> = [.statusBar],
configuration: Configuration = .default,
recording: Bool = false) throws {
guard let cgImage = screenshot.image.cgImage
else { throw Error.snaphotAssertingFailed }
else { throw Error.cgImageCreeationFailed }
let screenTexture = try self.context.texture(from: cgImage,
srgb: false)

Expand All @@ -141,7 +184,18 @@ open class SnapshotTestCase: XCTestCase {
configuration: configuration,
recording: recording)
}


/// Test `MTLTexture`.
///
/// This is a basic assertion function of this framework. Every element and screenshot is converted to texture and passed to this func.
///
/// - Parameters:
/// - texture: Texture to test.
/// - testName: Current test's name.
/// - rects: Rects to ignore.
/// - configuration: Current test configurartion.
/// - recording: Recording mode.
/// - Throws: Error on test fail.
public func assert(texture: MTLTexture,
testName: String,
ignore rects: Set<CGRect> = [],
Expand Down Expand Up @@ -201,10 +255,7 @@ open class SnapshotTestCase: XCTestCase {
}
#endif

XCTFail("""
Turn recording mode off and re-run "\(testName)" to test against the newly-recorded reference.
"""
)
throw Error.recordModeIsOn
} else {
let data: Data
#if targetEnvironment(simulator)
Expand Down

0 comments on commit 5ef5ef3

Please sign in to comment.