Skip to content

Commit

Permalink
Internal Sync: minor fixes to README, new contrast check for UITextVi…
Browse files Browse the repository at this point in the history
…ew (in addition to UILabel that already existed) and tests and lint fixes for globals in tests.
  • Loading branch information
j-sid committed Nov 2, 2018
1 parent 2de4383 commit 36f0c45
Show file tree
Hide file tree
Showing 19 changed files with 175 additions and 58 deletions.
7 changes: 6 additions & 1 deletion Classes/GTXChecksCollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,13 @@ typedef NS_ENUM(NSUInteger, GTXVersion) {
+ (id<GTXChecking>)checkForMinimumTappableArea;

/**
* @return a check that verifies that contrast of all text based elements is at least 3.0.
* @return a check that verifies that contrast of all UILabel elements is at least 3.0.
*/
+ (id<GTXChecking>)checkForSufficientContrastRatio;

/**
* @return a check that verifies that contrast of all UITextView elements is at least 3.0.
*/
+ (id<GTXChecking>)checkForSufficientTextViewContrastRatio;

@end
45 changes: 42 additions & 3 deletions Classes/GTXChecksCollection.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
NSString *const kGTXCheckNameAccessibilityTraitsDontConflict =
@"Accessibility Traits Don't Conflict";
NSString *const kGTXCheckNameMinimumTappableArea = @"Element has Minimum Tappable Area";
NSString *const kGTXCheckNameMinimumContrastRatio = @"Element has Minimum Contrast Ratio";
NSString *const kGTXCheckNameLabelMinimumContrastRatio = @"Label has Minimum Contrast Ratio";
NSString *const kGTXCheckNameTextViewMinimumContrastRatio = @"TextView has Minimum Contrast Ratio";


#pragma mark - Globals

Expand Down Expand Up @@ -166,6 +168,13 @@ @implementation GTXChecksCollection
UIAccessibilityTraits testUITrait = [testTrait unsignedLongLongValue];
if ((BOOL)(elementAXTraits & testUITrait)) {
if ([GTXChecksCollection caseInsensitive:elementAXLabel hasSuffix:redundantText]) {
if ([element isKindOfClass:[UIButton class]] &&
[GTXChecksCollection caseInsensitive:((UIButton *)element).titleLabel.text
hasSuffix:redundantText]) {
// This is a button whose title itself has the word "button", we must ignore this
// kind of elements.
continue;
}
NSError *error;
NSString *stringValue = [self stringValueOfUIAccessibilityTraits:testUITrait
error:&error];
Expand Down Expand Up @@ -302,10 +311,12 @@ @implementation GTXChecksCollection

+ (id<GTXChecking>)checkForSufficientContrastRatio {
id<GTXChecking> check =
[GTXCheckBlock GTXCheckWithName:kGTXCheckNameMinimumContrastRatio
[GTXCheckBlock GTXCheckWithName:kGTXCheckNameLabelMinimumContrastRatio
block:^BOOL(id element, GTXErrorRefType errorOrNil) {
if (![element isKindOfClass:[UILabel class]]) {
return YES;
} else if ([[(UILabel *)element text] length] == 0) {
return YES;
}
CGFloat ratio = [GTXImageAndColorUtils contrastRatioOfUILabel:element];
BOOL hasSufficientContrast =
Expand All @@ -318,7 +329,35 @@ @implementation GTXChecksCollection
(float)kGTXMinContrastRatioForAccessibleText, (float)ratio];
[NSError gtx_logOrSetGTXCheckFailedError:errorOrNil
element:element
name:kGTXCheckNameMinimumContrastRatio
name:kGTXCheckNameLabelMinimumContrastRatio
description:description];
}
return hasSufficientContrast;
}];
return check;
}

+ (id<GTXChecking>)checkForSufficientTextViewContrastRatio {
id<GTXChecking> check =
[GTXCheckBlock GTXCheckWithName:kGTXCheckNameTextViewMinimumContrastRatio
block:^BOOL(id element, GTXErrorRefType errorOrNil) {
if (![element isKindOfClass:[UITextView class]]) {
return YES;
} else if ([[(UITextView *)element text] length] == 0) {
return YES;
}
CGFloat ratio = [GTXImageAndColorUtils contrastRatioOfUILabel:element];
BOOL hasSufficientContrast =
(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)kGTXMinContrastRatioForAccessibleText, (float)ratio];
[NSError gtx_logOrSetGTXCheckFailedError:errorOrNil
element:element
name:kGTXCheckNameTextViewMinimumContrastRatio
description:description];
}
return hasSufficientContrast;
Expand Down
12 changes: 12 additions & 0 deletions Classes/GTXImageAndColorUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,16 @@ extern const CGFloat kGTXContrastRatioAccuracy;
*/
+ (CGFloat)contrastRatioOfUILabel:(UILabel *)label;

/**
* Computes contrast ratio of the given text view to its background.
* This method analyses the text view's text color in the context of its view
hierarchy in order to compute the contrast ratio, because
* of which the text view must already be in a window before this method can be used.
*
* @param view The text view whose contrast ratio is to be computed.
*
* @return The contrast ratio (proportional to 1.0) of the text view.
*/
+ (CGFloat)contrastRatioOfUITextView:(UITextView *)view;

@end
56 changes: 36 additions & 20 deletions Classes/GTXImageAndColorUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,42 @@ + (CGFloat)contrastRatioWithLuminaceOfFirstColor:(CGFloat)color1Luminance
}

+ (CGFloat)contrastRatioOfUILabel:(UILabel *)label {
NSAssert(label.window, @"Label %@ must be part of view hierarchy to use this method, see API "
@"docs for more info.", label);
// Create a block that can take snapshot of the given label.
UIImage *(^takeSnapshot)(void) = ^{
CGRect labelBounds = [label.window convertRect:label.bounds fromView:label];
UIWindow *window = label.window;
NSAssert(label.window, @"Label %@ must be part of view hierarchy to use this method, see API"
@" docs for more info.", label);

// Take snapshot of the label as it exists.
UIImage *before = [self gtx_takeSnapshot:label];

// Update the text color and take another snapshot.
UIColor *prevColor = label.textColor;
label.textColor = [self gtx_shiftedColorWithColor:prevColor];
UIImage *after = [self gtx_takeSnapshot:label];
label.textColor = prevColor;

return [self gtx_contrastRatioWithTextElementImage:before textElementColorShiftedImage:after];
}

+ (CGFloat)contrastRatioOfUITextView:(UITextView *)view {
NSAssert(view.window, @"View %@ must be part of view hierarchy to use this method, see API"
@" docs for more info.", view);

// Take snapshot of the text view as it exists.
UIImage *before = [self gtx_takeSnapshot:view];

// Update the text color and take another snapshot.
UIColor *prevColor = view.textColor;
view.textColor = [self gtx_shiftedColorWithColor:prevColor];
UIImage *after = [self gtx_takeSnapshot:view];
view.textColor = prevColor;

return [self gtx_contrastRatioWithTextElementImage:before textElementColorShiftedImage:after];
}

#pragma mark - Utils

+ (UIImage *)gtx_takeSnapshot:(UIView *)element {
CGRect labelBounds = [element.window convertRect:element.bounds fromView:element];
UIWindow *window = element.window;
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(labelBounds.size,
NO, [UIScreen mainScreen].scale);
Expand All @@ -76,22 +106,8 @@ + (CGFloat)contrastRatioOfUILabel:(UILabel *)label {
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
};

// Take snapshot of the label as it exists.
UIImage *before = takeSnapshot();

// Update the text color and take another snapshot.
UIColor *prevColor = label.textColor;
label.textColor = [self gtx_shiftedColorWithColor:prevColor];
UIImage *after = takeSnapshot();
label.textColor = prevColor;

return [self gtx_contrastRatioWithTextElementImage:before textElementColorShiftedImage:after];
}

#pragma mark - Utils

/**
* Computes the contrast ratio for the text in the given image. This method also requires image of
* the text element with the text color changed, by comparing both the image pixels its possible to
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ snippet of code to it.

```objective-c
// Include the GTXiLib umbrella header.
#import <GTXiLib/GTXiLib.h>

// Note that that is +setUp not -setUp
+ (void)setUp {
Expand Down
17 changes: 11 additions & 6 deletions Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,29 +58,34 @@ FOUNDATION_EXTERN NSString *const kAddAccessibleButtonInContainer;
FOUNDATION_EXTERN NSString *const kAddTinyTappableElement;

/**
* Name of the action that adds an element with very high contrast.
* Name of the action that adds a label with very high contrast.
*/
FOUNDATION_EXTERN NSString *const kAddVeryHighContrastLabel;

/**
* Name of the action that adds an element with very low contrast.
* Name of the action that adds a label with very low contrast.
*/
FOUNDATION_EXTERN NSString *const kAddVeryLowContrastLabel;

/**
* Name of the action that adds an element with barely high contrast.
* Name of the action that adds a label with barely high contrast.
*/
FOUNDATION_EXTERN NSString *const kAddBarelyHighContrastLabel;

/**
* Name of the action that adds an element with barely low contrast.
* Name of the action that adds a label with barely low contrast.
*/
FOUNDATION_EXTERN NSString *const kAddBarelyLowContrastLabel;

/**
* Name of the action that adds a low contrast background.
* Name of the action that adds a text view with low contrast.
*/
FOUNDATION_EXTERN NSString *const kAddLowContrastBackground;
FOUNDATION_EXTERN NSString *const kAddLowContrastTextView;

/**
* Name of the action that adds a standard text view.
*/
FOUNDATION_EXTERN NSString *const kAddStandardUIKitTextView;

/**
* Name of the action that adds a high contrast background.
Expand Down
29 changes: 29 additions & 0 deletions Tests/FunctionalTests/TestApp/Sources/GTXTestViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
NSString *const kAddVeryLowContrastLabel = @"Add very low contrast label";
NSString *const kAddBarelyHighContrastLabel = @"Add barely High contrast label";
NSString *const kAddBarelyLowContrastLabel = @"Add barely Low contrast label";
NSString *const kAddLowContrastTextView = @"Add low contrast text view";
NSString *const kAddStandardUIKitTextView = @"Add standard UIKit text view";
NSString *const kAddLowContrastBackground = @"Add Low contrast background";
NSString *const kAddHighContrastBackground = @"Add High contrast backgorund";

Expand Down Expand Up @@ -140,6 +142,18 @@ - (void)viewDidLoad {
backgroundColor:[UIColor clearColor]];
[sSelf.testArea setBackgroundColor:kAlmostRedButDarker];
}];
[self axetest_addActionNamed:kAddStandardUIKitTextView
handler:^(GTXTestViewController *sSelf) {
// Add a standard contrast text view: black text on white background.
[sSelf axetest_addTextViewWithForgroundColor:nil
backgroundColor:nil];
}];
[self axetest_addActionNamed:kAddLowContrastTextView handler:^(GTXTestViewController *sSelf) {
// Add a low contrast text view: black text on very dark grey background.
UIColor *veryDarkGreyColor = [UIColor colorWithWhite:0.2f alpha:1.0f];
[sSelf axetest_addTextViewWithForgroundColor:[UIColor blackColor]
backgroundColor:veryDarkGreyColor];
}];
}

- (void)axetest_addActionNamed:(NSString *)name handler:(ActionHandler)handler {
Expand Down Expand Up @@ -234,6 +248,21 @@ - (void)axetest_addLabelWithForgroundColor:(UIColor *)foregroundColor
[self.testArea addSubview:label];
}

- (void)axetest_addTextViewWithForgroundColor:(UIColor *)foregroundColor
backgroundColor:(UIColor *)backgroundColor {
UITextView *view = [[UITextView alloc] initWithFrame:CGRectMake(kMargin, kMargin, 0, 0)];
view.font = [UIFont systemFontOfSize:60.0];
view.text = @"Hello";
if (![foregroundColor isEqual:nil]) {
view.textColor = foregroundColor;
}
if (![backgroundColor isEqual:nil]) {
view.backgroundColor = backgroundColor;
}
[view sizeToFit];
[self.testArea addSubview:view];
}

- (IBAction)userTappedClearFields:(UIButton *)sender {
[self axetest_clearAllFields];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ @implementation GTXTestAnalyticsBaseTest {
+ (void)setUp {
[super setUp];
[GTXiLib installOnTestSuite:[GTXTestSuite suiteWithAllTestsInClass:self]
checks:@[checkFailsIfFailingClass]
checks:@[gCheckFailsIfFailingClass]
elementBlacklists:@[]];
[GTXTestViewController addElementToTestArea:
[[GTXTestFailingClass alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]];
Expand Down
6 changes: 3 additions & 3 deletions Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@
/**
Check that fails if the element is of Class @c GTXTestFailingClass.
*/
extern id<GTXChecking> checkFailsIfFailingClass;
extern id<GTXChecking> gCheckFailsIfFailingClass;

/**
Check that always passes.
*/
extern id<GTXChecking> alwaysFail;
extern id<GTXChecking> gAlwaysFail;

/**
Check that always fails.
*/
extern id<GTXChecking> alwaysPass;
extern id<GTXChecking> gAlwaysPass;

/**
Base test for all GTXiLib functional/integration tests used to setup GTXiLib and capture check
Expand Down
18 changes: 9 additions & 9 deletions Tests/FunctionalTests/TestApp/TestSources/GTXTestBaseTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,26 @@
#import "GTXTestViewController.h"

static NSInteger gFailureCount = 0;
id<GTXChecking> checkFailsIfFailingClass;
id<GTXChecking> alwaysFail;
id<GTXChecking> alwaysPass;
id<GTXChecking> gCheckFailsIfFailingClass;
id<GTXChecking> gAlwaysFail;
id<GTXChecking> gAlwaysPass;

@implementation GTXTestBaseTest

+ (void)setUp {
[super setUp];

checkFailsIfFailingClass = [GTXiLib checkWithName:@"checkFailsIfFailingClass"
block:^BOOL(id element, GTXErrorRefType errorOrNil) {
gCheckFailsIfFailingClass = [GTXiLib checkWithName:@"gCheckFailsIfFailingClass"
block:^BOOL(id element, GTXErrorRefType errorOrNil) {
return ![element isKindOfClass:[GTXTestFailingClass class]];
}];

alwaysFail = [GTXiLib checkWithName:@"AlwaysFail"
block:^BOOL(id element, GTXErrorRefType errorOrNil) {
gAlwaysFail = [GTXiLib checkWithName:@"gAlwaysFail"
block:^BOOL(id element, GTXErrorRefType errorOrNil) {
return NO;
}];
alwaysPass = [GTXiLib checkWithName:@"AlwaysPass"
block:^BOOL(id element, GTXErrorRefType errorOrNil) {
gAlwaysPass = [GTXiLib checkWithName:@"gAlwaysPass"
block:^BOOL(id element, GTXErrorRefType errorOrNil) {
return YES;
}];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ @implementation GTXTestFailingClassFailCheck
+ (void)setUp {
[super setUp];
[GTXiLib installOnTestSuite:[GTXTestSuite suiteWithAllTestsInClass:self]
checks:@[checkFailsIfFailingClass]
checks:@[gCheckFailsIfFailingClass]
elementBlacklists:@[]];
[GTXTestViewController addElementToTestArea:
[[GTXTestFailingClass alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ + (void)setUp {
[super setUp];
[GTXiLib installOnTestSuite:[GTXTestSuite suiteWithClass:self
andTests:@selector(testMiddle), nil]
checks:@[alwaysFail]
checks:@[gAlwaysFail]
elementBlacklists:@[]];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ @implementation GTXTestGtxCanDetectFailures
+ (void)setUp {
[super setUp];
[GTXiLib installOnTestSuite:[GTXTestSuite suiteWithAllTestsInClass:self]
checks:@[alwaysFail]
checks:@[gAlwaysFail]
elementBlacklists:@[]];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ + (void)setUp {
GTXTestSuite *suite = [GTXTestSuite suiteWithAllTestsFromAllClassesInheritedFromClass:
[GTXTestSuperClassWithATestMethod class]];
[GTXiLib installOnTestSuite:suite
checks:@[alwaysFail]
checks:@[gAlwaysFail]
elementBlacklists:@[]];
gTeardownCalledAtleastOnce = NO;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ + (void)setUp {
NSArray *blacklist =
@[[GTXBlacklistFactory blacklistWithClassName:className]];
[GTXiLib installOnTestSuite:[GTXTestSuite suiteWithAllTestsInClass:self]
checks:@[checkFailsIfFailingClass]
checks:@[gCheckFailsIfFailingClass]
elementBlacklists:blacklist];
[GTXTestViewController addElementToTestArea:
[[GTXTestFailingClass alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]];
Expand Down
Loading

0 comments on commit 36f0c45

Please sign in to comment.