diff --git a/FBSnapshotTestCase/FBSnapshotTestCase.h b/FBSnapshotTestCase/FBSnapshotTestCase.h index 4f710f5..36d1293 100644 --- a/FBSnapshotTestCase/FBSnapshotTestCase.h +++ b/FBSnapshotTestCase/FBSnapshotTestCase.h @@ -92,6 +92,12 @@ */ @property (readwrite, nonatomic, assign) BOOL recordMode; +/** + When YES, a test will run and fail when no reference image exists, without needing to run + recordMode first. The fail image is stored and can be reviewed and accepted as a reference. + */ +@property (readwrite, nonatomic, assign) BOOL autoRecord; + /** When @c YES appends the name of the device model and OS to the snapshot file name. The default value is @c NO. diff --git a/FBSnapshotTestCase/FBSnapshotTestCase.m b/FBSnapshotTestCase/FBSnapshotTestCase.m index fd81a13..f1d5c0c 100644 --- a/FBSnapshotTestCase/FBSnapshotTestCase.m +++ b/FBSnapshotTestCase/FBSnapshotTestCase.m @@ -39,6 +39,17 @@ - (void)setRecordMode:(BOOL)recordMode _snapshotController.recordMode = recordMode; } +- (BOOL)autoRecord +{ + return _snapshotController.autoRecord; +} + +- (void)setAutoRecord:(BOOL)autoRecord +{ + NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); + _snapshotController.autoRecord = autoRecord; +} + - (BOOL)isDeviceAgnostic { return _snapshotController.deviceAgnostic; @@ -121,12 +132,15 @@ - (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer } } - if (!testSuccess) { - return [NSString stringWithFormat:@"Snapshot comparison failed: %@", errors.firstObject]; - } if (self.recordMode) { return @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"; } + else if (!testSuccess && !self.autoRecord) { + return [NSString stringWithFormat:@"Snapshot comparison failed: %@", errors.firstObject]; + } + else if (!testSuccess && self.autoRecord) { + return [NSString stringWithFormat:@"No previous reference image. New image has been stored for approval."]; + } return nil; } diff --git a/FBSnapshotTestCase/FBSnapshotTestController.h b/FBSnapshotTestCase/FBSnapshotTestController.h index 1d67163..b6be129 100644 --- a/FBSnapshotTestCase/FBSnapshotTestController.h +++ b/FBSnapshotTestCase/FBSnapshotTestController.h @@ -51,6 +51,11 @@ extern NSString *const FBDiffedImageKey; */ @interface FBSnapshotTestController : NSObject +/** + Auto record snapshots on first run of new tests. + */ +@property (readwrite, nonatomic, assign) BOOL autoRecord; + /** Record snapshots. */ diff --git a/FBSnapshotTestCase/FBSnapshotTestController.m b/FBSnapshotTestCase/FBSnapshotTestController.m index 7aa1bf5..d4700c8 100644 --- a/FBSnapshotTestCase/FBSnapshotTestController.m +++ b/FBSnapshotTestCase/FBSnapshotTestController.m @@ -106,7 +106,7 @@ - (UIImage *)referenceImageForSelector:(SEL)selector UIImage *image = [UIImage imageWithContentsOfFile:filePath]; if (nil == image && NULL != errorPtr) { BOOL exists = [_fileManager fileExistsAtPath:filePath]; - if (!exists) { + if (!exists && !self.autoRecord) { *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain code:FBSnapshotTestControllerErrorCodeNeedsRecord userInfo:@{ @@ -177,7 +177,7 @@ - (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage return NO; } - if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) { + if (!self.autoRecord && ![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) { return NO; } @@ -196,7 +196,7 @@ - (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage UIImage *diffImage = [referenceImage fb_diffWithImage:testImage]; NSData *diffImageData = UIImagePNGRepresentation(diffImage); - if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) { + if (!self.autoRecord && ![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) { return NO; } @@ -280,9 +280,16 @@ - (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer error:(NSError **)errorPtr { UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr]; - if (nil != referenceImage) { + if (nil != referenceImage || self.autoRecord == true) { UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer]; - BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr]; + BOOL imagesSame; + + if (self.autoRecord) { + imagesSame = NO; + } else { + imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr]; + } + if (!imagesSame) { NSError *saveError = nil; if ([self saveFailedReferenceImage:referenceImage testImage:snapshot selector:selector identifier:identifier error:&saveError] == NO) { diff --git a/FBSnapshotTestCase/SwiftSupport.swift b/FBSnapshotTestCase/SwiftSupport.swift index 90477e8..1d63d5d 100644 --- a/FBSnapshotTestCase/SwiftSupport.swift +++ b/FBSnapshotTestCase/SwiftSupport.swift @@ -24,13 +24,18 @@ if let envReferenceImageDirectory = envReferenceImageDirectory { for suffix in suffixes { let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)" + var referenceImageExists = true if viewOrLayer.isKind(of: UIView.self) { do { try compareSnapshot(of: viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance) comparisonSuccess = true } catch let error1 as NSError { - error = error1 comparisonSuccess = false + if error1.code == 1 { + referenceImageExists = false + } else { + error = error1 + } } } else if viewOrLayer.isKind(of: CALayer.self) { do { @@ -39,6 +44,11 @@ } catch let error1 as NSError { error = error1 comparisonSuccess = false + if error1.code == 1 { + referenceImageExists = false + } else { + error = error1 + } } } else { assertionFailure("Only UIView and CALayer classes can be snapshotted") @@ -49,7 +59,13 @@ if comparisonSuccess || recordMode { break } + + assert(self.autoRecord == false || referenceImageExists, message: "No previous reference image. New image has been stored for approval.", file: file, line: line) + if self.autoRecord && !referenceImageExists { + break + } + assert(comparisonSuccess, message: "Snapshot comparison failed: \(String(describing: error))", file: file, line: line) } } else { @@ -86,16 +102,24 @@ public extension FBSnapshotTestCase { try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance) comparisonSuccess = true } catch let error1 as NSError { - error = error1 comparisonSuccess = false + if error1.code == 1 { + referenceImageExists = false + } else { + error = error1 + } } } else if viewOrLayer.isKindOfClass(CALayer) { do { try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance) comparisonSuccess = true } catch let error1 as NSError { - error = error1 comparisonSuccess = false + if error1.code == 1 { + referenceImageExists = false + } else { + error = error1 + } } } else { assertionFailure("Only UIView and CALayer classes can be snapshotted") @@ -107,6 +131,12 @@ public extension FBSnapshotTestCase { break } + assert(self.autoRecord == false || referenceImageExists, message: "No previous reference image. New image has been stored for approval.", file: file, line: line) + + if self.autoRecord && !referenceImageExists { + break + } + assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line) } } else {