diff --git a/Classes/GTXAccessibilityTree.m b/Classes/GTXAccessibilityTree.m index c86ff1f..483d1c9 100644 --- a/Classes/GTXAccessibilityTree.m +++ b/Classes/GTXAccessibilityTree.m @@ -23,7 +23,7 @@ * UITextEffectsWindow which reports 9223372036854775807 possibly due to internal type conversions * with -1, we use this bounds value to detect that case.. */ -const NSInteger kAccessibilityChildrenUpperBound = 50000; +static const NSInteger kAccessibilityChildrenUpperBound = 50000; @implementation GTXAccessibilityTree { // A queue of elements to be visited. @@ -89,6 +89,10 @@ - (id)nextObject { accessibilityElementsSet, @([nextInQueue accessibilityElementCount]), accessibilityElementsFromIndicesSet); + + // Ensure accessibilityElements* are marked as used even if NSAssert is removed. + (void)accessibilityElementsSet; + (void)accessibilityElementsFromIndicesSet; } else { // Set accessibilityElements to whichever is non nil or leave it as is. axElements = axElementsFromIndices ? axElementsFromIndices : axElements; diff --git a/Classes/GTXBlacklistBlock.h b/Classes/GTXBlacklistBlock.h new file mode 100644 index 0000000..1e073cd --- /dev/null +++ b/Classes/GTXBlacklistBlock.h @@ -0,0 +1,51 @@ +// +// Copyright 2018 Google 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 + +#import "GTXBlacklisting.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + A matcher block that determines if the given element must be ignored for the given check. + + @param element Element to be looked up if it needs to be ignored. + @param checkName Name of the check for which element must be looked up. + @return @c YES if element needs to be ignored for the given check @c NO other wise. + */ +typedef BOOL(^GTXIgnoreElementMatcher)(id element, NSString *checkName); + +@interface GTXBlacklistBlock: NSObject + +/** + * GTXBlacklistBlock::init is disabled, instead use GTXBlacklistBlock::blacklistWithBlock: + * method to create GTXBlacklists. + */ +- (instancetype)init __attribute__((unavailable("Use blacklistWithBlock: instead."))); + +/** + * Creates an GTXBlacklist with the given @c block that determines if an element should be ignored. + * + * @param block A block that determines if an element should be ignored. + * + * @return A GTXBlacklist object. + */ ++ (id)blacklistWithBlock:(GTXIgnoreElementMatcher)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTXBlacklistBlock.m b/Classes/GTXBlacklistBlock.m new file mode 100644 index 0000000..3c16499 --- /dev/null +++ b/Classes/GTXBlacklistBlock.m @@ -0,0 +1,47 @@ +// +// Copyright 2018 Google 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 "GTXBlacklistBlock.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation GTXBlacklistBlock { + GTXIgnoreElementMatcher _block; +} + ++ (id)blacklistWithBlock:(GTXIgnoreElementMatcher)block { + return [[GTXBlacklistBlock alloc] initWithBlock:block]; +} + +- (instancetype)initWithBlock:(GTXIgnoreElementMatcher)block { + NSParameterAssert(block); + + self = [super init]; + if (self) { + _block = block; + } + return self; +} + +#pragma mark - GTXBlacklisting + +- (BOOL)shouldIgnoreElement:(id)element forCheckNamed:(NSString *)check { + return _block(element, check); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTXBlacklistFactory.h b/Classes/GTXBlacklistFactory.h new file mode 100644 index 0000000..0fe3a83 --- /dev/null +++ b/Classes/GTXBlacklistFactory.h @@ -0,0 +1,61 @@ +// +// Copyright 2018 Google 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 + +#import "GTXBlacklisting.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Factory for GTXBlacklisting objects for blacklisting elements. + */ +@interface GTXBlacklistFactory : NSObject + +- (instancetype)init NS_UNAVAILABLE; +/** + * Returns a GTXBlacklisting object that ignores all elements that are instances of + * a given @c elementClassName. + * + * @param elementClassName The name of the class that should be blacklisted. + * @return A GTXBlacklisting object that ignores all elements of the given class. + */ ++ (id)blacklistWithClassName:(NSString *)elementClassName; +/** + * Returns a GTXBlacklisting object that ignores all elements that are instances of + * a given @c elementClassName, but only for GTXChecking objects with a given @c checkName. + * + * @param elementClassName The name of the class that should be blacklisted. + * @param skipCheckName The name of the check that should ignore elements of the given class. + * @return A GTXBlacklisting object that ignores all elements of the given class when running the + * given check. + */ ++ (id)blacklistWithClassName:(NSString *)elementClassName + checkName:(NSString *)skipCheckName; +/** + * Returns a GTXBlacklisting object that ignores all elements with a given accessibility + * identifier, but only for GTXChecking objects with a given @c checkName. + * + * @param accessibilityId The accessibility identifier of the element that should be blacklisted. + * @param skipCheckName The name of the check that should ignore elements with the given + * accessibility identifier. + */ ++ (id)blacklistWithAccessibilityIdentifier:(NSString *)accessibilityId + checkName:(NSString *)skipCheckName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTXBlacklistFactory.m b/Classes/GTXBlacklistFactory.m new file mode 100644 index 0000000..5c4d2d8 --- /dev/null +++ b/Classes/GTXBlacklistFactory.m @@ -0,0 +1,58 @@ +// +// Copyright 2018 Google 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 "GTXBlacklistFactory.h" + +#import "GTXBlacklistBlock.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation GTXBlacklistFactory + ++ (id)blacklistWithClassName:(NSString *)elementClassName { + Class classObject = NSClassFromString(elementClassName); + NSAssert(classObject, @"Class named %@ does not exist!", elementClassName); + GTXIgnoreElementMatcher matcher = ^BOOL(id element, NSString *checkName) { + return [element isKindOfClass:classObject]; + }; + return [GTXBlacklistBlock blacklistWithBlock:matcher]; +} + ++ (id)blacklistWithClassName:(NSString *)elementClassName + checkName:(NSString *)skipCheckName { + NSParameterAssert(elementClassName); + Class classObject = NSClassFromString(elementClassName); + NSAssert(classObject, @"Class named %@ does not exist!", elementClassName); + GTXIgnoreElementMatcher matcher = ^BOOL(id element, NSString *checkName) { + return [element isKindOfClass:classObject] && [checkName isEqualToString:skipCheckName]; + }; + return [GTXBlacklistBlock blacklistWithBlock:matcher]; +} + ++ (id)blacklistWithAccessibilityIdentifier:(NSString *)accessibilityId + checkName:(NSString *)skipCheckName { + NSParameterAssert(accessibilityId); + NSParameterAssert(skipCheckName); + GTXIgnoreElementMatcher matcher = ^BOOL(id element, NSString *checkName) { + return [[element accessibilityIdentifier] isEqualToString:accessibilityId] && + [checkName isEqualToString:skipCheckName]; + }; + return [GTXBlacklistBlock blacklistWithBlock:matcher]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTXBlacklisting.h b/Classes/GTXBlacklisting.h new file mode 100644 index 0000000..401d86f --- /dev/null +++ b/Classes/GTXBlacklisting.h @@ -0,0 +1,38 @@ +// +// Copyright 2018 Google 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 + +NS_ASSUME_NONNULL_BEGIN + +/** + * Protocol for blacklisting certain types of UI elements in accessibility checks. + */ +@protocol GTXBlacklisting + +/** + * Determines if the given @c check should not be run on the given @c element. + * + * @param element The target element on which the GTX check is to be performed. + * @param[out] checkName The name of the check being run. + * + * @return @c YES if the check should NOT be performed on the given element, NO otherwise. + */ +- (BOOL)shouldIgnoreElement:(id)element forCheckNamed:(NSString *)checkName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTXChecksCollection.h b/Classes/GTXChecksCollection.h index 3f48a8a..9a4c331 100644 --- a/Classes/GTXChecksCollection.h +++ b/Classes/GTXChecksCollection.h @@ -42,7 +42,8 @@ typedef NS_ENUM(NSUInteger, GTXVersion) { * Organizes all checks provided by GTX, developers can use these as a starting point * for implementing their own checks. These checks are based on recommendations found in * "Accessibility Programming Guide for iOS" - @link https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Introduction/Introduction.html + @link + https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Introduction/Introduction.html // NOLINT * and on WCAG. */ @interface GTXChecksCollection : NSObject @@ -50,12 +51,12 @@ typedef NS_ENUM(NSUInteger, GTXVersion) { /** * @return An array of all supported GTXChecks for the given @c version. */ -+ (NSArray *)allChecksForVersion:(GTXVersion)version; ++ (NSArray> *)allChecksForVersion:(GTXVersion)version; /** * @return An array of all supported GTXChecks. */ -+ (NSArray *)allGTXChecks; ++ (NSArray> *)allGTXChecks; /** * @return a check that verifies that accessibility label is present on all accessibility elements. @@ -68,6 +69,12 @@ typedef NS_ENUM(NSUInteger, GTXVersion) { */ + (id)checkForAXLabelNotPunctuated; +/** + * @return a check that verifies that accessibility labels do not end in strings that VoiceOver + * announces via the element's accessibility traits. + */ ++ (id)checkForAXLabelNotRedundantWithTraits; + /** * @return a check that verifies that accessibility traits dont conflict with each other as * recommended by "Accessibility Programming Guide for iOS" (see class docs). diff --git a/Classes/GTXChecksCollection.m b/Classes/GTXChecksCollection.m index 47cf6a9..1d6379d 100644 --- a/Classes/GTXChecksCollection.m +++ b/Classes/GTXChecksCollection.m @@ -26,6 +26,8 @@ NSString *const kGTXCheckNameAccessibilityLabelPresent = @"Accessibility Label Present"; NSString *const kGTXCheckNameAccessibilityLabelNotPunctuated = @"Accessibility Label Not Punctuated"; +NSString *const kGTXCheckNameAccessibilityLabelIsNotRedundantWithTraits = + @"Accessibility Label is Not Redundant with Traits"; NSString *const kGTXCheckNameAccessibilityTraitsDontConflict = @"Accessibility Traits Don't Conflict"; NSString *const kGTXCheckNameMinimumTappableArea = @"Element has Minimum Tappable Area"; @@ -34,21 +36,23 @@ #pragma mark - Globals /** - * The minimum size (width or height) for a given element to be easily accessible. + * The minimum size (width or height) for a given element to be easily accessible. Based on + * Material design guidelines: + * https://material.io/design/layout/spacing-methods.html#touch-click-targets */ -static const float kMinSizeForAccessibleElements = 48.0; +static const float kGTXMinSizeForAccessibleElements = 48.0; /** * The minimum contrast ratio for any given text to be considered accessible. Note that smaller * text has even stricter requirement of 4.5:1. */ -static const float kMinContrastRatioForAccessibleText = 3.0; +static const float kGTXMinContrastRatioForAccessibleText = 3.0; #pragma mark - Implementations @implementation GTXChecksCollection -+ (NSArray *)allChecksForVersion:(GTXVersion)version { ++ (NSArray> *)allChecksForVersion:(GTXVersion)version { switch (version) { case GTXVersionLatest: return [self allGTXChecks]; case GTXVersionPreRelease: return [self allGTXChecks]; @@ -59,6 +63,7 @@ @implementation GTXChecksCollection + (NSArray> *)allGTXChecks { return @[[self checkForAXLabelPresent], [self checkForAXLabelNotPunctuated], + [self checkForAXLabelNotRedundantWithTraits], [self checkForAXTraitDontConflict], [self checkForMinimumTappableArea], [self checkForSufficientContrastRatio]]; @@ -141,6 +146,66 @@ @implementation GTXChecksCollection return check; } +// @todo Include all UIAccessibilityTraits that announce themselves (image, search +// field, etc.), and find a more robust way to determine if the label is redundant. Currently, the +// suffix is compared to a single string, which causes false positives when that string occurs +// elsewhere in the label or synonyms are used. Additionally, the hardcoded string only works in +// English. This method also needs to be updated for i18n. ++ (id)checkForAXLabelNotRedundantWithTraits { + id check = + [GTXCheckBlock GTXCheckWithName:kGTXCheckNameAccessibilityLabelIsNotRedundantWithTraits + block:^BOOL(id element, GTXErrorRefType errorOrNil) { + UIAccessibilityTraits elementAXTraits = [element accessibilityTraits]; + NSString *elementAXLabel = [element accessibilityLabel]; + NSDictionary const *redundantLabelsDictionary = + [self traitsToRedundantLabelsDictionary]; + NSMutableArray *redundantTextList = [[NSMutableArray alloc] init]; + NSMutableArray *redundantTraitNameList = [[NSMutableArray alloc] init]; + for (NSNumber *testTrait in redundantLabelsDictionary) { + NSString* redundantText = [redundantLabelsDictionary objectForKey:testTrait]; + UIAccessibilityTraits testUITrait = [testTrait unsignedLongLongValue]; + if ((BOOL)(elementAXTraits & testUITrait)) { + if ([GTXChecksCollection caseInsensitive:elementAXLabel hasSuffix:redundantText]) { + NSError *error; + NSString *stringValue = [self stringValueOfUIAccessibilityTraits:testUITrait + error:&error]; + if (error) { + if (errorOrNil) { + *errorOrNil = error; + } + return NO; + } + + [redundantTextList addObject:redundantText]; + [redundantTraitNameList addObject:stringValue]; + } + } + } + if ([redundantTraitNameList count] > 0) { + NSString* stringOfRedundantTextList = [redundantTextList componentsJoinedByString:@", "]; + NSString* stringOfRedundantTraitNameList = + [redundantTraitNameList componentsJoinedByString:@", "]; + NSString *description = [NSString stringWithFormat:@"Suggest removing '%@' from the " + @"accessibility label '%@'. The element " + @"already contains the accessibility " + @"trait(s) %@. VoiceOver announces those " + @"traits to the user, so putting " + @"redundant words in the label causes " + @"VoiceOver to repeat them.", + stringOfRedundantTextList, + elementAXLabel, + stringOfRedundantTraitNameList]; + [NSError gtx_logOrSetGTXCheckFailedError:errorOrNil + element:element + name:kGTXCheckNameAccessibilityLabelIsNotRedundantWithTraits + description:description]; + return NO; + } + return YES; + }]; + return check; +} + + (id)checkForAXTraitDontConflict { id check = [GTXCheckBlock GTXCheckWithName:kGTXCheckNameAccessibilityTraitsDontConflict @@ -200,8 +265,8 @@ @implementation GTXChecksCollection } if ([element respondsToSelector:@selector(accessibilityFrame)]) { CGRect frame = [element accessibilityFrame]; - BOOL hasSmallWidth = CGRectGetWidth(frame) < kMinSizeForAccessibleElements; - BOOL hasSmallHeight = CGRectGetHeight(frame) < kMinSizeForAccessibleElements; + BOOL hasSmallWidth = CGRectGetWidth(frame) < kGTXMinSizeForAccessibleElements; + BOOL hasSmallHeight = CGRectGetHeight(frame) < kGTXMinSizeForAccessibleElements; if (hasSmallWidth || hasSmallHeight) { NSString *dimensionsToBeFixed; // Append a suggestion to the error description. @@ -219,9 +284,9 @@ @implementation GTXChecksCollection [NSString stringWithFormat:@"Suggest increasing element's %@ to at least %d for " @"a suggested tappable area of at least %dX%d", dimensionsToBeFixed, - (int)kMinSizeForAccessibleElements, - (int)kMinSizeForAccessibleElements, - (int)kMinSizeForAccessibleElements]; + (int)kGTXMinSizeForAccessibleElements, + (int)kGTXMinSizeForAccessibleElements, + (int)kGTXMinSizeForAccessibleElements]; [NSError gtx_logOrSetGTXCheckFailedError:errorOrNil element:element @@ -244,13 +309,13 @@ @implementation GTXChecksCollection } CGFloat ratio = [GTXImageAndColorUtils contrastRatioOfUILabel:element]; BOOL hasSufficientContrast = - (ratio >= kMinContrastRatioForAccessibleText - kContrastRatioAccuracy); + (ratio >= kGTXMinContrastRatioForAccessibleText - kGTXContrastRatioAccuracy); if (!hasSufficientContrast) { NSString *description = [NSString stringWithFormat:@"Suggest increasing this element's contrast ratio to at " "least " @"%.5f the actual ratio was computed as %.5f", - (float)kMinContrastRatioForAccessibleText, (float)ratio]; + (float)kGTXMinContrastRatioForAccessibleText, (float)ratio]; [NSError gtx_logOrSetGTXCheckFailedError:errorOrNil element:element name:kGTXCheckNameMinimumContrastRatio @@ -385,6 +450,17 @@ + (NSString *)trimmedStringFromString:(NSString *)string { return [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; } +/** + * @return The UIAccessibilityTraits to redundant NSString accessibility label mapping dictionary + * as type NSDictionary const *. + */ ++ (NSDictionary const *)traitsToRedundantLabelsDictionary { + return @{ + @(UIAccessibilityTraitButton): + @"button", + }; +} + /** * @return @c YES if @c element is tappable (for ex button) @c NO otherwise. */ @@ -419,4 +495,12 @@ + (BOOL)gtx_isTextDisplayingElement:(id)element { hasTextTrait); } +/** + * @return @c YES if the last characters of @c string equals @c suffix. Comparison is done case + * insensitively. + */ ++ (BOOL)caseInsensitive:(NSString*)string hasSuffix:(NSString*)suffix { + return [[string lowercaseString] hasSuffix:suffix]; +} + @end diff --git a/Classes/GTXImageAndColorUtils.h b/Classes/GTXImageAndColorUtils.h index 34ce3c4..5d2792c 100644 --- a/Classes/GTXImageAndColorUtils.h +++ b/Classes/GTXImageAndColorUtils.h @@ -16,7 +16,7 @@ #import -extern const CGFloat kContrastRatioAccuracy; +extern const CGFloat kGTXContrastRatioAccuracy; /** * Collection of accessibility related utils that use colors and/or images. diff --git a/Classes/GTXImageAndColorUtils.m b/Classes/GTXImageAndColorUtils.m index 8fd91ff..cd9bb9d 100644 --- a/Classes/GTXImageAndColorUtils.m +++ b/Classes/GTXImageAndColorUtils.m @@ -21,7 +21,7 @@ /** * Accuracy of the contrast ratios provided by the APIs in this class. */ -const CGFloat kContrastRatioAccuracy = 0.05f; +const CGFloat kGTXContrastRatioAccuracy = 0.05f; @implementation GTXImageAndColorUtils diff --git a/Classes/GTXPluginXCTestCase.m b/Classes/GTXPluginXCTestCase.m index a0b25bf..4f52fd8 100644 --- a/Classes/GTXPluginXCTestCase.m +++ b/Classes/GTXPluginXCTestCase.m @@ -16,8 +16,9 @@ #import "GTXPluginXCTestCase.h" -#import "GTXiLibCore.h" #import "GTXAssertions.h" +#import "GTXiLibCore.h" +#import "GTXTestEnvironment.h" #import @@ -44,6 +45,7 @@ + (void)installPlugin { [self gtx_swizzleInstanceMethod:@selector(invokeTest) withMethod:@selector(gtx_invokeTest) inClass:gXCTestCaseClass]; + [GTXTestEnvironment setupEnvironment]; }); } @@ -81,6 +83,7 @@ + (void)gtx_addInstanceMethod:(SEL)methodSelector typeEncoding); NSAssert(success, @"Failed to add %@ from %@ to %@", NSStringFromSelector(methodSelector), srcClass, destClass); + (void)success; // Ensures 'success' is marked as used even if NSAssert is removed. } /** diff --git a/Classes/GTXTestEnvironment.h b/Classes/GTXTestEnvironment.h new file mode 100644 index 0000000..bde20a5 --- /dev/null +++ b/Classes/GTXTestEnvironment.h @@ -0,0 +1,41 @@ +// +// Copyright 2018 Google 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 + +#import "GTXCommon.h" + +/** + * A class for setting up enviornment (devices, simulators) etc that GTX runs on. + */ +@interface GTXTestEnvironment : NSObject + +/** + * Sets up the enviornment for use by GTX. + * + * @param[out] errorOrNil A pointer to an NSError object to populate if there is an error, or nil + * if errors are not being handled. + * @return YES if the environment was set up successfully, NO otherwise, If NO and @c errorOrNil + * is not nil, @c errorOrNil is populated with a description of the error. + */ ++ (BOOL)setupEnvironmentWithError:(GTXErrorRefType)errorOrNil; +/** + * Sets up the enviornment for use by GTX. If the environment cannot be setup, then an exception + * is raised. + */ ++ (void)setupEnvironment; + +@end diff --git a/Classes/GTXTestEnvironment.m b/Classes/GTXTestEnvironment.m new file mode 100644 index 0000000..7af591f --- /dev/null +++ b/Classes/GTXTestEnvironment.m @@ -0,0 +1,222 @@ +// +// Copyright 2018 Google 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 "GTXTestEnvironment.h" + +#import "GTXLogging.h" +#import "NSError+GTXAdditions.h" + +#include + +#pragma mark - Exposed Interfaces + +/** + * An Exposed internal class used for enabling accessibility on simulators. + */ +@interface AXBackBoardServer + +/** + * Returns current backboard server instance. + */ ++ (instancetype)server; + +/** + * Sets the given preference and posts the given notification. + * + * @param key Name of the key whose value is to be set. + * @param value The value to be set to. + * @param notification The notification to be raised. + */ +- (void)setAccessibilityPreferenceAsMobile:(CFStringRef)key + value:(CFBooleanRef)value + notification:(CFStringRef)notification; + +@end + +#pragma mark - Globals + +/** + * Path to accessibility utils framework on the simulator. + */ +static NSString *const kPathToAXUtils = + @"/System/Library/PrivateFrameworks/AccessibilityUtilities.framework/AccessibilityUtilities"; + +/** + * Class name of the private class: XCAXClient_iOS class. + */ +static NSString *const kXCAXClientClassName = @"XCAXClient_iOS"; + +#pragma mark - Implementations + +@implementation GTXTestEnvironment + ++ (BOOL)setupEnvironmentWithError:(GTXErrorRefType)errorOrNil { + static dispatch_once_t onceToken; + __block BOOL setupSuccessful = YES; + dispatch_once(&onceToken, ^{ +#if TARGET_OS_SIMULATOR + setupSuccessful = [self _enableAccessibilityForSimulatorWithError:errorOrNil]; +#else + setupSuccessful = [self _enableAccessibilityOnDeviceWithError:errorOrNil]; +#endif + }); + return setupSuccessful; +} + ++ (void)setupEnvironment { + NSAssert([GTXTestEnvironment setupEnvironmentWithError:nil], + @"Test environment could not be set up"); +} + +/** + * Enables accessibility to allow using accessibility properties on simulators. + * + * @param[out] errorOrNil A pointer to an error object to return information on failure, or nil. + * @return YES if accessibility was enabled, NO if there was an error. + */ ++ (BOOL)_enableAccessibilityForSimulatorWithError:(GTXErrorRefType)errorOrNil { + // Set the preferences that turn on Accessibility. + BOOL setSuccessful = YES; + setSuccessful = + [self _setAccessibilityPreference:(CFStringRef) @"ApplicationAccessibilityEnabled" + value:kCFBooleanTrue + notification:(CFStringRef) @"com.apple.accessibility.cache.app.ax" + error:errorOrNil]; + if (!setSuccessful) { + return NO; + } + setSuccessful = + [self _setAccessibilityPreference:(CFStringRef) @"AccessibilityEnabled" + value:kCFBooleanTrue + notification:(CFStringRef) @"com.apple.accessibility.cache.ax" + error:errorOrNil]; + return setSuccessful; +} + +/** + * Sets the given preference and posts the given notification using @c AXBackBoardServer. + * + * @param key Name of the key whose value is to be set. + * @param value The value to be set to. + * @param name The name of the notification to be raised. + * @param[out] errorOrNil A pointer to an error object to return information on failure, or nil. + * @return YES if the preference was set, NO if there was an error. + */ ++ (BOOL)_setAccessibilityPreference:(CFStringRef)key + value:(CFBooleanRef)value + notification:(CFStringRef)name + error:(GTXErrorRefType)errorOrNil { + __block BOOL setupSuccessful = YES; + static AXBackBoardServer *server; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + char const *const localPath = [kPathToAXUtils fileSystemRepresentation]; + void *handle = dlopen(localPath, RTLD_LOCAL); + if (!handle) { + NSString *description = + [NSString stringWithFormat:@"Could not load AccessibilityUtilities at %s", localPath]; + [NSError gtx_logOrSetError:errorOrNil + description:description + code:GTXCheckErrorCodeInvalidTestEnvironment + userInfo:nil]; + setupSuccessful = NO; + return; + } + (void)handle; // Ensures 'handle' is marked as used even if NSError is removed. + Class AXBackBoardServerClass = NSClassFromString(@"AXBackBoardServer"); + if (!AXBackBoardServerClass) { + [NSError gtx_logOrSetError:errorOrNil + description:@"AXBackBoardServer class not found" + code:GTXCheckErrorCodeInvalidTestEnvironment + userInfo:nil]; + setupSuccessful = NO; + return; + } + server = [AXBackBoardServerClass performSelector:@selector(server)]; + if (!server) { + [NSError gtx_logOrSetError:errorOrNil + description:@"Could not retrieve AXBackBoardServer object" + code:GTXCheckErrorCodeInvalidTestEnvironment + userInfo:nil]; + setupSuccessful = NO; + return; + } + }); + [server setAccessibilityPreferenceAsMobile:key + value:value + notification:name]; + return setupSuccessful; +} + +/** + * Enables accessibility to allow using accessibility properties on devices. + */ ++ (BOOL)_enableAccessibilityOnDeviceWithError:(GTXErrorRefType)errorOrNil { + Class XCAXClientClass = NSClassFromString(kXCAXClientClassName); + if (XCAXClientClass == nil) { + NSString *description = [NSString stringWithFormat:@"%@ class not found", kXCAXClientClassName]; + [NSError gtx_logOrSetError:errorOrNil + description:description + code:GTXCheckErrorCodeInvalidTestEnvironment + userInfo:nil]; + return NO; + } + id XCAXClient = [XCAXClientClass sharedClient]; + if (XCAXClient == nil) { + NSString *description = + [NSString stringWithFormat:@"%@ sharedClient doesn't exist", kXCAXClientClassName]; + [NSError gtx_logOrSetError:errorOrNil + description:description + code:GTXCheckErrorCodeInvalidTestEnvironment + userInfo:nil]; + return NO; + } + // The method may not be available on versions older than iOS 9.1 + if ([XCAXClient respondsToSelector:@selector(loadAccessibility:)]) { + typedef void (*MethodType)(id, SEL, void*); + SEL selector = @selector(loadAccessibility:); + static void *unused = 0; + MethodType method = (MethodType)[XCAXClient methodForSelector:selector]; + method(XCAXClient, selector, &unused); + } else { + [NSError gtx_logOrSetError:errorOrNil + description:@"Could not enable accessibility! iOS version must be >= 9.1" + code:GTXCheckErrorCodeInvalidTestEnvironment + userInfo:nil]; + return NO; + } + return YES; +} + +#pragma mark - unused methods + +/** + * Unused method only used for selector name. + */ +- (id)sharedClient { + NSAssert(NO, @"This method must not be invoked directly, its only used for exposing selector"); + return nil; +} + +/** + * Unused method only used for selector name. + */ +- (BOOL)loadAccessibility:(void **)unused { + NSAssert(NO, @"This method must not be invoked directly, its only used for exposing selector"); + return NO; +} + +@end diff --git a/Classes/GTXToolKit.h b/Classes/GTXToolKit.h index 6f20081..ac9ea49 100644 --- a/Classes/GTXToolKit.h +++ b/Classes/GTXToolKit.h @@ -16,8 +16,10 @@ #import -#import "GTXChecking.h" +#import "GTXBlacklistBlock.h" +#import "GTXBlacklisting.h" #import "GTXCheckBlock.h" +#import "GTXChecking.h" NS_ASSUME_NONNULL_BEGIN @@ -45,9 +47,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)registerCheck:(id)check; +/** + Registers the given blacklist to be executed on all elements this instance is used on. Registered + checks are not performed on blacklisted elements. + + @param blacklist The blacklist to be registered. + */ +- (void)registerBlacklist:(id)blacklist; + /** - Applies the registered checks on the given element while respecting ignored elements. + Applies the registered checks on the given element while respecting blacklisted elements. @param element element to be checked. @param errorOrNil Error object to be filled with error info on check failures. @@ -58,7 +68,7 @@ NS_ASSUME_NONNULL_BEGIN /** Applies the registered checks on all elements in the accessibility tree under the given root - elements while respecting ignored elements. + elements while respecting blacklisted elements. @param rootElements An array of root elements whose accessibility trees are to be checked. @param errorOrNil Error object to be filled with error info on check failures. @@ -67,21 +77,6 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)checkAllElementsFromRootElements:(NSArray *)rootElements error:(GTXErrorRefType)errorOrNil; -/** - Configures this instance to skip elements of the given class name from all checks. - - @param className The name of the class to be ignored. - */ -- (void)ignoreElementsOfClassNamed:(NSString *)className; - -/** - Configures this instance to skip elements of the given class name from a specific check. - - @param className The name of the class to be ignored. - @param skipCheckName The name of the check for which this element is to be ignored. - */ -- (void)ignoreElementsOfClassNamed:(NSString *)className forCheckNamed:(NSString *)skipCheckName; - @end NS_ASSUME_NONNULL_END diff --git a/Classes/GTXToolKit.m b/Classes/GTXToolKit.m index 414e552..6857114 100644 --- a/Classes/GTXToolKit.m +++ b/Classes/GTXToolKit.m @@ -18,29 +18,22 @@ #import "GTXAccessibilityTree.h" #import "GTXAnalytics.h" +#import "GTXBlacklistBlock.h" +#import "GTXBlacklistFactory.h" #import "NSError+GTXAdditions.h" -/** - A matcher block that determines if the given element must be ignored for the given check. - - @param element Element to be looked up if it needs to be ignored. - @param checkName Name of the check for which element must be looked up. - @return @c YES if element needs to be ignored for the given check @c NO other wise. - */ -typedef BOOL(^GTXIgnoreElementMatcher)(id element, NSString *checkName); - #pragma mark - Implementation @implementation GTXToolKit { NSMutableArray> *_checks; - NSMutableArray *_blackListMatchers; + NSMutableArray> *_blacklists; } - (instancetype)init { self = [super init]; if (self) { _checks = [[NSMutableArray alloc] init]; - _blackListMatchers = [[NSMutableArray alloc] init]; + _blacklists = [[NSMutableArray alloc] init]; } return self; } @@ -54,10 +47,15 @@ - (void)registerCheck:(id)check { for (id existingCheck in _checks) { NSAssert(![[existingCheck name] isEqualToString:[check name]], @"Check named %@ already exists!", [check name]); + (void)existingCheck; // Ensures 'existingCheck' is marked as used even if NSAssert is removed. } [_checks addObject:check]; } +- (void)registerBlacklist:(id)blacklist { + [_blacklists addObject:blacklist]; +} + - (BOOL)checkElement:(id)element error:(GTXErrorRefType)errorOrNil { return [self _checkElement:element analyticsEnabled:YES error:errorOrNil]; } @@ -125,29 +123,10 @@ - (BOOL)checkAllElementsFromRootElements:(NSArray *)rootElements return errors == nil; } -- (void)ignoreElementsOfClassNamed:(NSString *)className { - Class classObject = NSClassFromString(className); - NSAssert(classObject, @"Class named %@ does not exist!", className); - GTXIgnoreElementMatcher matcher = ^BOOL(id element, NSString *checkName) { - return [element isKindOfClass:classObject]; - }; - [_blackListMatchers addObject:matcher]; -} - -- (void)ignoreElementsOfClassNamed:(NSString *)className forCheckNamed:(NSString *)skipCheckName { - NSParameterAssert(skipCheckName); - Class classObject = NSClassFromString(className); - NSAssert(classObject, @"Class named %@ does not exist!", className); - GTXIgnoreElementMatcher matcher = ^BOOL(id element, NSString *checkName) { - return [element isKindOfClass:classObject] && [checkName isEqualToString:skipCheckName]; - }; - [_blackListMatchers addObject:matcher]; -} - #pragma mark - private /** - Applies the registered checks on the given element while respecting ignored elements. + Applies the registered checks on the given element while respecting blacklisted elements. @param element element to be checked. @param checkAnalyticsEnabled Boolean that indicates if analytics events are to be invoked. @@ -167,8 +146,8 @@ - (BOOL)_checkElement:(id)element NSMutableArray *failedCheckErrors; for (id checker in _checks) { BOOL shouldSkipThisCheck = NO; - for (GTXIgnoreElementMatcher matcher in _blackListMatchers) { - if (matcher(element, [checker name])) { + for (id blacklist in _blacklists) { + if ([blacklist shouldIgnoreElement:element forCheckNamed:[checker name]]) { shouldSkipThisCheck = YES; break; } @@ -186,7 +165,8 @@ - (BOOL)_checkElement:(id)element @"check implementation.", [checker name]]; error = [NSError errorWithDomain:kGTXErrorDomain code:GTXCheckErrorCodeAccessibilityCheckFailed - userInfo:@{ NSLocalizedDescriptionKey: errorDescription }]; + userInfo:@{ NSLocalizedDescriptionKey: errorDescription, + kGTXErrorFailingElementKey: element }]; } if (!failedCheckErrors) { failedCheckErrors = [[NSMutableArray alloc] init]; diff --git a/Classes/GTXiLib.h b/Classes/GTXiLib.h index 4a84dee..22964be 100644 --- a/Classes/GTXiLib.h +++ b/Classes/GTXiLib.h @@ -33,7 +33,7 @@ FOUNDATION_EXPORT const unsigned char GTXiLibVersionString[]; #import #import #import -#import +#import #import #import #import diff --git a/Classes/GTXiLibCore.h b/Classes/GTXiLibCore.h index 56ea83b..a601a22 100644 --- a/Classes/GTXiLibCore.h +++ b/Classes/GTXiLibCore.h @@ -16,10 +16,10 @@ #import "GTXAnalytics.h" #import "GTXAccessibilityTree.h" +#import "GTXBlacklisting.h" #import "GTXCheckBlock.h" #import "GTXChecksCollection.h" #import "GTXCommon.h" -#import "GTXElementBlacklist.h" #import "GTXErrorReporter.h" #import "GTXTestSuite.h" #import "NSError+GTXAdditions.h" @@ -78,7 +78,7 @@ typedef void(^GTXiLibFailureHandler)(NSError *error); */ + (void)installOnTestSuite:(GTXTestSuite *)suite checks:(NSArray> *)checks - elementBlacklists:(NSArray *)blacklists; + elementBlacklists:(NSArray> *)blacklists; /** Creates a check with the given name and block. @@ -90,24 +90,6 @@ typedef void(^GTXiLibFailureHandler)(NSError *error); */ + (id)checkWithName:(NSString *)name block:(GTXCheckHandlerBlock)block; -/** - Creates a blacklist matcher that skips elements of the given class from all checks. - - @param className The class name of the element. - @return The newly created blacklist matcher. - */ -+ (GTXElementBlacklist *)blacklistForElementsOfClassNamed:(NSString *)className; - -/** - Creates a blacklist matcher that skips elements of the given class from the given check. - - @param className The class name of the element. - @param skipCheckName The name of the check from which to skip the elements. - @return The newly created blacklist matcher. - */ -+ (GTXElementBlacklist *)blacklistForElementsOfClassNamed:(NSString *)className - forCheckNamed:(NSString *)skipCheckName; - /** The failure handler to be invoked when checks fail, by default if checks fail an Assertion is raised. diff --git a/Classes/GTXiLibCore.m b/Classes/GTXiLibCore.m index 68858c0..015b3fa 100644 --- a/Classes/GTXiLibCore.m +++ b/Classes/GTXiLibCore.m @@ -16,12 +16,12 @@ #import "GTXiLibCore.h" -#import "GTXToolKit.h" #import "GTXAssertions.h" #import "GTXChecking.h" #import "GTXChecksCollection.h" #import "GTXLogging.h" #import "GTXPluginXCTestCase.h" +#import "GTXToolKit.h" #import "NSError+GTXAdditions.h" #pragma mark - Global definitions. @@ -81,7 +81,7 @@ @implementation GTXiLib + (void)installOnTestSuite:(GTXTestSuite *)suite checks:(NSArray> *)checks - elementBlacklists:(NSArray *)blacklists { + elementBlacklists:(NSArray> *)blacklists { [GTXPluginXCTestCase installPlugin]; if (!gIntsallOptions) { gIntsallOptions = [[NSMutableArray alloc] init]; @@ -98,6 +98,7 @@ + (void)installOnTestSuite:(GTXTestSuite *)suite NSAssert(intersection.tests.count == 0, @"Error! Attempting to install GTXChecks multiple times on the same test cases: %@", intersection); + (void)intersection; // Ensures 'intersection' is marked as used even if NSAssert is removed. } [gIntsallOptions addObject:options]; @@ -143,17 +144,6 @@ + (GTXiLibFailureHandler)failureHandler { return [GTXToolKit checkWithName:name block:block]; } -+ (GTXElementBlacklist *)blacklistForElementsOfClassNamed:(NSString *)className { - return [[GTXElementBlacklist alloc] initWithElementClassName:className - checkName:nil]; -} - -+ (GTXElementBlacklist *)blacklistForElementsOfClassNamed:(NSString *)className - forCheckNamed:(NSString *)skipCheckName { - return [[GTXElementBlacklist alloc] initWithElementClassName:className - checkName:skipCheckName]; -} - #pragma mark - Private /** @@ -216,13 +206,8 @@ + (void)_testCaseDidBegin:(NSNotification *)notification { for (id check in gCurrentOptions.checks) { [gToolkit registerCheck:check]; } - for (GTXElementBlacklist *blacklist in gCurrentOptions.elementBlacklist) { - if (blacklist.checkName) { - [gToolkit ignoreElementsOfClassNamed:blacklist.elementClassName - forCheckNamed:blacklist.checkName]; - } else { - [gToolkit ignoreElementsOfClassNamed:blacklist.elementClassName]; - } + for (id blacklist in gCurrentOptions.elementBlacklist) { + [gToolkit registerBlacklist:blacklist]; } } } diff --git a/Classes/NSError+GTXAdditions.h b/Classes/NSError+GTXAdditions.h index 0cfe713..731d87f 100644 --- a/Classes/NSError+GTXAdditions.h +++ b/Classes/NSError+GTXAdditions.h @@ -57,6 +57,10 @@ typedef NS_ENUM(NSInteger, GTXCheckErrorCode) { * The element provided for accessibility checking cannot be focused by VoiceOver. */ GTXCheckErrorCodeNotFocusableElement, + /** + * The test environment could not be set up before tests are run. + */ + GTXCheckErrorCodeInvalidTestEnvironment, /** * An error code for all errors not related to the above. */ diff --git a/Classes/NSError+GTXAdditions.m b/Classes/NSError+GTXAdditions.m index 0e35935..2c43da9 100644 --- a/Classes/NSError+GTXAdditions.m +++ b/Classes/NSError+GTXAdditions.m @@ -59,7 +59,7 @@ + (BOOL)gtx_logOrSetGTXCheckFailedError:(GTXErrorRefType)errorOrNil [NSString stringWithFormat:@"Check \"%@\" failed, %@", name, description]; NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : fullDescription, - kGTXErrorFailingElementKey: [element description], + kGTXErrorFailingElementKey: element, kGTXErrorCheckNameKey: name }; NSError *error = [NSError errorWithDomain:kGTXErrorDomain code:GTXCheckErrorCodeAccessibilityCheckFailed diff --git a/GTXiLib.xcodeproj/project.pbxproj b/GTXiLib.xcodeproj/project.pbxproj index 33f7a2c..6650126 100644 --- a/GTXiLib.xcodeproj/project.pbxproj +++ b/GTXiLib.xcodeproj/project.pbxproj @@ -18,6 +18,31 @@ 6115E7A0204F5BC9003E32F9 /* GTXTestAccessibilityElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 6115E798204F5BC8003E32F9 /* GTXTestAccessibilityElements.m */; }; 6115E7A1204F5BC9003E32F9 /* GTXImageAndColorUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6115E799204F5BC8003E32F9 /* GTXImageAndColorUtilsTests.m */; }; 6115E7A2204F5BC9003E32F9 /* GTXBaseTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 6115E79A204F5BC9003E32F9 /* GTXBaseTestCase.m */; }; + 61284DCB211E3FE400F8FF29 /* GTXTestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DC3211E3FE100F8FF29 /* GTXTestViewController.m */; }; + 61284DCC211E3FE400F8FF29 /* GTXTestViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 61284DC4211E3FE200F8FF29 /* GTXTestViewController.xib */; }; + 61284DCD211E3FE400F8FF29 /* GTXTestAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DC5211E3FE200F8FF29 /* GTXTestAppDelegate.m */; }; + 61284DCE211E3FE400F8FF29 /* GTXTestStepperButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DC6211E3FE200F8FF29 /* GTXTestStepperButton.m */; }; + 61284DCF211E3FE400F8FF29 /* GTXTestApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DCA211E3FE400F8FF29 /* GTXTestApp.m */; }; + 61284DD5211E42CF00F8FF29 /* GTXBlacklistBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DD2211E42CD00F8FF29 /* GTXBlacklistBlock.m */; }; + 61284DD6211E42CF00F8FF29 /* GTXBlacklistBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 61284DD3211E42CE00F8FF29 /* GTXBlacklistBlock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 61284DD7211E42CF00F8FF29 /* GTXBlacklisting.h in Headers */ = {isa = PBXBuildFile; fileRef = 61284DD4211E42CE00F8FF29 /* GTXBlacklisting.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 61284DDA211E437800F8FF29 /* GTXTestEnvironment.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DD8211E437700F8FF29 /* GTXTestEnvironment.m */; }; + 61284DDB211E437800F8FF29 /* GTXTestEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 61284DD9211E437800F8FF29 /* GTXTestEnvironment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 61284DF9211E447E00F8FF29 /* GTXTestGtxWorksForPassingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DEA211E447800F8FF29 /* GTXTestGtxWorksForPassingTests.m */; }; + 61284DFA211E447E00F8FF29 /* GTXTestFailingClassFailCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DEB211E447900F8FF29 /* GTXTestFailingClassFailCheck.m */; }; + 61284DFB211E447E00F8FF29 /* GTXTestAnalyticsBaseTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DEC211E447900F8FF29 /* GTXTestAnalyticsBaseTest.m */; }; + 61284DFC211E447E00F8FF29 /* GTXTestGtxCanBlacklistTestCases.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DED211E447900F8FF29 /* GTXTestGtxCanBlacklistTestCases.m */; }; + 61284DFD211E447E00F8FF29 /* GTXTestGtxCanDetectFailuresInInheritedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DEE211E447A00F8FF29 /* GTXTestGtxCanDetectFailuresInInheritedTests.m */; }; + 61284DFE211E447E00F8FF29 /* GTXTestGtxCanIgnoreElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DEF211E447A00F8FF29 /* GTXTestGtxCanIgnoreElements.m */; }; + 61284DFF211E447E00F8FF29 /* GTXTestGtxWithDefaultChecks.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DF0211E447A00F8FF29 /* GTXTestGtxWithDefaultChecks.m */; }; + 61284E00211E447E00F8FF29 /* GTXTestBaseTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DF1211E447B00F8FF29 /* GTXTestBaseTest.m */; }; + 61284E01211E447E00F8FF29 /* GTXTestGtxCanWhitelistTestCases.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DF2211E447C00F8FF29 /* GTXTestGtxCanWhitelistTestCases.m */; }; + 61284E02211E447E00F8FF29 /* GTXTestAnalyticsIsTriggered.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DF3211E447C00F8FF29 /* GTXTestAnalyticsIsTriggered.m */; }; + 61284E03211E447E00F8FF29 /* GTXTestAnalyticsCanBeDisabled.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DF5211E447C00F8FF29 /* GTXTestAnalyticsCanBeDisabled.m */; }; + 61284E04211E447E00F8FF29 /* GTXTestGtxCanIgnoreElementsOnSpecificCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DF6211E447D00F8FF29 /* GTXTestGtxCanIgnoreElementsOnSpecificCheck.m */; }; + 61284E05211E447E00F8FF29 /* GTXTestGtxCanDetectFailures.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284DF8211E447D00F8FF29 /* GTXTestGtxCanDetectFailures.m */; }; + 61284E0A211E45C600F8FF29 /* GTXBlacklistFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 61284E08211E45C500F8FF29 /* GTXBlacklistFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 61284E0B211E45C600F8FF29 /* GTXBlacklistFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 61284E09211E45C600F8FF29 /* GTXBlacklistFactory.m */; }; 61A0C4DE2061896300DF0169 /* GTXiLibCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A0C4DB2061896300DF0169 /* GTXiLibCore.m */; }; 61A0C4DF2061896300DF0169 /* GTXiLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A0C4DC2061896300DF0169 /* GTXiLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61A0C4E02061896300DF0169 /* GTXiLibCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A0C4DD2061896300DF0169 /* GTXiLibCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -31,7 +56,6 @@ 61ABAEB7204A0B0B006DBF0A /* GTXLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = 61ABAE95204A0B03006DBF0A /* GTXLogging.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61ABAEB8204A0B0B006DBF0A /* GTXAnalyticsUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 61ABAE96204A0B03006DBF0A /* GTXAnalyticsUtils.m */; }; 61ABAEB9204A0B0B006DBF0A /* GTXChecking.h in Headers */ = {isa = PBXBuildFile; fileRef = 61ABAE97204A0B04006DBF0A /* GTXChecking.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 61ABAEBA204A0B0B006DBF0A /* GTXElementBlacklist.m in Sources */ = {isa = PBXBuildFile; fileRef = 61ABAE98204A0B04006DBF0A /* GTXElementBlacklist.m */; }; 61ABAEBB204A0B0B006DBF0A /* GTXTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 61ABAE99204A0B04006DBF0A /* GTXTestCase.m */; }; 61ABAEBC204A0B0B006DBF0A /* GTXAccessibilityTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 61ABAE9A204A0B04006DBF0A /* GTXAccessibilityTree.m */; }; 61ABAEBD204A0B0B006DBF0A /* GTXAssertions.h in Headers */ = {isa = PBXBuildFile; fileRef = 61ABAE9B204A0B05006DBF0A /* GTXAssertions.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -50,7 +74,6 @@ 61ABAECC204A0B0B006DBF0A /* GTXErrorReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 61ABAEAA204A0B09006DBF0A /* GTXErrorReporter.m */; }; 61ABAECD204A0B0B006DBF0A /* GTXToolKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 61ABAEAB204A0B09006DBF0A /* GTXToolKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61ABAECE204A0B0B006DBF0A /* GTXImageRGBAData.m in Sources */ = {isa = PBXBuildFile; fileRef = 61ABAEAC204A0B0A006DBF0A /* GTXImageRGBAData.m */; }; - 61ABAED1204A0B0B006DBF0A /* GTXElementBlacklist.h in Headers */ = {isa = PBXBuildFile; fileRef = 61ABAEAF204A0B0A006DBF0A /* GTXElementBlacklist.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -61,6 +84,20 @@ remoteGlobalIDString = 613C3F40204A09E7007D44A8; remoteInfo = GTAxe; }; + 61284DE5211E442800F8FF29 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 613C3F38204A09E7007D44A8 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 61284DAC211E3F8300F8FF29; + remoteInfo = TestApp; + }; + 61284E06211E448F00F8FF29 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 613C3F38204A09E7007D44A8 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 613C3F40204A09E7007D44A8; + remoteInfo = GTXiLib; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -77,6 +114,39 @@ 6115E798204F5BC8003E32F9 /* GTXTestAccessibilityElements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestAccessibilityElements.m; path = Tests/UnitTests/GTXTestAccessibilityElements.m; sourceTree = SOURCE_ROOT; }; 6115E799204F5BC8003E32F9 /* GTXImageAndColorUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXImageAndColorUtilsTests.m; path = Tests/UnitTests/GTXImageAndColorUtilsTests.m; sourceTree = SOURCE_ROOT; }; 6115E79A204F5BC9003E32F9 /* GTXBaseTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXBaseTestCase.m; path = Tests/UnitTests/GTXBaseTestCase.m; sourceTree = SOURCE_ROOT; }; + 61284DAD211E3F8300F8FF29 /* TestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 61284DC3211E3FE100F8FF29 /* GTXTestViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestViewController.m; path = Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.m; sourceTree = SOURCE_ROOT; }; + 61284DC4211E3FE200F8FF29 /* GTXTestViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = GTXTestViewController.xib; path = Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.xib; sourceTree = SOURCE_ROOT; }; + 61284DC5211E3FE200F8FF29 /* GTXTestAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestAppDelegate.m; path = Tests/FunctionalTests/TestApp/Sources/GTXTestAppDelegate.m; sourceTree = SOURCE_ROOT; }; + 61284DC6211E3FE200F8FF29 /* GTXTestStepperButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestStepperButton.m; path = Tests/FunctionalTests/TestApp/Sources/GTXTestStepperButton.m; sourceTree = SOURCE_ROOT; }; + 61284DC7211E3FE300F8FF29 /* GTXTestViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXTestViewController.h; path = Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.h; sourceTree = SOURCE_ROOT; }; + 61284DC8211E3FE300F8FF29 /* GTXTestStepperButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXTestStepperButton.h; path = Tests/FunctionalTests/TestApp/Sources/GTXTestStepperButton.h; sourceTree = SOURCE_ROOT; }; + 61284DC9211E3FE300F8FF29 /* GTXTestAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXTestAppDelegate.h; path = Tests/FunctionalTests/TestApp/Sources/GTXTestAppDelegate.h; sourceTree = SOURCE_ROOT; }; + 61284DCA211E3FE400F8FF29 /* GTXTestApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestApp.m; path = Tests/FunctionalTests/TestApp/Sources/GTXTestApp.m; sourceTree = SOURCE_ROOT; }; + 61284DD0211E3FF400F8FF29 /* GTXTestApp-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GTXTestApp-Info.plist"; path = "Tests/FunctionalTests/TestApp/GTXTestApp-Info.plist"; sourceTree = SOURCE_ROOT; }; + 61284DD2211E42CD00F8FF29 /* GTXBlacklistBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXBlacklistBlock.m; path = Classes/GTXBlacklistBlock.m; sourceTree = SOURCE_ROOT; }; + 61284DD3211E42CE00F8FF29 /* GTXBlacklistBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXBlacklistBlock.h; path = Classes/GTXBlacklistBlock.h; sourceTree = SOURCE_ROOT; }; + 61284DD4211E42CE00F8FF29 /* GTXBlacklisting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXBlacklisting.h; path = Classes/GTXBlacklisting.h; sourceTree = SOURCE_ROOT; }; + 61284DD8211E437700F8FF29 /* GTXTestEnvironment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestEnvironment.m; path = Classes/GTXTestEnvironment.m; sourceTree = SOURCE_ROOT; }; + 61284DD9211E437800F8FF29 /* GTXTestEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXTestEnvironment.h; path = Classes/GTXTestEnvironment.h; sourceTree = SOURCE_ROOT; }; + 61284DE0211E442700F8FF29 /* FunctionalTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FunctionalTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 61284DEA211E447800F8FF29 /* GTXTestGtxWorksForPassingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestGtxWorksForPassingTests.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxWorksForPassingTests.m; sourceTree = SOURCE_ROOT; }; + 61284DEB211E447900F8FF29 /* GTXTestFailingClassFailCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestFailingClassFailCheck.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestFailingClassFailCheck.m; sourceTree = SOURCE_ROOT; }; + 61284DEC211E447900F8FF29 /* GTXTestAnalyticsBaseTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestAnalyticsBaseTest.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestAnalyticsBaseTest.m; sourceTree = SOURCE_ROOT; }; + 61284DED211E447900F8FF29 /* GTXTestGtxCanBlacklistTestCases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestGtxCanBlacklistTestCases.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanBlacklistTestCases.m; sourceTree = SOURCE_ROOT; }; + 61284DEE211E447A00F8FF29 /* GTXTestGtxCanDetectFailuresInInheritedTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestGtxCanDetectFailuresInInheritedTests.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanDetectFailuresInInheritedTests.m; sourceTree = SOURCE_ROOT; }; + 61284DEF211E447A00F8FF29 /* GTXTestGtxCanIgnoreElements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestGtxCanIgnoreElements.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElements.m; sourceTree = SOURCE_ROOT; }; + 61284DF0211E447A00F8FF29 /* GTXTestGtxWithDefaultChecks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestGtxWithDefaultChecks.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxWithDefaultChecks.m; sourceTree = SOURCE_ROOT; }; + 61284DF1211E447B00F8FF29 /* GTXTestBaseTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestBaseTest.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.m; sourceTree = SOURCE_ROOT; }; + 61284DF2211E447C00F8FF29 /* GTXTestGtxCanWhitelistTestCases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestGtxCanWhitelistTestCases.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanWhitelistTestCases.m; sourceTree = SOURCE_ROOT; }; + 61284DF3211E447C00F8FF29 /* GTXTestAnalyticsIsTriggered.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestAnalyticsIsTriggered.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestAnalyticsIsTriggered.m; sourceTree = SOURCE_ROOT; }; + 61284DF4211E447C00F8FF29 /* GTXTestAnalyticsBaseTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXTestAnalyticsBaseTest.h; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestAnalyticsBaseTest.h; sourceTree = SOURCE_ROOT; }; + 61284DF5211E447C00F8FF29 /* GTXTestAnalyticsCanBeDisabled.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestAnalyticsCanBeDisabled.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestAnalyticsCanBeDisabled.m; sourceTree = SOURCE_ROOT; }; + 61284DF6211E447D00F8FF29 /* GTXTestGtxCanIgnoreElementsOnSpecificCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestGtxCanIgnoreElementsOnSpecificCheck.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElementsOnSpecificCheck.m; sourceTree = SOURCE_ROOT; }; + 61284DF7211E447D00F8FF29 /* GTXTestBaseTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXTestBaseTest.h; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.h; sourceTree = SOURCE_ROOT; }; + 61284DF8211E447D00F8FF29 /* GTXTestGtxCanDetectFailures.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestGtxCanDetectFailures.m; path = Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanDetectFailures.m; sourceTree = SOURCE_ROOT; }; + 61284E08211E45C500F8FF29 /* GTXBlacklistFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXBlacklistFactory.h; path = Classes/GTXBlacklistFactory.h; sourceTree = SOURCE_ROOT; }; + 61284E09211E45C600F8FF29 /* GTXBlacklistFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXBlacklistFactory.m; path = Classes/GTXBlacklistFactory.m; sourceTree = SOURCE_ROOT; }; 613C3F41204A09E8007D44A8 /* GTXiLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GTXiLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 61A0C4DB2061896300DF0169 /* GTXiLibCore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXiLibCore.m; path = Classes/GTXiLibCore.m; sourceTree = SOURCE_ROOT; }; 61A0C4DC2061896300DF0169 /* GTXiLib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXiLib.h; path = Classes/GTXiLib.h; sourceTree = SOURCE_ROOT; }; @@ -92,7 +162,6 @@ 61ABAE95204A0B03006DBF0A /* GTXLogging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXLogging.h; path = Classes/GTXLogging.h; sourceTree = SOURCE_ROOT; }; 61ABAE96204A0B03006DBF0A /* GTXAnalyticsUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXAnalyticsUtils.m; path = Classes/GTXAnalyticsUtils.m; sourceTree = SOURCE_ROOT; }; 61ABAE97204A0B04006DBF0A /* GTXChecking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXChecking.h; path = Classes/GTXChecking.h; sourceTree = SOURCE_ROOT; }; - 61ABAE98204A0B04006DBF0A /* GTXElementBlacklist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXElementBlacklist.m; path = Classes/GTXElementBlacklist.m; sourceTree = SOURCE_ROOT; }; 61ABAE99204A0B04006DBF0A /* GTXTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXTestCase.m; path = Classes/GTXTestCase.m; sourceTree = SOURCE_ROOT; }; 61ABAE9A204A0B04006DBF0A /* GTXAccessibilityTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXAccessibilityTree.m; path = Classes/GTXAccessibilityTree.m; sourceTree = SOURCE_ROOT; }; 61ABAE9B204A0B05006DBF0A /* GTXAssertions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXAssertions.h; path = Classes/GTXAssertions.h; sourceTree = SOURCE_ROOT; }; @@ -111,7 +180,6 @@ 61ABAEAA204A0B09006DBF0A /* GTXErrorReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXErrorReporter.m; path = Classes/GTXErrorReporter.m; sourceTree = SOURCE_ROOT; }; 61ABAEAB204A0B09006DBF0A /* GTXToolKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXToolKit.h; path = Classes/GTXToolKit.h; sourceTree = SOURCE_ROOT; }; 61ABAEAC204A0B0A006DBF0A /* GTXImageRGBAData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTXImageRGBAData.m; path = Classes/GTXImageRGBAData.m; sourceTree = SOURCE_ROOT; }; - 61ABAEAF204A0B0A006DBF0A /* GTXElementBlacklist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTXElementBlacklist.h; path = Classes/GTXElementBlacklist.h; sourceTree = SOURCE_ROOT; }; 61B7F4A2204A15D30062DF65 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; /* End PBXFileReference section */ @@ -124,6 +192,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 61284DAA211E3F8300F8FF29 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61284DDD211E442700F8FF29 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 613C3F3D204A09E7007D44A8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -134,11 +216,51 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 61284DAE211E3F8300F8FF29 /* TestApp */ = { + isa = PBXGroup; + children = ( + 61284DD0211E3FF400F8FF29 /* GTXTestApp-Info.plist */, + 61284DCA211E3FE400F8FF29 /* GTXTestApp.m */, + 61284DC9211E3FE300F8FF29 /* GTXTestAppDelegate.h */, + 61284DC5211E3FE200F8FF29 /* GTXTestAppDelegate.m */, + 61284DC8211E3FE300F8FF29 /* GTXTestStepperButton.h */, + 61284DC6211E3FE200F8FF29 /* GTXTestStepperButton.m */, + 61284DC7211E3FE300F8FF29 /* GTXTestViewController.h */, + 61284DC3211E3FE100F8FF29 /* GTXTestViewController.m */, + 61284DC4211E3FE200F8FF29 /* GTXTestViewController.xib */, + ); + path = TestApp; + sourceTree = ""; + }; + 61284DE1211E442700F8FF29 /* FunctionalTests */ = { + isa = PBXGroup; + children = ( + 61284DF4211E447C00F8FF29 /* GTXTestAnalyticsBaseTest.h */, + 61284DEC211E447900F8FF29 /* GTXTestAnalyticsBaseTest.m */, + 61284DF5211E447C00F8FF29 /* GTXTestAnalyticsCanBeDisabled.m */, + 61284DF3211E447C00F8FF29 /* GTXTestAnalyticsIsTriggered.m */, + 61284DF7211E447D00F8FF29 /* GTXTestBaseTest.h */, + 61284DF1211E447B00F8FF29 /* GTXTestBaseTest.m */, + 61284DEB211E447900F8FF29 /* GTXTestFailingClassFailCheck.m */, + 61284DED211E447900F8FF29 /* GTXTestGtxCanBlacklistTestCases.m */, + 61284DF8211E447D00F8FF29 /* GTXTestGtxCanDetectFailures.m */, + 61284DEE211E447A00F8FF29 /* GTXTestGtxCanDetectFailuresInInheritedTests.m */, + 61284DEF211E447A00F8FF29 /* GTXTestGtxCanIgnoreElements.m */, + 61284DF6211E447D00F8FF29 /* GTXTestGtxCanIgnoreElementsOnSpecificCheck.m */, + 61284DF2211E447C00F8FF29 /* GTXTestGtxCanWhitelistTestCases.m */, + 61284DF0211E447A00F8FF29 /* GTXTestGtxWithDefaultChecks.m */, + 61284DEA211E447800F8FF29 /* GTXTestGtxWorksForPassingTests.m */, + ); + path = FunctionalTests; + sourceTree = ""; + }; 613C3F37204A09E7007D44A8 = { isa = PBXGroup; children = ( 613C3F43204A09E8007D44A8 /* GTXiLib */, 61BC5526204E2736004D2FB9 /* GTXiUnitTests */, + 61284DAE211E3F8300F8FF29 /* TestApp */, + 61284DE1211E442700F8FF29 /* FunctionalTests */, 613C3F42204A09E8007D44A8 /* Products */, 61B7F4A1204A15D30062DF65 /* Frameworks */, ); @@ -149,6 +271,8 @@ children = ( 613C3F41204A09E8007D44A8 /* GTXiLib.framework */, 6115E786204F5BA2003E32F9 /* GTXiUnitTests.xctest */, + 61284DAD211E3F8300F8FF29 /* TestApp.app */, + 61284DE0211E442700F8FF29 /* FunctionalTests.xctest */, ); name = Products; sourceTree = ""; @@ -168,6 +292,8 @@ 61A0C4DC2061896300DF0169 /* GTXiLib.h */, 61A0C4DD2061896300DF0169 /* GTXiLibCore.h */, 61A0C4DB2061896300DF0169 /* GTXiLibCore.m */, + 61284E08211E45C500F8FF29 /* GTXBlacklistFactory.h */, + 61284E09211E45C600F8FF29 /* GTXBlacklistFactory.m */, 61ABAE93204A0B03006DBF0A /* GTXAccessibilityTree.h */, 61ABAE9A204A0B04006DBF0A /* GTXAccessibilityTree.m */, 61ABAE8F204A0B02006DBF0A /* GTXAnalytics.h */, @@ -175,6 +301,9 @@ 61ABAE8E204A0B02006DBF0A /* GTXAnalyticsUtils.h */, 61ABAE96204A0B03006DBF0A /* GTXAnalyticsUtils.m */, 61ABAE9B204A0B05006DBF0A /* GTXAssertions.h */, + 61284DD3211E42CE00F8FF29 /* GTXBlacklistBlock.h */, + 61284DD2211E42CD00F8FF29 /* GTXBlacklistBlock.m */, + 61284DD4211E42CE00F8FF29 /* GTXBlacklisting.h */, 61ABAEA9204A0B09006DBF0A /* GTXCheckBlock.h */, 61ABAE9E204A0B06006DBF0A /* GTXCheckBlock.m */, 610CA3FB204DFC76008BAAA1 /* GTXPluginXCTestCase.h */, @@ -183,8 +312,6 @@ 61ABAEA8204A0B08006DBF0A /* GTXChecksCollection.h */, 61ABAEA4204A0B07006DBF0A /* GTXChecksCollection.m */, 61ABAE94204A0B03006DBF0A /* GTXCommon.h */, - 61ABAEAF204A0B0A006DBF0A /* GTXElementBlacklist.h */, - 61ABAE98204A0B04006DBF0A /* GTXElementBlacklist.m */, 61ABAE90204A0B02006DBF0A /* GTXErrorReporter.h */, 61ABAEAA204A0B09006DBF0A /* GTXErrorReporter.m */, 61ABAEA2204A0B06006DBF0A /* GTXImageAndColorUtils.h */, @@ -192,6 +319,8 @@ 61ABAE9C204A0B05006DBF0A /* GTXImageRGBAData.h */, 61ABAEAC204A0B0A006DBF0A /* GTXImageRGBAData.m */, 61ABAE95204A0B03006DBF0A /* GTXLogging.h */, + 61284DD9211E437800F8FF29 /* GTXTestEnvironment.h */, + 61284DD8211E437700F8FF29 /* GTXTestEnvironment.m */, 61ABAEA1204A0B06006DBF0A /* GTXTestCase.h */, 61ABAE99204A0B04006DBF0A /* GTXTestCase.m */, 61ABAE9F204A0B06006DBF0A /* GTXTestSuite.h */, @@ -236,8 +365,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 61284DDB211E437800F8FF29 /* GTXTestEnvironment.h in Headers */, 61ABAEB5204A0B0B006DBF0A /* GTXAccessibilityTree.h in Headers */, - 61ABAED1204A0B0B006DBF0A /* GTXElementBlacklist.h in Headers */, 61ABAECD204A0B0B006DBF0A /* GTXToolKit.h in Headers */, 61ABAEC8204A0B0B006DBF0A /* NSError+GTXAdditions.h in Headers */, 61ABAEC4204A0B0B006DBF0A /* GTXImageAndColorUtils.h in Headers */, @@ -245,11 +374,14 @@ 61ABAEBD204A0B0B006DBF0A /* GTXAssertions.h in Headers */, 610CA3FD204DFC76008BAAA1 /* GTXPluginXCTestCase.h in Headers */, 61ABAECB204A0B0B006DBF0A /* GTXCheckBlock.h in Headers */, + 61284DD6211E42CF00F8FF29 /* GTXBlacklistBlock.h in Headers */, 61ABAEB9204A0B0B006DBF0A /* GTXChecking.h in Headers */, + 61284E0A211E45C600F8FF29 /* GTXBlacklistFactory.h in Headers */, 61ABAEBE204A0B0B006DBF0A /* GTXImageRGBAData.h in Headers */, 61ABAEB2204A0B0B006DBF0A /* GTXErrorReporter.h in Headers */, 61ABAEC3204A0B0B006DBF0A /* GTXTestCase.h in Headers */, 61ABAEB1204A0B0B006DBF0A /* GTXAnalytics.h in Headers */, + 61284DD7211E42CF00F8FF29 /* GTXBlacklisting.h in Headers */, 61A0C4E02061896300DF0169 /* GTXiLibCore.h in Headers */, 61A0C4DF2061896300DF0169 /* GTXiLib.h in Headers */, 61ABAECA204A0B0B006DBF0A /* GTXChecksCollection.h in Headers */, @@ -280,6 +412,42 @@ productReference = 6115E786204F5BA2003E32F9 /* GTXiUnitTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 61284DAC211E3F8300F8FF29 /* TestApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 61284DC2211E3F8600F8FF29 /* Build configuration list for PBXNativeTarget "TestApp" */; + buildPhases = ( + 61284DA9211E3F8300F8FF29 /* Sources */, + 61284DAA211E3F8300F8FF29 /* Frameworks */, + 61284DAB211E3F8300F8FF29 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TestApp; + productName = TestApp; + productReference = 61284DAD211E3F8300F8FF29 /* TestApp.app */; + productType = "com.apple.product-type.application"; + }; + 61284DDF211E442700F8FF29 /* FunctionalTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 61284DE7211E442800F8FF29 /* Build configuration list for PBXNativeTarget "FunctionalTests" */; + buildPhases = ( + 61284DDC211E442700F8FF29 /* Sources */, + 61284DDD211E442700F8FF29 /* Frameworks */, + 61284DDE211E442700F8FF29 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 61284E07211E448F00F8FF29 /* PBXTargetDependency */, + 61284DE6211E442800F8FF29 /* PBXTargetDependency */, + ); + name = FunctionalTests; + productName = FunctionalTests; + productReference = 61284DE0211E442700F8FF29 /* FunctionalTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 613C3F40204A09E7007D44A8 /* GTXiLib */ = { isa = PBXNativeTarget; buildConfigurationList = 613C3F49204A09E8007D44A8 /* Build configuration list for PBXNativeTarget "GTXiLib" */; @@ -311,6 +479,15 @@ CreatedOnToolsVersion = 9.1; ProvisioningStyle = Automatic; }; + 61284DAC211E3F8300F8FF29 = { + CreatedOnToolsVersion = 9.4.1; + ProvisioningStyle = Automatic; + }; + 61284DDF211E442700F8FF29 = { + CreatedOnToolsVersion = 9.4.1; + ProvisioningStyle = Automatic; + TestTargetID = 61284DAC211E3F8300F8FF29; + }; 613C3F40204A09E7007D44A8 = { CreatedOnToolsVersion = 9.1; ProvisioningStyle = Automatic; @@ -323,6 +500,7 @@ hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 613C3F37204A09E7007D44A8; productRefGroup = 613C3F42204A09E8007D44A8 /* Products */; @@ -331,6 +509,8 @@ targets = ( 613C3F40204A09E7007D44A8 /* GTXiLib */, 6115E785204F5BA2003E32F9 /* GTXiUnitTests */, + 61284DAC211E3F8300F8FF29 /* TestApp */, + 61284DDF211E442700F8FF29 /* FunctionalTests */, ); }; /* End PBXProject section */ @@ -343,6 +523,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 61284DAB211E3F8300F8FF29 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 61284DCC211E3FE400F8FF29 /* GTXTestViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61284DDE211E442700F8FF29 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 613C3F3F204A09E7007D44A8 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -368,6 +563,37 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 61284DA9211E3F8300F8FF29 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 61284DCD211E3FE400F8FF29 /* GTXTestAppDelegate.m in Sources */, + 61284DCE211E3FE400F8FF29 /* GTXTestStepperButton.m in Sources */, + 61284DCB211E3FE400F8FF29 /* GTXTestViewController.m in Sources */, + 61284DCF211E3FE400F8FF29 /* GTXTestApp.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61284DDC211E442700F8FF29 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 61284E05211E447E00F8FF29 /* GTXTestGtxCanDetectFailures.m in Sources */, + 61284DFD211E447E00F8FF29 /* GTXTestGtxCanDetectFailuresInInheritedTests.m in Sources */, + 61284DFC211E447E00F8FF29 /* GTXTestGtxCanBlacklistTestCases.m in Sources */, + 61284E00211E447E00F8FF29 /* GTXTestBaseTest.m in Sources */, + 61284DFA211E447E00F8FF29 /* GTXTestFailingClassFailCheck.m in Sources */, + 61284E02211E447E00F8FF29 /* GTXTestAnalyticsIsTriggered.m in Sources */, + 61284DFF211E447E00F8FF29 /* GTXTestGtxWithDefaultChecks.m in Sources */, + 61284DFE211E447E00F8FF29 /* GTXTestGtxCanIgnoreElements.m in Sources */, + 61284DF9211E447E00F8FF29 /* GTXTestGtxWorksForPassingTests.m in Sources */, + 61284DFB211E447E00F8FF29 /* GTXTestAnalyticsBaseTest.m in Sources */, + 61284E04211E447E00F8FF29 /* GTXTestGtxCanIgnoreElementsOnSpecificCheck.m in Sources */, + 61284E03211E447E00F8FF29 /* GTXTestAnalyticsCanBeDisabled.m in Sources */, + 61284E01211E447E00F8FF29 /* GTXTestGtxCanWhitelistTestCases.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 613C3F3C204A09E7007D44A8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -376,11 +602,13 @@ 61ABAECC204A0B0B006DBF0A /* GTXErrorReporter.m in Sources */, 61ABAEB4204A0B0B006DBF0A /* GTXTestSuite.m in Sources */, 61ABAEB3204A0B0B006DBF0A /* GTXImageAndColorUtils.m in Sources */, + 61284E0B211E45C600F8FF29 /* GTXBlacklistFactory.m in Sources */, 61ABAEC2204A0B0B006DBF0A /* GTXToolKit.m in Sources */, 61ABAEC9204A0B0B006DBF0A /* GTXAnalytics.m in Sources */, - 61ABAEBA204A0B0B006DBF0A /* GTXElementBlacklist.m in Sources */, 610CA3FE204DFC76008BAAA1 /* GTXPluginXCTestCase.m in Sources */, + 61284DDA211E437800F8FF29 /* GTXTestEnvironment.m in Sources */, 61ABAEB8204A0B0B006DBF0A /* GTXAnalyticsUtils.m in Sources */, + 61284DD5211E42CF00F8FF29 /* GTXBlacklistBlock.m in Sources */, 61ABAEBF204A0B0B006DBF0A /* NSError+GTXAdditions.m in Sources */, 61ABAEBC204A0B0B006DBF0A /* GTXAccessibilityTree.m in Sources */, 61ABAEC0204A0B0B006DBF0A /* GTXCheckBlock.m in Sources */, @@ -398,6 +626,16 @@ target = 613C3F40204A09E7007D44A8 /* GTXiLib */; targetProxy = 6115E78C204F5BA3003E32F9 /* PBXContainerItemProxy */; }; + 61284DE6211E442800F8FF29 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 61284DAC211E3F8300F8FF29 /* TestApp */; + targetProxy = 61284DE5211E442800F8FF29 /* PBXContainerItemProxy */; + }; + 61284E07211E448F00F8FF29 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 613C3F40204A09E7007D44A8 /* GTXiLib */; + targetProxy = 61284E06211E448F00F8FF29 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -425,6 +663,76 @@ }; name = Release; }; + 61284DC0211E3F8600F8FF29 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "$(SRCROOT)/Tests/FunctionalTests/TestApp/GTXTestApp-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.gtx.dev.TestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 61284DC1211E3F8600F8FF29 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "$(SRCROOT)/Tests/FunctionalTests/TestApp/GTXTestApp-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.gtx.dev.TestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 61284DE8211E442800F8FF29 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Tests/FunctionalTests/TestApp/GTXTestApp-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.gtx.dev.FunctionalTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestApp.app/TestApp"; + }; + name = Debug; + }; + 61284DE9211E442800F8FF29 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Tests/FunctionalTests/TestApp/GTXTestApp-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.gtx.dev.FunctionalTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestApp.app/TestApp"; + }; + name = Release; + }; 613C3F47204A09E8007D44A8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -585,6 +893,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 61284DC2211E3F8600F8FF29 /* Build configuration list for PBXNativeTarget "TestApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 61284DC0211E3F8600F8FF29 /* Debug */, + 61284DC1211E3F8600F8FF29 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 61284DE7211E442800F8FF29 /* Build configuration list for PBXNativeTarget "FunctionalTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 61284DE8211E442800F8FF29 /* Debug */, + 61284DE9211E442800F8FF29 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 613C3F3B204A09E7007D44A8 /* Build configuration list for PBXProject "GTXiLib" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.m b/Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.m index b1e7268..2b67d4a 100644 --- a/Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.m +++ b/Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.m @@ -76,7 +76,8 @@ - (void)viewDidLoad { actionsToHandlers = [[NSMutableDictionary alloc] init]; [self.navigationController setNavigationBarHidden:YES animated:NO]; - [self axetest_addActionNamed:kAddNoLabelElementActionName handler:^(GTXTestViewController *sSelf) { + [self axetest_addActionNamed:kAddNoLabelElementActionName + handler:^(GTXTestViewController *sSelf) { [sSelf axetest_addElementWithLabel:@""]; }]; [self axetest_addActionNamed:kAddPunctuatedLabelElementActionName @@ -152,6 +153,7 @@ - (void)axetest_addActionNamed:(NSString *)name handler:(ActionHandler)handler { CGRect buttonFrame = newButton.frame; buttonFrame.size.height = kMinimumElementSize; newButton.frame = buttonFrame; + newButton.accessibilityIdentifier = name; CGSize contentSize = self.actionsContainerView.contentSize; buttonFrame.origin.y = contentSize.height + kMargin; diff --git a/Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.h b/Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.h index fec8e1a..641ac32 100644 --- a/Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.h +++ b/Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.h @@ -24,17 +24,17 @@ /** Check that fails if the element is of Class @c GTXTestFailingClass. */ -id checkFailsIfFailingClass; +extern id checkFailsIfFailingClass; /** Check that always passes. */ -id alwaysFail; +extern id alwaysFail; /** Check that always fails. */ -id alwaysPass; +extern id alwaysPass; /** Base test for all GTXiLib functional/integration tests used to setup GTXiLib and capture check diff --git a/Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.m b/Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.m index d0665a3..07d8c9f 100644 --- a/Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.m +++ b/Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.m @@ -19,6 +19,9 @@ #import "GTXTestViewController.h" static NSInteger gFailureCount = 0; +id checkFailsIfFailingClass; +id alwaysFail; +id alwaysPass; @implementation GTXTestBaseTest diff --git a/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElements.m b/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElements.m index e3dc768..b39c3a2 100644 --- a/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElements.m +++ b/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElements.m @@ -15,6 +15,7 @@ // #import "GTXTestBaseTest.h" +#import "GTXBlacklistFactory.h" #import "GTXiLib.h" @interface GTXTestGtxCanIgnoreElements : GTXTestBaseTest @@ -24,8 +25,9 @@ @implementation GTXTestGtxCanIgnoreElements + (void)setUp { [super setUp]; + NSString *className = NSStringFromClass([GTXTestFailingClass class]); NSArray *blacklist = - @[[GTXiLib blacklistForElementsOfClassNamed:NSStringFromClass([GTXTestFailingClass class])]]; + @[[GTXBlacklistFactory blacklistWithClassName:className]]; [GTXiLib installOnTestSuite:[GTXTestSuite suiteWithAllTestsInClass:self] checks:@[checkFailsIfFailingClass] elementBlacklists:blacklist]; diff --git a/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElementsOnSpecificCheck.m b/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElementsOnSpecificCheck.m index fafe612..8d3cde0 100644 --- a/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElementsOnSpecificCheck.m +++ b/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxCanIgnoreElementsOnSpecificCheck.m @@ -24,9 +24,10 @@ @implementation GTXTestGtxCanIgnoreElementsOnSpecificCheck + (void)setUp { [super setUp]; + NSString *className = NSStringFromClass([GTXTestFailingClass class]); NSArray *blacklist = - @[[GTXiLib blacklistForElementsOfClassNamed:NSStringFromClass([GTXTestFailingClass class]) - forCheckNamed:checkFailsIfFailingClass.name]]; + @[[GTXBlacklistFactory blacklistWithClassName:className + checkName:checkFailsIfFailingClass.name]]; id secondFailingCheck = [GTXiLib checkWithName:@"secondFailingCheck" block:^BOOL(id element, GTXErrorRefType errorOrNil) { return ![element isKindOfClass:[GTXTestFailingClass class]]; diff --git a/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxWithDefaultChecks.m b/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxWithDefaultChecks.m index 534d00d..20260f5 100644 --- a/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxWithDefaultChecks.m +++ b/Tests/FunctionalTests/TestApp/TestSources/GTXTestGtxWithDefaultChecks.m @@ -28,10 +28,16 @@ @implementation GTXTestGtxWithDefaultChecks { + (void)setUp { [super setUp]; + NSString *notRedundantCheckName = [[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + name]; + NSArray> *blacklists = @[ + [GTXBlacklistFactory blacklistWithAccessibilityIdentifier:kAddInaccessibleButton + checkName:notRedundantCheckName] + ]; // Install all the default checks on the current test class. [GTXiLib installOnTestSuite:[GTXTestSuite suiteWithAllTestsInClass:self] checks:[GTXChecksCollection allGTXChecks] - elementBlacklists:@[]]; + elementBlacklists:blacklists]; } - (void)setUp { diff --git a/Tests/UnitTests/GTXChecksCollectionTests.m b/Tests/UnitTests/GTXChecksCollectionTests.m index 5c359f0..b209668 100644 --- a/Tests/UnitTests/GTXChecksCollectionTests.m +++ b/Tests/UnitTests/GTXChecksCollectionTests.m @@ -160,6 +160,61 @@ - (void)testGtxCheckForAXLabelPresentWorksWithMutableStrings { errorDescription:nil]; } +- (void)testGtxCheckForAXLabelNotRedundantWithTraits { + // Label is not redundant with traits. + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:YES + withElement:[self uiAccessibilityElementWithLabel:@"foo" + traits:UIAccessibilityTraitButton] + errorDescription:nil]; + // Label is redundant with traits (uppercase). + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:NO + withElement:[self uiAccessibilityElementWithLabel:@"FOO BUTTON" + traits:UIAccessibilityTraitButton] + errorDescription:nil]; + // Label is redundant with traits (lowercase). + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:NO + withElement:[self uiAccessibilityElementWithLabel:@"foo button" + traits:UIAccessibilityTraitButton] + errorDescription:nil]; + // Label is not redundant with traits (not suffix). + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:YES + withElement:[self uiAccessibilityElementWithLabel:@"button foo" + traits:UIAccessibilityTraitButton] + errorDescription:nil]; + // Empty label (not redundant). + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:YES + withElement:[self uiAccessibilityElementWithLabel:@"" + traits:UIAccessibilityTraitButton] + errorDescription:nil]; + // Whitespace label (not redundant). + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:YES + withElement:[self uiAccessibilityElementWithLabel:@" " + traits:UIAccessibilityTraitButton] + errorDescription:nil]; + // No traits or violating label (not redundant). + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:YES + withElement:[self uiAccessibilityElementWithLabel:@"foo"] + errorDescription:nil]; + // No traits (not redundant). + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:YES + withElement:[self uiAccessibilityElementWithLabel:@"foo button"] + errorDescription:nil]; + // Wrong traits (not redundant). + [self assertGtxCheck:[GTXChecksCollection checkForAXLabelNotRedundantWithTraits] + succeeds:YES + withElement:[self uiAccessibilityElementWithLabel:@"foo button" + traits:UIAccessibilityTraitStaticText] + errorDescription:nil]; +} + - (void)testGtxCheckForAXTraitsConflict { NSString * const expectedErrorDescription = @"Check \"Accessibility Traits Don't Conflict\" failed"; @@ -214,6 +269,17 @@ - (UIAccessibilityElement *)uiAccessibilityElementWithTraits:(UIAccessibilityTra return element; } +/** + * @return An accessibility element whose accessibility label is set to the specified @c label + * and accessibility trait is set to the specified @c traits. + */ +- (UIAccessibilityElement *)uiAccessibilityElementWithLabel:(id)label + traits:(UIAccessibilityTraits)traits { + UIAccessibilityElement *element = [self uiAccessibilityElementWithLabel:label]; + element.accessibilityTraits = traits; + return element; +} + /** * Asserts that the given GTXCheck succeeds or fails with the specified element. * @@ -234,9 +300,11 @@ - (void)assertGtxCheck:(id)gtxCheck XCTAssertNil(error); } else { XCTAssertNotNil(error); - XCTAssertTrue([[error description] containsString:descriptionOrNil], - @"[Expected] was not present in [Actual]!\n Expected: %@\n Actual: %@", - descriptionOrNil, [error description]); + if (descriptionOrNil != nil) { + XCTAssertTrue([[error description] containsString:descriptionOrNil], + @"[Expected] was not present in [Actual]!\n Expected: %@\n Actual: %@", + descriptionOrNil, [error description]); + } } } diff --git a/Tests/UnitTests/GTXImageAndColorUtilsTests.m b/Tests/UnitTests/GTXImageAndColorUtilsTests.m index fedb2d6..f406032 100644 --- a/Tests/UnitTests/GTXImageAndColorUtilsTests.m +++ b/Tests/UnitTests/GTXImageAndColorUtilsTests.m @@ -129,7 +129,9 @@ - (void)gtx_assertContrastRatioIsSufficientlyAccurateInImageOfSize:(CGSize)size CGFloat contrastRatioInImage = [GTXImageAndColorUtils gtx_contrastRatioWithTextElementImage:imageWithRect textElementColorShiftedImage:imageWithShiftedColorRect]; - XCTAssertEqualWithAccuracy(contrastRatioInImage, contrastRatioOfColors, kContrastRatioAccuracy); + XCTAssertEqualWithAccuracy(contrastRatioInImage, + contrastRatioOfColors, + kGTXContrastRatioAccuracy); } /** diff --git a/Tests/UnitTests/GTXToolKitTests.m b/Tests/UnitTests/GTXToolKitTests.m index 7712494..224f253 100644 --- a/Tests/UnitTests/GTXToolKitTests.m +++ b/Tests/UnitTests/GTXToolKitTests.m @@ -19,20 +19,27 @@ #import "GTXToolKit.h" #import "GTXAnalytics.h" +#import "GTXBlacklistFactory.h" #import "GTXBaseTestCase.h" -@interface GTXTestElementClass1 : NSObject +@interface GTXTestElementClass1 : UIAccessibilityElement @end @implementation GTXTestElementClass1 @end -@interface GTXTestElementClass2 : NSObject +@interface GTXTestElementClass2 : UIAccessibilityElement @end @implementation GTXTestElementClass2 @end +@interface GTXTestElementClass3 : UIAccessibilityElement +@end + +@implementation GTXTestElementClass3 +@end + @interface GTXToolKitTests : GTXBaseTestCase @end @@ -104,7 +111,7 @@ - (void)testCheckElementsFromRootElementsSkipsHiddenAXElements { XCTAssertTrue([toolkit checkAllElementsFromRootElements:@[root] error:&error]); } -- (void)testIgnoreElementAPIIgnoresElementsFromChecks { +- (void)testBlacklistAPICanSkipElementsFromChecks { GTXToolKit *toolkit = [[GTXToolKit alloc] init]; NSObject *failingElement = [self newAccessibleElement]; id check = [GTXToolKit checkWithName:@"Foo" @@ -114,17 +121,23 @@ - (void)testIgnoreElementAPIIgnoresElementsFromChecks { }]; [toolkit registerCheck:check]; XCTAssertFalse([toolkit checkElement:failingElement error:nil]); - [toolkit ignoreElementsOfClassNamed:NSStringFromClass([failingElement class])]; + [toolkit registerBlacklist: + [GTXBlacklistFactory blacklistWithClassName:NSStringFromClass([failingElement class])]]; XCTAssertTrue([toolkit checkElement:failingElement error:nil]); } -- (void)testIgnoreElementAPIIgnoresElementsFromSpecificChecks { +- (void)testBlacklistAPICanSkipElementsFromSpecificChecks { GTXTestElementClass1 *check1FailingElement = [[GTXTestElementClass1 alloc] init]; check1FailingElement.isAccessibilityElement = YES; + check1FailingElement.accessibilityIdentifier = @"check1FailingElement"; GTXTestElementClass2 *allChecksFailingElement = [[GTXTestElementClass2 alloc] init]; allChecksFailingElement.isAccessibilityElement = YES; + GTXTestElementClass3 *check3FailingElement = [[GTXTestElementClass3 alloc] init]; + check3FailingElement.isAccessibilityElement = YES; + check3FailingElement.accessibilityIdentifier = @"check3FailingElement"; + NSString *check1Name = @"Check 1"; NSString *check2Name = @"Check 2"; id check1 = [GTXToolKit checkWithName:check1Name @@ -143,12 +156,26 @@ - (void)testIgnoreElementAPIIgnoresElementsFromSpecificChecks { [toolkit1 registerCheck:check2]; XCTAssertFalse([toolkit1 checkElement:check1FailingElement error:nil]); XCTAssertFalse([toolkit1 checkElement:allChecksFailingElement error:nil]); + XCTAssertFalse([toolkit1 checkElement:check3FailingElement error:nil]); + + [toolkit1 registerBlacklist: + [GTXBlacklistFactory blacklistWithAccessibilityIdentifier:@"check3FailingElement" + checkName:check1Name]]; + XCTAssertFalse([toolkit1 checkElement:check1FailingElement error:nil]); + XCTAssertFalse([toolkit1 checkElement:allChecksFailingElement error:nil]); + XCTAssertTrue([toolkit1 checkElement:check3FailingElement error:nil]); - [toolkit1 ignoreElementsOfClassNamed:NSStringFromClass([check1FailingElement class])]; + [toolkit1 registerBlacklist: + [GTXBlacklistFactory blacklistWithClassName:NSStringFromClass([check1FailingElement class])]]; + [toolkit1 registerBlacklist: + [GTXBlacklistFactory blacklistWithClassName:NSStringFromClass([check1FailingElement class])]]; XCTAssertTrue([toolkit1 checkElement:check1FailingElement error:nil]); XCTAssertFalse([toolkit1 checkElement:allChecksFailingElement error:nil]); + XCTAssertTrue([toolkit1 checkElement:check3FailingElement error:nil]); - [toolkit1 ignoreElementsOfClassNamed:NSStringFromClass([allChecksFailingElement class])]; + NSString *allChecksFailingElementClass = NSStringFromClass([allChecksFailingElement class]); + [toolkit1 registerBlacklist: + [GTXBlacklistFactory blacklistWithClassName:allChecksFailingElementClass]]; XCTAssertTrue([toolkit1 checkElement:allChecksFailingElement error:nil]); }