Skip to content

Commit 46aead8

Browse files
committed
Merge remote-tracking branches 'origin/master' and 'fork1/rtl_support_with_test_plans' into main
* origin/master: Make image diffing strategies custom scale aware (pointfreeco#336) * fork1/rtl_support_with_test_plans: Update README Make configuration directory a subdirectory of file name directory Change snapshot directory url when SNAPSHOT_CONFIGURATION_NAME environment variable is available Remove layout direction trait override
3 parents eeeeab8 + 641ad58 + d2fea93 commit 46aead8

File tree

10 files changed

+47
-19
lines changed

10 files changed

+47
-19
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ end
192192
- **Subclass-free.** Assert from any XCTest case or Quick spec.
193193
- **Device-agnostic snapshots.** Render views and view controllers for specific devices and trait collections from a single simulator.
194194
- **First-class Xcode support.** Image differences are captured as XCTest attachments. Text differences are rendered in inline error messages.
195+
- **Support for Xcode Test Plans**. Just add `SNAPSHOT_CONFIGURATION_NAME` environment variable to every Configuration in your test plan (with distinctive names as values) to save separate snapshots for each one of them. It can be used for making UI snapshots with different langauges and locales.
195196
- **Supports any platform that supports Swift.** Write snapshot tests for iOS, Linux, macOS, and tvOS.
196197
- **SceneKit, SpriteKit, and WebKit support.** Most snapshot testing libraries don't support these view subclasses.
197198
- **`Codable` support**. Snapshot encodable data structures into their [JSON](Documentation/Available-Snapshot-Strategies.md#json) and [property list](Documentation/Available-Snapshot-Strategies.md#plist) representations.

Sources/SnapshotTesting/AssertSnapshot.swift

+12
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,16 @@ public func verifySnapshot<Value, Format>(
178178
do {
179179
let fileUrl = URL(fileURLWithPath: "\(file)", isDirectory: false)
180180
let fileName = fileUrl.deletingPathExtension().lastPathComponent
181+
182+
/// Support for Xcode 11 test plans and running tests multiple times with different configurations
183+
let configurationName = ProcessInfo.processInfo.environment["SNAPSHOT_CONFIGURATION_NAME"]
181184

182185
let snapshotDirectoryUrl = snapshotDirectory.map { URL(fileURLWithPath: $0, isDirectory: true) }
183186
?? fileUrl
184187
.deletingLastPathComponent()
185188
.appendingPathComponent("__Snapshots__")
186189
.appendingPathComponent(fileName)
190+
.appendingPathComponentIfExists(configurationName)
187191

188192
let identifier: String
189193
if let name = name {
@@ -311,3 +315,11 @@ func sanitizePathComponent(_ string: String) -> String {
311315
.replacingOccurrences(of: "\\W+", with: "-", options: .regularExpression)
312316
.replacingOccurrences(of: "^-|-$", with: "", options: .regularExpression)
313317
}
318+
319+
private extension URL {
320+
func appendingPathComponentIfExists(_ pathComponent: String?) -> URL {
321+
guard let pathComponent = pathComponent else { return self }
322+
323+
return appendingPathComponent(pathComponent)
324+
}
325+
}

Sources/SnapshotTesting/Common/View.swift

+12-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import UIKit
1111
import WebKit
1212
#endif
1313

14+
// Explanation for not providing `layoutDirection` trait in base traits for devices:
15+
// .init(layoutDirection: .leftToRight) is commented out because of http://www.openradar.me/radar?id=5044259694575616
16+
// When tests are ran with RTL language (e.g. Arabic), this override does not fully change layout to LTR (due to issue linked above)
17+
// which causes layout to not be correct on snapshots.
18+
// When tests are ran with LTR langauge (e.g. English), this override is not needed.
19+
1420
#if os(iOS) || os(tvOS)
1521
public struct ViewImageConfig {
1622
public enum Orientation {
@@ -352,8 +358,8 @@ extension UITraitCollection {
352358
let base: [UITraitCollection] = [
353359
// .init(displayGamut: .SRGB),
354360
// .init(displayScale: 2),
361+
// .init(layoutDirection: .leftToRight), // See explanation at the beginning of the file
355362
.init(forceTouchCapability: .available),
356-
.init(layoutDirection: .leftToRight),
357363
.init(preferredContentSizeCategory: .medium),
358364
.init(userInterfaceIdiom: .phone)
359365
]
@@ -380,8 +386,8 @@ extension UITraitCollection {
380386
let base: [UITraitCollection] = [
381387
// .init(displayGamut: .P3),
382388
// .init(displayScale: 2),
389+
// .init(layoutDirection: .leftToRight), // See explanation at the beginning of the file
383390
.init(forceTouchCapability: .available),
384-
.init(layoutDirection: .leftToRight),
385391
.init(preferredContentSizeCategory: .medium),
386392
.init(userInterfaceIdiom: .phone)
387393
]
@@ -408,8 +414,8 @@ extension UITraitCollection {
408414
let base: [UITraitCollection] = [
409415
// .init(displayGamut: .P3),
410416
// .init(displayScale: 3),
417+
// .init(layoutDirection: .leftToRight), // See explanation at the beginning of the file
411418
.init(forceTouchCapability: .available),
412-
.init(layoutDirection: .leftToRight),
413419
.init(preferredContentSizeCategory: .medium),
414420
.init(userInterfaceIdiom: .phone)
415421
]
@@ -436,8 +442,8 @@ extension UITraitCollection {
436442
let base: [UITraitCollection] = [
437443
// .init(displayGamut: .P3),
438444
// .init(displayScale: 3),
445+
// .init(layoutDirection: .leftToRight), // See explanation at the beginning of the file
439446
.init(forceTouchCapability: .available),
440-
.init(layoutDirection: .leftToRight),
441447
.init(preferredContentSizeCategory: .medium),
442448
.init(userInterfaceIdiom: .phone)
443449
]
@@ -464,8 +470,8 @@ extension UITraitCollection {
464470
let base: [UITraitCollection] = [
465471
// .init(displayGamut: .P3),
466472
// .init(displayScale: 2),
473+
// .init(layoutDirection: .leftToRight), // See explanation at the beginning of the file
467474
.init(forceTouchCapability: .unavailable),
468-
.init(layoutDirection: .leftToRight),
469475
.init(preferredContentSizeCategory: .medium),
470476
.init(userInterfaceIdiom: .phone)
471477
]
@@ -492,8 +498,8 @@ extension UITraitCollection {
492498
let base: [UITraitCollection] = [
493499
// .init(displayGamut: .P3),
494500
// .init(displayScale: 3),
501+
// .init(layoutDirection: .leftToRight), // See explanation at the beginning of the file
495502
.init(forceTouchCapability: .available),
496-
.init(layoutDirection: .leftToRight),
497503
.init(preferredContentSizeCategory: .medium),
498504
.init(userInterfaceIdiom: .phone)
499505
]

Sources/SnapshotTesting/Snapshotting/CALayer.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ extension Snapshotting where Value == CALayer, Format == UIImage {
3737
/// - Parameter precision: The percentage of pixels that must match.
3838
public static func image(precision: Float = 1, traits: UITraitCollection = .init())
3939
-> Snapshotting {
40-
return SimplySnapshotting.image(precision: precision).pullback { layer in
40+
return SimplySnapshotting.image(precision: precision, scale: traits.displayScale).pullback { layer in
4141
renderer(bounds: layer.bounds, for: traits).image { ctx in
4242
layer.setNeedsLayout()
4343
layer.layoutIfNeeded()

Sources/SnapshotTesting/Snapshotting/CGPath.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ extension Snapshotting where Value == CGPath, Format == UIImage {
4040
///
4141
/// - Parameter precision: The percentage of pixels that must match.
4242
public static func image(precision: Float = 1, scale: CGFloat = 1, drawingMode: CGPathDrawingMode = .eoFill) -> Snapshotting {
43-
return SimplySnapshotting.image(precision: precision).pullback { path in
43+
return SimplySnapshotting.image(precision: precision, scale: scale).pullback { path in
4444
let bounds = path.boundingBoxOfPath
4545
let format: UIGraphicsImageRendererFormat
4646
if #available(iOS 11.0, tvOS 11.0, *) {

Sources/SnapshotTesting/Snapshotting/SwiftUIView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ extension Snapshotting where Value: SwiftUI.View, Format == UIImage {
5151
config = .init(safeArea: .zero, size: size, traits: traits)
5252
}
5353

54-
return SimplySnapshotting.image(precision: precision).asyncPullback { view in
54+
return SimplySnapshotting.image(precision: precision, scale: traits.displayScale).asyncPullback { view in
5555
var config = config
5656

5757
let controller: UIViewController

Sources/SnapshotTesting/Snapshotting/UIBezierPath.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extension Snapshotting where Value == UIBezierPath, Format == UIImage {
1111
///
1212
/// - Parameter precision: The percentage of pixels that must match.
1313
public static func image(precision: Float = 1, scale: CGFloat = 1) -> Snapshotting {
14-
return SimplySnapshotting.image(precision: precision).pullback { path in
14+
return SimplySnapshotting.image(precision: precision, scale: scale).pullback { path in
1515
let bounds = path.bounds
1616
let format: UIGraphicsImageRendererFormat
1717
if #available(iOS 11.0, tvOS 11.0, *) {

Sources/SnapshotTesting/Snapshotting/UIImage.swift

+15-6
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,24 @@ import XCTest
44

55
extension Diffing where Value == UIImage {
66
/// A pixel-diffing strategy for UIImage's which requires a 100% match.
7-
public static let image = Diffing.image(precision: 1)
7+
public static let image = Diffing.image(precision: 1, scale: nil)
88

99
/// A pixel-diffing strategy for UIImage that allows customizing how precise the matching must be.
1010
///
1111
/// - Parameter precision: A value between 0 and 1, where 1 means the images must match 100% of their pixels.
12+
/// - Parameter scale: Scale to use when loading the reference image from disk. If `nil` or the `UITraitCollection`s default value of `0.0`, the screens scale is used.
1213
/// - Returns: A new diffing strategy.
13-
public static func image(precision: Float) -> Diffing {
14+
public static func image(precision: Float, scale: CGFloat?) -> Diffing {
15+
let imageScale: CGFloat
16+
if let scale = scale, scale != 0.0 {
17+
imageScale = scale
18+
} else {
19+
imageScale = UIScreen.main.scale
20+
}
21+
1422
return Diffing(
1523
toData: { $0.pngData() ?? emptyImage().pngData()! },
16-
fromData: { UIImage(data: $0, scale: UIScreen.main.scale)! }
24+
fromData: { UIImage(data: $0, scale: imageScale)! }
1725
) { old, new in
1826
guard !compare(old, new, precision: precision) else { return nil }
1927
let difference = SnapshotTesting.diff(old, new)
@@ -48,16 +56,17 @@ extension Diffing where Value == UIImage {
4856
extension Snapshotting where Value == UIImage, Format == UIImage {
4957
/// A snapshot strategy for comparing images based on pixel equality.
5058
public static var image: Snapshotting {
51-
return .image(precision: 1)
59+
return .image(precision: 1, scale: nil)
5260
}
5361

5462
/// A snapshot strategy for comparing images based on pixel equality.
5563
///
5664
/// - Parameter precision: The percentage of pixels that must match.
57-
public static func image(precision: Float) -> Snapshotting {
65+
/// - Parameter scale: The scale of the reference image stored on disk.
66+
public static func image(precision: Float, scale: CGFloat?) -> Snapshotting {
5867
return .init(
5968
pathExtension: "png",
60-
diffing: .image(precision: precision)
69+
diffing: .image(precision: precision, scale: scale)
6170
)
6271
}
6372
}

Sources/SnapshotTesting/Snapshotting/UIView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extension Snapshotting where Value == UIView, Format == UIImage {
2222
)
2323
-> Snapshotting {
2424

25-
return SimplySnapshotting.image(precision: precision).asyncPullback { view in
25+
return SimplySnapshotting.image(precision: precision, scale: traits.displayScale).asyncPullback { view in
2626
snapshotView(
2727
config: .init(safeArea: .zero, size: size ?? view.frame.size, traits: .init()),
2828
drawHierarchyInKeyWindow: drawHierarchyInKeyWindow,

Sources/SnapshotTesting/Snapshotting/UIViewController.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extension Snapshotting where Value == UIViewController, Format == UIImage {
2222
)
2323
-> Snapshotting {
2424

25-
return SimplySnapshotting.image(precision: precision).asyncPullback { viewController in
25+
return SimplySnapshotting.image(precision: precision, scale: traits.displayScale).asyncPullback { viewController in
2626
snapshotView(
2727
config: size.map { .init(safeArea: config.safeArea, size: $0, traits: config.traits) } ?? config,
2828
drawHierarchyInKeyWindow: false,
@@ -48,7 +48,7 @@ extension Snapshotting where Value == UIViewController, Format == UIImage {
4848
)
4949
-> Snapshotting {
5050

51-
return SimplySnapshotting.image(precision: precision).asyncPullback { viewController in
51+
return SimplySnapshotting.image(precision: precision, scale: traits.displayScale).asyncPullback { viewController in
5252
snapshotView(
5353
config: .init(safeArea: .zero, size: size, traits: traits),
5454
drawHierarchyInKeyWindow: drawHierarchyInKeyWindow,

0 commit comments

Comments
 (0)