Skip to content

Commit

Permalink
Merge pull request #794 from RoyalPineapple/kifuiviewtestactor
Browse files Browse the repository at this point in the history
Add a new test actor, KIFUIViewTestActor.
  • Loading branch information
mikelupo committed Mar 3, 2016
2 parents 4ebc8c5 + dc859d1 commit 59fc98a
Show file tree
Hide file tree
Showing 41 changed files with 2,655 additions and 113 deletions.
18 changes: 18 additions & 0 deletions Additions/NSPredicate+KIFAdditions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// NSPredicate+KIFAdditions.h
// KIF
//
// Created by Alex Odawa on 2/3/15.
//
//

#import <Foundation/Foundation.h>

@interface NSPredicate (KIFAdditions)

@property NSString *kifPredicateDescription;

- (NSArray *)flatten;
- (NSCompoundPredicate *)minusSubpredicatesFrom:(NSPredicate *)otherPredicate;

@end
71 changes: 71 additions & 0 deletions Additions/NSPredicate+KIFAdditions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// NSPredicate+KIFAdditions.m
// KIF
//
// Created by Alex Odawa on 2/3/15.
//
//

#import <objc/runtime.h>
#import "NSPredicate+KIFAdditions.h"

@implementation NSPredicate (KIFAdditions)

- (NSArray *)flatten
{
NSMutableArray *result = [[NSMutableArray alloc] init];

if ([self isKindOfClass:[NSCompoundPredicate class]]) {
for (NSPredicate *predicate in ((NSCompoundPredicate *)self).subpredicates) {
[result addObjectsFromArray:[predicate flatten]];
}
} else {
[result addObject:self];
}

return result;
}

- (NSCompoundPredicate *)minusSubpredicatesFrom:(NSPredicate *)otherPredicate;
{
if (self == otherPredicate) {
return nil;
}
NSMutableSet *subpredicates = [NSMutableSet setWithArray:[self flatten]];
NSMutableSet *otherSubpredicates = [NSMutableSet setWithArray:[otherPredicate flatten]];
[subpredicates minusSet:otherSubpredicates];
return [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType
subpredicates:[subpredicates allObjects]];
}

- (void)setKifPredicateDescription:(NSString *)description;
{
NSString *desc = description.copy;
objc_setAssociatedObject(self, @selector(kifPredicateDescription), desc, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)kifPredicateDescription;
{
id object = objc_getAssociatedObject(self, @selector(kifPredicateDescription));
if (object) {
return object;
}
// Compound predicates containing subpredicates with the kifPredicateDescription set should still get our pretty formatting.
if ([self isKindOfClass:[NSCompoundPredicate class]]) {
NSArray *subpredicates = [self flatten];
NSString *description = @"";

for (NSPredicate *predicate in subpredicates) {
if (description.length > 0) {
description = [description stringByAppendingString:@", "];
}
description = [description stringByAppendingString:predicate.kifPredicateDescription];
}

return description;
}

return self.description;
}

@end
16 changes: 16 additions & 0 deletions Additions/NSString+KIFAdditions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// NSString+KIFAdditions.h
// KIF
//
// Created by Alex Odawa on 1/28/16.
//
//

#import <Foundation/Foundation.h>

#pragma mark - NSString
@interface NSString (KIFAdditions)

- (BOOL)KIF_isEqualToStringOrAttributedString:(id)aString;

@end
24 changes: 24 additions & 0 deletions Additions/NSString+KIFAdditions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// NSString+KIFAdditions.m
// KIF
//
// Created by Alex Odawa on 1/28/16.
//
//

#import "NSString+KIFAdditions.h"

#pragma mark - NSString
@implementation NSString (KIFAdditions)

- (BOOL)KIF_isEqualToStringOrAttributedString:(id)aString;
{
// Somtimes Accessibility Elements will return an AXAttributedString.
// This compares the raw backing string against a vanilla NSString, ignoring any attributes.
if ([aString respondsToSelector:@selector(string)]) {
return [self isEqualToString:[(id)aString string]];
}
return [self isEqualToString:aString];
}

@end
6 changes: 6 additions & 0 deletions Additions/UIAccessibilityElement-KIFAdditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@
*/
+ (UIView *)viewContainingAccessibilityElement:(UIAccessibilityElement *)element tappable:(BOOL)mustBeTappable error:(NSError **)error;

/*!
@abstract Returns a human readable string of UIAccessiblityTrait names, derived from UIAccessibilityConstants.h.
@param traits The accessibility traits to list.
*/
+ (NSString *)stringFromAccessibilityTraits:(UIAccessibilityTraits)traits;

@end
117 changes: 116 additions & 1 deletion Additions/UIAccessibilityElement-KIFAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// which Square, Inc. licenses this file to you.

#import "NSError-KIFAdditions.h"
#import "NSPredicate+KIFAdditions.h"
#import "UIAccessibilityElement-KIFAdditions.h"
#import "UIApplication-KIFAdditions.h"
#import "UIScrollView-KIFAdditions.h"
Expand Down Expand Up @@ -62,7 +63,7 @@ + (BOOL)accessibilityElement:(out UIAccessibilityElement **)foundElement view:(o

if (!element) {
if (error) {
*error = [NSError KIFErrorWithFormat:@"Could not find view matching: %@", predicate];
*error = [self errorForFailingPredicate:predicate];
}
return NO;
}
Expand Down Expand Up @@ -174,4 +175,118 @@ + (UIView *)viewContainingAccessibilityElement:(UIAccessibilityElement *)element
return view;
}

+ (NSError *)errorForFailingPredicate:(NSPredicate*)failingPredicate;
{
NSPredicate *closestMatchingPredicate = [self findClosestMatchingPredicate:failingPredicate];
if (closestMatchingPredicate) {
return [NSError KIFErrorWithFormat:@"Found element with %@ but not %@", \
closestMatchingPredicate.kifPredicateDescription, \
[failingPredicate minusSubpredicatesFrom:closestMatchingPredicate].kifPredicateDescription];
}
return [NSError KIFErrorWithFormat:@"Could not find element with %@", failingPredicate.kifPredicateDescription];
}

+ (NSPredicate *)findClosestMatchingPredicate:(NSPredicate *)aPredicate;
{
if (!aPredicate) {
return nil;
}

UIAccessibilityElement *match = [[UIApplication sharedApplication] accessibilityElementMatchingBlock:^BOOL (UIAccessibilityElement *element) {
return [aPredicate evaluateWithObject:element];
}];
if (match) {
return aPredicate;
}

// Breadth-First algorithm to match as many subpredicates as possible
NSMutableArray *queue = [NSMutableArray arrayWithObject:aPredicate];
while (queue.count > 0) {
// Dequeuing
NSPredicate *predicate = [queue firstObject];
[queue removeObject:predicate];

// Remove one subpredicate at a time an then check if an element would match this resulting predicate
for (NSPredicate *subpredicate in [predicate flatten]) {
NSPredicate *predicateMinusOneCondition = [predicate minusSubpredicatesFrom:subpredicate];
if (predicateMinusOneCondition) {
UIAccessibilityElement *match = [[UIApplication sharedApplication] accessibilityElementMatchingBlock:^BOOL (UIAccessibilityElement *element) {
return [predicateMinusOneCondition evaluateWithObject:element];
}];
if (match) {
return predicateMinusOneCondition;
}
[queue addObject:predicateMinusOneCondition];
}
}
}
return nil;
}

+ (NSString *)stringFromAccessibilityTraits:(UIAccessibilityTraits)traits;
{
if (traits == UIAccessibilityTraitNone) {
return @"UIAccessibilityTraitNone";
}

NSString *string = @"";

NSArray *allTraits = @[
@(UIAccessibilityTraitButton),
@(UIAccessibilityTraitLink),
@(UIAccessibilityTraitHeader),
@(UIAccessibilityTraitSearchField),
@(UIAccessibilityTraitImage),
@(UIAccessibilityTraitSelected),
@(UIAccessibilityTraitPlaysSound),
@(UIAccessibilityTraitKeyboardKey),
@(UIAccessibilityTraitStaticText),
@(UIAccessibilityTraitSummaryElement),
@(UIAccessibilityTraitNotEnabled),
@(UIAccessibilityTraitUpdatesFrequently),
@(UIAccessibilityTraitStartsMediaSession),
@(UIAccessibilityTraitAdjustable),
@(UIAccessibilityTraitAllowsDirectInteraction),
@(UIAccessibilityTraitCausesPageTurn)
];

NSArray *traitNames = @[
@"UIAccessibilityTraitButton",
@"UIAccessibilityTraitLink",
@"UIAccessibilityTraitHeader",
@"UIAccessibilityTraitSearchField",
@"UIAccessibilityTraitImage",
@"UIAccessibilityTraitSelected",
@"UIAccessibilityTraitPlaysSound",
@"UIAccessibilityTraitKeyboardKey",
@"UIAccessibilityTraitStaticText",
@"UIAccessibilityTraitSummaryElement",
@"UIAccessibilityTraitNotEnabled",
@"UIAccessibilityTraitUpdatesFrequently",
@"UIAccessibilityTraitStartsMediaSession",
@"UIAccessibilityTraitAdjustable",
@"UIAccessibilityTraitAllowsDirectInteraction",
@"UIAccessibilityTraitCausesPageTurn"
];


for (NSNumber *trait in allTraits) {
if ((traits & trait.longLongValue) == trait.longLongValue) {
NSString *name = [traitNames objectAtIndex:[allTraits indexOfObject:trait]];
if (string.length > 0) {
string = [string stringByAppendingString:@", "];
}
string = [string stringByAppendingString:name];
traits &= ~trait.longLongValue;
}
}
if (traits != UIAccessibilityTraitNone) {
if (string.length > 0) {
string = [string stringByAppendingString:@", "];
}
string = [string stringByAppendingFormat:@"UNKNOWN ACCESSIBILITY TRAIT: %llu", traits];
}
return string;
}

@end
3 changes: 3 additions & 0 deletions Classes/KIF.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@
#import "KIFUITestActor.h"
#import "KIFUITestActor-ConditionalTests.h"

#import "KIFUIViewTestActor.h"
#import "KIFUIObject.h"

#import "XCTestCase-KIFAdditions.h"
2 changes: 0 additions & 2 deletions Classes/KIFTestActor.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ return KIFTestStepResultWait; \
} \
})


/*!
@enum KIFTestStepResult
@abstract Result codes from a test step.
Expand Down Expand Up @@ -98,7 +97,6 @@ typedef void (^KIFTestCompletionBlock)(KIFTestStepResult result, NSError *error)
- (void)runBlock:(KIFTestExecutionBlock)executionBlock timeout:(NSTimeInterval)timeout;
- (void)runBlock:(KIFTestExecutionBlock)executionBlock;


/*!
@discussion Attempts to run the test block similar to -runBlock:complete:timeout: but does not halt the test on completion, instead returning NO on failure and providing an error description to the optional error parameter.
*/
Expand Down
18 changes: 18 additions & 0 deletions Classes/KIFUIObject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// KIFUIObject.h
// KIF
//
// Created by Alex Odawa on 1/26/15.
//
//

#import <UIKit/UIKit.h>

@interface KIFUIObject : NSObject

@property (nonatomic, weak, readonly) UIView *view;
@property (nonatomic, weak, readonly) UIAccessibilityElement *element;

- (instancetype)initWithElement:(UIAccessibilityElement *)element view:(UIView *)view;

@end
28 changes: 28 additions & 0 deletions Classes/KIFUIObject.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// KIFUIObject.m
// KIF
//
// Created by Alex Odawa on 1/26/15.
//
//

#import "KIFUIObject.h"


@implementation KIFUIObject

- (instancetype)initWithElement:(UIAccessibilityElement *)element view:(UIView *)view;
{
self = [super init];
if (self) {
_element = element;
_view = view;
}
return self;
}

- (NSString *)description;
{
return [NSString stringWithFormat:@"<%@;\n| element=%@;\n| | view=%@>", [super description], self.element, self.view];
}
@end
Loading

0 comments on commit 59fc98a

Please sign in to comment.