This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
Doric/iOS/Example/Pods/Nimble-Snapshots/HaveValidSnapshot.swift
2019-07-25 19:30:14 +08:00

240 lines
9.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import FBSnapshotTestCase
import Foundation
import Nimble
import QuartzCore
import UIKit
@objc public protocol Snapshotable {
var snapshotObject: UIView? { get }
}
extension UIViewController : Snapshotable {
public var snapshotObject: UIView? {
self.beginAppearanceTransition(true, animated: false)
self.endAppearanceTransition()
return view
}
}
extension UIView : Snapshotable {
public var snapshotObject: UIView? {
return self
}
}
@objc public class FBSnapshotTest: NSObject {
var referenceImagesDirectory: String?
var tolerance: CGFloat = 0
static let sharedInstance = FBSnapshotTest()
public class func setReferenceImagesDirectory(_ directory: String?) {
sharedInstance.referenceImagesDirectory = directory
}
// swiftlint:disable:next function_parameter_count
class func compareSnapshot(_ instance: Snapshotable, isDeviceAgnostic: Bool = false,
usesDrawRect: Bool = false, snapshot: String, record: Bool,
referenceDirectory: String, tolerance: CGFloat,
filename: String) -> Bool {
let testName = parseFilename(filename: filename)
let snapshotController: FBSnapshotTestController = FBSnapshotTestController(testName: testName)
snapshotController.isDeviceAgnostic = isDeviceAgnostic
snapshotController.recordMode = record
snapshotController.referenceImagesDirectory = referenceDirectory
snapshotController.usesDrawViewHierarchyInRect = usesDrawRect
let reason = "Missing value for referenceImagesDirectory - " +
"Call FBSnapshotTest.setReferenceImagesDirectory(FB_REFERENCE_IMAGE_DIR)"
assert(snapshotController.referenceImagesDirectory != nil, reason)
do {
try snapshotController.compareSnapshot(ofViewOrLayer: instance.snapshotObject,
selector: Selector(snapshot), identifier: nil, tolerance: tolerance)
} catch {
return false
}
return true
}
}
// Note that these must be lower case.
private var testFolderSuffixes = ["tests", "specs"]
public func setNimbleTestFolder(_ testFolder: String) {
testFolderSuffixes = [testFolder.lowercased()]
}
public func setNimbleTolerance(_ tolerance: CGFloat) {
FBSnapshotTest.sharedInstance.tolerance = tolerance
}
func getDefaultReferenceDirectory(_ sourceFileName: String) -> String {
if let globalReference = FBSnapshotTest.sharedInstance.referenceImagesDirectory {
return globalReference
}
// Search the test file's path to find the first folder with a test suffix,
// then append "/ReferenceImages" and use that.
// Grab the file's path
let pathComponents = (sourceFileName as NSString).pathComponents as NSArray
// Find the directory in the path that ends with a test suffix.
let testPath = pathComponents.first { component -> Bool in
return !testFolderSuffixes.filter {
(component as AnyObject).lowercased.hasSuffix($0)
}.isEmpty
}
guard let testDirectory = testPath else {
fatalError("Could not infer reference image folder You should provide a reference dir using " +
"FBSnapshotTest.setReferenceImagesDirectory(FB_REFERENCE_IMAGE_DIR)")
}
// Recombine the path components and append our own image directory.
let currentIndex = pathComponents.index(of: testDirectory) + 1
let folderPathComponents = pathComponents.subarray(with: NSRange(location: 0, length: currentIndex)) as NSArray
let folderPath = folderPathComponents.componentsJoined(by: "/")
return folderPath + "/ReferenceImages"
}
private func parseFilename(filename: String) -> String {
let nsName = filename as NSString
let type = ".\(nsName.pathExtension)"
let sanitizedName = nsName.lastPathComponent.replacingOccurrences(of: type, with: "")
return sanitizedName
}
func sanitizedTestName(_ name: String?) -> String {
guard let testName = currentTestName() else {
fatalError("Test matchers must be called from inside a test block")
}
var filename = name ?? testName
filename = filename.replacingOccurrences(of: "root example group, ", with: "")
let characterSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_")
let components = filename.components(separatedBy: characterSet.inverted)
return components.joined(separator: "_")
}
func getTolerance() -> CGFloat {
return FBSnapshotTest.sharedInstance.tolerance
}
func clearFailureMessage(_ failureMessage: FailureMessage) {
failureMessage.actualValue = nil
failureMessage.expected = ""
failureMessage.postfixMessage = ""
failureMessage.to = ""
}
private func performSnapshotTest(_ name: String?, isDeviceAgnostic: Bool = false, usesDrawRect: Bool = false,
actualExpression: Expression<Snapshotable>, failureMessage: FailureMessage,
tolerance: CGFloat?) -> Bool {
// swiftlint:disable:next force_try force_unwrapping
let instance = try! actualExpression.evaluate()!
let testFileLocation = actualExpression.location.file
let referenceImageDirectory = getDefaultReferenceDirectory(testFileLocation)
let snapshotName = sanitizedTestName(name)
let tolerance = tolerance ?? getTolerance()
let result = FBSnapshotTest.compareSnapshot(instance, isDeviceAgnostic: isDeviceAgnostic,
usesDrawRect: usesDrawRect, snapshot: snapshotName, record: false,
referenceDirectory: referenceImageDirectory, tolerance: tolerance,
filename: actualExpression.location.file)
if !result {
clearFailureMessage(failureMessage)
failureMessage.expected = "expected a matching snapshot in \(snapshotName)"
}
return result
}
private func recordSnapshot(_ name: String?, isDeviceAgnostic: Bool = false, usesDrawRect: Bool = false,
actualExpression: Expression<Snapshotable>, failureMessage: FailureMessage) -> Bool {
// swiftlint:disable:next force_try force_unwrapping
let instance = try! actualExpression.evaluate()!
let testFileLocation = actualExpression.location.file
let referenceImageDirectory = getDefaultReferenceDirectory(testFileLocation)
let snapshotName = sanitizedTestName(name)
let tolerance = getTolerance()
clearFailureMessage(failureMessage)
if FBSnapshotTest.compareSnapshot(instance, isDeviceAgnostic: isDeviceAgnostic, usesDrawRect: usesDrawRect,
snapshot: snapshotName, record: true, referenceDirectory: referenceImageDirectory,
tolerance: tolerance, filename: actualExpression.location.file) {
let name = name ?? snapshotName
failureMessage.expected = "snapshot \(name) successfully recorded, replace recordSnapshot with a check"
} else {
let expectedMessage: String
if let name = name {
expectedMessage = "expected to record a snapshot in \(name)"
} else {
expectedMessage = "expected to record a snapshot"
}
failureMessage.expected = expectedMessage
}
return false
}
private func currentTestName() -> String? {
return CurrentTestCaseTracker.shared.currentTestCase?.sanitizedName
}
internal var switchChecksWithRecords = false
public func haveValidSnapshot(named name: String? = nil, usesDrawRect: Bool = false,
tolerance: CGFloat? = nil) -> Predicate<Snapshotable> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
if switchChecksWithRecords {
return recordSnapshot(name, usesDrawRect: usesDrawRect, actualExpression: actualExpression,
failureMessage: failureMessage)
}
return performSnapshotTest(name, usesDrawRect: usesDrawRect, actualExpression: actualExpression,
failureMessage: failureMessage, tolerance: tolerance)
}
}
public func haveValidDeviceAgnosticSnapshot(named name: String? = nil, usesDrawRect: Bool = false,
tolerance: CGFloat? = nil) -> Predicate<Snapshotable> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
if switchChecksWithRecords {
return recordSnapshot(name, isDeviceAgnostic: true, usesDrawRect: usesDrawRect,
actualExpression: actualExpression, failureMessage: failureMessage)
}
return performSnapshotTest(name, isDeviceAgnostic: true, usesDrawRect: usesDrawRect,
actualExpression: actualExpression,
failureMessage: failureMessage, tolerance: tolerance)
}
}
public func recordSnapshot(named name: String? = nil, usesDrawRect: Bool = false) -> Predicate<Snapshotable> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return recordSnapshot(name, usesDrawRect: usesDrawRect,
actualExpression: actualExpression, failureMessage: failureMessage)
}
}
public func recordDeviceAgnosticSnapshot(named name: String? = nil,
usesDrawRect: Bool = false) -> Predicate<Snapshotable> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return recordSnapshot(name, isDeviceAgnostic: true, usesDrawRect: usesDrawRect,
actualExpression: actualExpression, failureMessage: failureMessage)
}
}