diff --git a/Example/AccessibilitySnapshot.xcodeproj/project.pbxproj b/Example/AccessibilitySnapshot.xcodeproj/project.pbxproj index f10f78b8..996ea405 100644 --- a/Example/AccessibilitySnapshot.xcodeproj/project.pbxproj +++ b/Example/AccessibilitySnapshot.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 3D3F2E142263E6B900F7608E /* InvertColorsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3F2E132263E6B900F7608E /* InvertColorsViewController.swift */; }; 3D3F2E162263E94D00F7608E /* InvertColorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3F2E152263E94D00F7608E /* InvertColorsTests.swift */; }; 3D4674682116A3F100278B57 /* ViewAccessibilityPropertiesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D4674672116A3F100278B57 /* ViewAccessibilityPropertiesViewController.swift */; }; + 3D9334942A8B2E520078A142 /* ImpreciseObjectiveCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D9334932A8B2E520078A142 /* ImpreciseObjectiveCTests.m */; }; 3D9894F9213509C8006C16F6 /* DescriptionEdgeCasesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9894F8213509C8006C16F6 /* DescriptionEdgeCasesViewController.swift */; }; 3DA12A3222405B9E00EB3C33 /* DataTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA12A3122405B9E00EB3C33 /* DataTableViewController.swift */; }; 3DBAC28722406EBB00EF4D0A /* AccessibilityContainersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBAC28622406EBB00EF4D0A /* AccessibilityContainersTests.swift */; }; @@ -94,6 +95,7 @@ 3D4674672116A3F100278B57 /* ViewAccessibilityPropertiesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAccessibilityPropertiesViewController.swift; sourceTree = ""; }; 3D881956246E03C00061DA6A /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3D88195A246E03C00061DA6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3D9334932A8B2E520078A142 /* ImpreciseObjectiveCTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImpreciseObjectiveCTests.m; sourceTree = ""; }; 3D9894F8213509C8006C16F6 /* DescriptionEdgeCasesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionEdgeCasesViewController.swift; sourceTree = ""; }; 3DA12A3122405B9E00EB3C33 /* DataTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTableViewController.swift; sourceTree = ""; }; 3DBAC28622406EBB00EF4D0A /* AccessibilityContainersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityContainersTests.swift; sourceTree = ""; }; @@ -294,6 +296,7 @@ isa = PBXGroup; children = ( 3D13DB512221124000066519 /* ObjectiveCTests.m */, + 3D9334932A8B2E520078A142 /* ImpreciseObjectiveCTests.m */, 3DBAC28622406EBB00EF4D0A /* AccessibilityContainersTests.swift */, 607FACEB1AFB9204008FA782 /* AccessibilityPropertiesTests.swift */, 3D39BFAF2239BC42009C3EF4 /* ActivationPointTests.swift */, @@ -637,6 +640,7 @@ 3D220A2B252AF72900359C1E /* AccessibleContainerView.swift in Sources */, 1635CE4E251EAC6700907101 /* SnapshotTestingTests.swift in Sources */, 3DBEAA5D2223C0CE00FAE61D /* SwitchControlsTests.swift in Sources */, + 3D9334942A8B2E520078A142 /* ImpreciseObjectiveCTests.m in Sources */, 3DF46500220D5FB00048D446 /* ElementSelectionTests.swift in Sources */, 3DBAC28722406EBB00EF4D0A /* AccessibilityContainersTests.swift in Sources */, 3DC488392212B40C006D1E15 /* ModalTests.swift in Sources */, diff --git a/Example/SnapshotTests/ImpreciseObjectiveCTests.m b/Example/SnapshotTests/ImpreciseObjectiveCTests.m new file mode 100644 index 00000000..ed56084d --- /dev/null +++ b/Example/SnapshotTests/ImpreciseObjectiveCTests.m @@ -0,0 +1,92 @@ +// +// Copyright 2023 Block Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import AccessibilitySnapshot; +@import FBSnapshotTestCase; +@import XCTest; + + +@interface ImpreciseObjectiveCTests : FBSnapshotTestCase + +@end + + +@implementation ImpreciseObjectiveCTests + +- (void)setUp; +{ + [super setUp]; + + self.fileNameOptions = FBSnapshotTestCaseFileNameIncludeOptionOS | FBSnapshotTestCaseFileNameIncludeOptionScreenSize | FBSnapshotTestCaseFileNameIncludeOptionScreenScale; +} + +- (void)testSimpleView; +{ + UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 50)]; + + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(25, 10, 150, 30)]; + label.text = @"Objective-C Snapshot"; + label.textColor = [UIColor redColor]; + label.font = [UIFont systemFontOfSize:12]; + label.textAlignment = NSTextAlignmentCenter; + [view addSubview:label]; + + SnapshotImpreciseVerifyAccessibility(view, nil, 0, 0); +} + +- (void)testSimpleViewWithIdentifier; +{ + UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 50)]; + + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(25, 10, 150, 30)]; + label.text = @"Objective-C Snapshot"; + label.textColor = [UIColor redColor]; + label.font = [UIFont systemFontOfSize:12]; + label.textAlignment = NSTextAlignmentCenter; + [view addSubview:label]; + + SnapshotImpreciseVerifyAccessibility(view, @"identifier", 0, 0); +} + +- (void)testSimpleViewWithActivationPointAlways; +{ + UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 50)]; + + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(25, 10, 150, 30)]; + label.text = @"Objective-C Snapshot"; + label.textColor = [UIColor redColor]; + label.font = [UIFont systemFontOfSize:12]; + label.textAlignment = NSTextAlignmentCenter; + [view addSubview:label]; + + SnapshotImpreciseVerifyAccessibilityWithOptions(view, nil, YES, YES, 0, 0); +} + +- (void)testViewWithInvertedColors; +{ + UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + [view setBackgroundColor:[UIColor redColor]]; + + UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(25, 25, 50, 50)]; + [subview setBackgroundColor:[UIColor greenColor]]; + + [subview setAccessibilityIgnoresInvertColors:YES]; + [view addSubview:subview]; + + SnapshotImpreciseVerifyWithInvertedColors(view, nil, 0, 0); +} + +@end diff --git a/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithActivationPointAlways_13_7_375x812@3x.png b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithActivationPointAlways_13_7_375x812@3x.png new file mode 100644 index 00000000..31861f2d Binary files /dev/null and b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithActivationPointAlways_13_7_375x812@3x.png differ diff --git a/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithActivationPointAlways_14_5_390x844@3x.png b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithActivationPointAlways_14_5_390x844@3x.png new file mode 100644 index 00000000..ee084019 Binary files /dev/null and b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithActivationPointAlways_14_5_390x844@3x.png differ diff --git a/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithIdentifier_identifier_13_7_375x812@3x.png b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithIdentifier_identifier_13_7_375x812@3x.png new file mode 100644 index 00000000..a3684839 Binary files /dev/null and b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithIdentifier_identifier_13_7_375x812@3x.png differ diff --git a/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithIdentifier_identifier_14_5_390x844@3x.png b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithIdentifier_identifier_14_5_390x844@3x.png new file mode 100644 index 00000000..a38acfa9 Binary files /dev/null and b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleViewWithIdentifier_identifier_14_5_390x844@3x.png differ diff --git a/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleView_13_7_375x812@3x.png b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleView_13_7_375x812@3x.png new file mode 100644 index 00000000..a3684839 Binary files /dev/null and b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleView_13_7_375x812@3x.png differ diff --git a/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleView_14_5_390x844@3x.png b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleView_14_5_390x844@3x.png new file mode 100644 index 00000000..a38acfa9 Binary files /dev/null and b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testSimpleView_14_5_390x844@3x.png differ diff --git a/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testViewWithInvertedColors_13_7_375x812@3x.png b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testViewWithInvertedColors_13_7_375x812@3x.png new file mode 100644 index 00000000..f7d5b9fc Binary files /dev/null and b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testViewWithInvertedColors_13_7_375x812@3x.png differ diff --git a/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testViewWithInvertedColors_14_5_390x844@3x.png b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testViewWithInvertedColors_14_5_390x844@3x.png new file mode 100644 index 00000000..f7d5b9fc Binary files /dev/null and b/Example/SnapshotTests/ReferenceImages/_64/ImpreciseObjectiveCTests/testViewWithInvertedColors_14_5_390x844@3x.png differ diff --git a/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/ObjC/include/FBSnapshotTestCase_Accessibility.h b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/ObjC/include/FBSnapshotTestCase_Accessibility.h index 5fc42492..007f5999 100644 --- a/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/ObjC/include/FBSnapshotTestCase_Accessibility.h +++ b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/ObjC/include/FBSnapshotTestCase_Accessibility.h @@ -1,5 +1,5 @@ // -// Copyright 2019 Square Inc. +// Copyright 2023 Block Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ {\ _Pragma("clang diagnostic push")\ _Pragma("clang diagnostic ignored \"-Wundeclared-selector\"")\ - SEL selector = @selector(snapshotVerifyAccessibility:identifier:);\ + SEL selector = @selector(snapshotVerifyAccessibility:identifier:perPixelTolerance:overallTolerance:);\ _Pragma("clang diagnostic pop")\ - typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *);\ + typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *, CGFloat, CGFloat);\ SnapshotMethod snapshotVerifyAccessibility = (SnapshotMethod)[self methodForSelector:selector];\ - NSString *errorDescription = snapshotVerifyAccessibility(self, selector, view__, identifier__ ?: @"");\ + NSString *errorDescription = snapshotVerifyAccessibility(self, selector, view__, identifier__ ?: @"", 0, 0);\ if (errorDescription == nil) {\ XCTAssertTrue(YES);\ } else {\ @@ -34,11 +34,11 @@ {\ _Pragma("clang diagnostic push")\ _Pragma("clang diagnostic ignored \"-Wundeclared-selector\"")\ - SEL selector = @selector(snapshotVerifyAccessibility:identifier:showActivationPoints:useMonochromeSnapshot:);\ + SEL selector = @selector(snapshotVerifyAccessibility:identifier:showActivationPoints:useMonochromeSnapshot:perPixelTolerance:overallTolerance:);\ _Pragma("clang diagnostic pop")\ - typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *, BOOL, BOOL);\ + typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *, BOOL, BOOL, CGFloat, CGFloat);\ SnapshotMethod snapshotVerifyAccessibility = (SnapshotMethod)[self methodForSelector:selector];\ - NSString *errorDescription = snapshotVerifyAccessibility(self, selector, view__, identifier__ ?: @"", showActivationPoints__, useMonochromeSnapshot__);\ + NSString *errorDescription = snapshotVerifyAccessibility(self, selector, view__, identifier__ ?: @"", showActivationPoints__, useMonochromeSnapshot__, 0, 0);\ if (errorDescription == nil) {\ XCTAssertTrue(YES);\ } else {\ @@ -50,11 +50,11 @@ {\ _Pragma("clang diagnostic push")\ _Pragma("clang diagnostic ignored \"-Wundeclared-selector\"")\ - SEL selector = @selector(snapshotVerifyWithInvertedColors:identifier:);\ + SEL selector = @selector(snapshotVerifyWithInvertedColors:identifier:perPixelTolerance:overallTolerance:);\ _Pragma("clang diagnostic pop")\ - typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *);\ + typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *, CGFloat, CGFloat);\ SnapshotMethod snapshotVerifyWithInvertedColors = (SnapshotMethod)[self methodForSelector:selector];\ - NSString *errorDescription = snapshotVerifyWithInvertedColors(self, selector, view__, identifier__ ?: @"");\ + NSString *errorDescription = snapshotVerifyWithInvertedColors(self, selector, view__, identifier__ ?: @"", 0, 0);\ if (errorDescription == nil) {\ XCTAssertTrue(YES);\ } else {\ diff --git a/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/ObjC/include/FBSnapshotTestCase_ImpreciseAccessibility.h b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/ObjC/include/FBSnapshotTestCase_ImpreciseAccessibility.h new file mode 100644 index 00000000..eedb2372 --- /dev/null +++ b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/ObjC/include/FBSnapshotTestCase_ImpreciseAccessibility.h @@ -0,0 +1,63 @@ +// +// Copyright 2023 Block Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#define SnapshotImpreciseVerifyAccessibility(view__, identifier__, perPixelTolerance__, overallTolerance__)\ + {\ + _Pragma("clang diagnostic push")\ + _Pragma("clang diagnostic ignored \"-Wundeclared-selector\"")\ + SEL selector = @selector(snapshotVerifyAccessibility:identifier:perPixelTolerance:overallTolerance:);\ + _Pragma("clang diagnostic pop")\ + typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *, CGFloat, CGFloat);\ + SnapshotMethod snapshotVerifyAccessibility = (SnapshotMethod)[self methodForSelector:selector];\ + NSString *errorDescription = snapshotVerifyAccessibility(self, selector, view__, identifier__ ?: @"", perPixelTolerance__, overallTolerance__);\ + if (errorDescription == nil) {\ + XCTAssertTrue(YES);\ + } else {\ + XCTFail("%@", errorDescription);\ + }\ + } + +#define SnapshotImpreciseVerifyAccessibilityWithOptions(view__, identifier__, showActivationPoints__, useMonochromeSnapshot__, perPixelTolerance__, overallTolerance__)\ + {\ + _Pragma("clang diagnostic push")\ + _Pragma("clang diagnostic ignored \"-Wundeclared-selector\"")\ + SEL selector = @selector(snapshotVerifyAccessibility:identifier:showActivationPoints:useMonochromeSnapshot:perPixelTolerance:overallTolerance:);\ + _Pragma("clang diagnostic pop")\ + typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *, BOOL, BOOL, CGFloat, CGFloat);\ + SnapshotMethod snapshotVerifyAccessibility = (SnapshotMethod)[self methodForSelector:selector];\ + NSString *errorDescription = snapshotVerifyAccessibility(self, selector, view__, identifier__ ?: @"", showActivationPoints__, useMonochromeSnapshot__, perPixelTolerance__, overallTolerance__);\ + if (errorDescription == nil) {\ + XCTAssertTrue(YES);\ + } else {\ + XCTFail("%@", errorDescription);\ + }\ + } + +#define SnapshotImpreciseVerifyWithInvertedColors(view__, identifier__, perPixelTolerance__, overallTolerance__)\ + {\ + _Pragma("clang diagnostic push")\ + _Pragma("clang diagnostic ignored \"-Wundeclared-selector\"")\ + SEL selector = @selector(snapshotVerifyWithInvertedColors:identifier:perPixelTolerance:overallTolerance:);\ + _Pragma("clang diagnostic pop")\ + typedef NSString * (*SnapshotMethod)(id, SEL, UIView *, NSString *, CGFloat, CGFloat);\ + SnapshotMethod snapshotVerifyWithInvertedColors = (SnapshotMethod)[self methodForSelector:selector];\ + NSString *errorDescription = snapshotVerifyWithInvertedColors(self, selector, view__, identifier__ ?: @"", perPixelTolerance__, overallTolerance__);\ + if (errorDescription == nil) {\ + XCTAssertTrue(YES);\ + } else {\ + XCTFail("%@", errorDescription);\ + }\ + } diff --git a/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+Accessibility.swift b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+Accessibility.swift index 5758337b..bf748721 100644 --- a/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+Accessibility.swift +++ b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+Accessibility.swift @@ -1,5 +1,5 @@ // -// Copyright 2019 Square Inc. +// Copyright 2023 Block Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+ImpreciseAccessibility.swift b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+ImpreciseAccessibility.swift new file mode 100644 index 00000000..79684a10 --- /dev/null +++ b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+ImpreciseAccessibility.swift @@ -0,0 +1,179 @@ +// +// Copyright 2023 Block Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +#if SWIFT_PACKAGE +import AccessibilitySnapshotCore +import AccessibilitySnapshotCore_ObjC +import iOSSnapshotTestCase +#else +import FBSnapshotTestCase +#endif + +extension FBSnapshotTestCase { + + /// Snapshots the `view` with colored overlays of each accessibility element it contains, as well as an + /// approximation of the description that VoiceOver will read for each element. + /// + /// When `recordMode` is true, records a snapshot of the view. When `recordMode` is false, performs a comparison + /// with the existing snapshot. + /// + /// - Note: This method will modify the view hierarchy in order to snapshot the view. It will attempt to restore the + /// hierarchy to its original state as much as possible, but is not guaranteed to be without side effects (for + /// example if something observes changes in the view hierarchy). + /// + /// - Warning: Using a `perPixelTolerance` or `overallTolerance` greater than `0` may result in allowing regressions + /// through. Prefer using `perPixelTolerance` over `overallTolerance` where possible, and only raise the tolerances + /// to the extent needed to allow your tests to pass across multiple machines. + /// + /// - parameter view: The view that will be snapshotted. + /// - parameter identifier: An optional identifier included in the snapshot name, for use when there are multiple + /// snapshot tests in a given test method. Defaults to no identifier. + /// - parameter showActivationPoints: When to show indicators for elements' accessibility activation points. + /// Defaults to showing activation points only when they are different than the default activation point for that + /// element. + /// - parameter useMonochromeSnapshot: Whether or not the snapshot of the `view` should be monochrome. Using a + /// monochrome snapshot makes it more clear where the highlighted elements are, but may make it difficult to + /// read certain views. Defaults to `true`. + /// - parameter markerColors: An array of colors to use for the highlighted regions. These colors will be used in + /// order, repeating through the array as necessary. + /// - parameter suffixes: NSOrderedSet object containing strings that are appended to the reference images + /// directory. Defaults to `FBSnapshotTestCaseDefaultSuffixes()`. + /// - parameter perPixelTolerance: The amount the RGBA components of a pixel can differ for the pixel to still be + /// considered "unchanged". Value must be in the range `[0,1]`, where `0` means no difference allowed and `1` means + /// any two colors are considered identical. + /// - parameter overallTolerance: The portion of pixels that are allowed to have changed (as defined by the + /// per-pixel tolerance) for the image to still considered "unchanged" overall. Value must be in the range `[0,1]`, + /// where `0` means no pixels may change and `1` means all pixels may change. + /// - parameter file: The file in which the test result should be attributed. + /// - parameter line: The line in which the test result should be attributed. + public func SnapshotImpreciseVerifyAccessibility( + _ view: UIView, + identifier: String = "", + showActivationPoints activationPointDisplayMode: ActivationPointDisplayMode = .whenOverridden, + useMonochromeSnapshot: Bool = true, + markerColors: [UIColor] = [], + suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), + perPixelTolerance: CGFloat = 0, + overallTolerance: CGFloat = 0, + file: StaticString = #file, + line: UInt = #line + ) { + guard isRunningInHostApplication else { + XCTFail(ErrorMessageFactory.errorMessageForMissingHostApplication, file: file, line: line) + return + } + + let containerView = AccessibilitySnapshotView( + containedView: view, + viewRenderingMode: (usesDrawViewHierarchyInRect ? .drawHierarchyInRect : .renderLayerInContext), + markerColors: markerColors, + activationPointDisplayMode: activationPointDisplayMode + ) + + let window = UIWindow(frame: UIScreen.main.bounds) + window.makeKeyAndVisible() + containerView.center = window.center + window.addSubview(containerView) + + do { + try containerView.parseAccessibility(useMonochromeSnapshot: useMonochromeSnapshot) + } catch { + XCTFail(ErrorMessageFactory.errorMessageForAccessibilityParsingError(error), file: file, line: line) + return + } + containerView.sizeToFit() + + FBSnapshotVerifyView( + containerView, + identifier: identifier, + suffixes: suffixes, + perPixelTolerance: perPixelTolerance, + overallTolerance: overallTolerance, + file: file, + line: line + ) + } + + /// Snapshots the `view` simulating the way it will appear with Smart Invert Colors enabled. + /// + /// When `recordMode` is true, records a snapshot of the view. When `recordMode` is false, performs a comparison with the + /// existing snapshot. + /// + /// - Warning: Using a `perPixelTolerance` or `overallTolerance` greater than `0` may result in allowing regressions + /// through. Prefer using `perPixelTolerance` over `overallTolerance` where possible, and only raise the tolerances + /// to the extent needed to allow your tests to pass across multiple machines. + /// + /// - parameter view: The view that will be snapshotted. + /// - parameter identifier: An optional identifier included in the snapshot name, for use when there are multiple snapshot tests + /// in a given test method. Defaults to no identifier. + /// - parameter suffixes: NSOrderedSet object containing strings that are appended to the reference images directory. + /// Defaults to `FBSnapshotTestCaseDefaultSuffixes()`. + /// - parameter perPixelTolerance: The amount the RGBA components of a pixel can differ for the pixel to still be + /// considered "unchanged". Value must be in the range `[0,1]`, where `0` means no difference allowed and `1` means + /// any two colors are considered identical. + /// - parameter overallTolerance: The portion of pixels that are allowed to have changed (as defined by the + /// per-pixel tolerance) for the image to still considered "unchanged" overall. Value must be in the range `[0,1]`, + /// where `0` means no pixels may change and `1` means all pixels may change. + /// - parameter file: The file in which the test result should be attributed. + /// - parameter line: The line in which the test result should be attributed. + public func SnapshotImpreciseVerifyWithInvertedColors( + _ view: UIView, + identifier: String = "", + suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), + perPixelTolerance: CGFloat = 0, + overallTolerance: CGFloat = 0, + file: StaticString = #file, + line: UInt = #line + ) { + func postNotification() { + NotificationCenter.default.post( + name: UIAccessibility.invertColorsStatusDidChangeNotification, + object: nil, + userInfo: nil + ) + } + + let requiresWindow = (view.window == nil && !(view is UIWindow)) + if requiresWindow { + let window = UIApplication.shared.firstKeyWindow ?? UIWindow(frame: UIScreen.main.bounds) + window.addSubview(view) + } + + view.layoutIfNeeded() + + let statusUtility = UIAccessibilityStatusUtility() + statusUtility.mockInvertColorsStatus() + postNotification() + + let renderer = UIGraphicsImageRenderer(bounds: view.bounds) + let image = renderer.image { context in + view.drawHierarchyWithInvertedColors(in: view.bounds, using: context) + } + + let imageView = UIImageView(image: image) + FBSnapshotVerifyView(imageView, suffixes: suffixes, file: file, line: line) + + statusUtility.unmockStatuses() + postNotification() + + if requiresWindow { + view.removeFromSuperview() + } + } + +} diff --git a/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+ObjCSupport.swift b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+ObjCSupport.swift index fd9b2191..c749441d 100644 --- a/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+ObjCSupport.swift +++ b/Sources/AccessibilitySnapshot/iOSSnapshotTestCase/Swift/FBSnapshotTestCase+ObjCSupport.swift @@ -24,36 +24,50 @@ import FBSnapshotTestCase extension FBSnapshotTestCase { - @objc(snapshotVerifyAccessibility:identifier:) - private func 🚫objc_snapshotVerifyAccessibility(_ view: UIView, identifier: String) -> String? { + @objc(snapshotVerifyAccessibility:identifier:perPixelTolerance:overallTolerance:) + private func 🚫objc_snapshotVerifyAccessibility( + _ view: UIView, + identifier: String, + perPixelTolerance: CGFloat, + overallTolerance: CGFloat + ) -> String? { return snapshotVerifyAccessibility( view, identifier: identifier, activationPointDisplayMode: .whenOverridden, - useMonochromeSnapshot: true + useMonochromeSnapshot: true, + perPixelTolerance: perPixelTolerance, + overallTolerance: overallTolerance ) } - @objc(snapshotVerifyAccessibility:identifier:showActivationPoints:useMonochromeSnapshot:) + @objc(snapshotVerifyAccessibility:identifier:showActivationPoints:useMonochromeSnapshot:perPixelTolerance:overallTolerance:) private func 🚫objc_snapshotVerifyAccessibility( _ view: UIView, identifier: String, showActivationPoints: Bool, - useMonochromeSnapshot: Bool + useMonochromeSnapshot: Bool, + perPixelTolerance: CGFloat, + overallTolerance: CGFloat ) -> String? { return snapshotVerifyAccessibility( view, identifier: identifier, activationPointDisplayMode: showActivationPoints ? .always : .never, - useMonochromeSnapshot: useMonochromeSnapshot + useMonochromeSnapshot: useMonochromeSnapshot, + perPixelTolerance: perPixelTolerance, + overallTolerance: overallTolerance ) } - private func snapshotVerifyAccessibility( + @nonobjc + internal func snapshotVerifyAccessibility( _ view: UIView, identifier: String, activationPointDisplayMode: ActivationPointDisplayMode, - useMonochromeSnapshot: Bool + useMonochromeSnapshot: Bool, + perPixelTolerance: CGFloat, + overallTolerance: CGFloat ) -> String? { guard isRunningInHostApplication else { return ErrorMessageFactory.errorMessageForMissingHostApplication @@ -82,15 +96,20 @@ extension FBSnapshotTestCase { containerView, identifier: identifier, suffixes: FBSnapshotTestCaseDefaultSuffixes(), - perPixelTolerance: 0, - overallTolerance: 0, + perPixelTolerance: perPixelTolerance, + overallTolerance: overallTolerance, defaultReferenceDirectory: FB_REFERENCE_IMAGE_DIR, defaultImageDiffDirectory: IMAGE_DIFF_DIR ) } - @objc(snapshotVerifyWithInvertedColors:identifier:) - private func snapshotVerifyWithInvertedColors(_ view: UIView, identifier: String) -> String? { + @objc(snapshotVerifyWithInvertedColors:identifier:perPixelTolerance:overallTolerance:) + private func snapshotVerifyWithInvertedColors( + _ view: UIView, + identifier: String, + perPixelTolerance: CGFloat, + overallTolerance: CGFloat + ) -> String? { func postNotification() { NotificationCenter.default.post( name: UIAccessibility.invertColorsStatusDidChangeNotification, @@ -121,8 +140,8 @@ extension FBSnapshotTestCase { imageView, identifier: identifier, suffixes: FBSnapshotTestCaseDefaultSuffixes(), - perPixelTolerance: 0, - overallTolerance: 0, + perPixelTolerance: perPixelTolerance, + overallTolerance: overallTolerance, defaultReferenceDirectory: FB_REFERENCE_IMAGE_DIR, defaultImageDiffDirectory: IMAGE_DIFF_DIR )