Skip to content

Commit 59fc98a

Browse files
committed
Merge pull request #794 from RoyalPineapple/kifuiviewtestactor
Add a new test actor, KIFUIViewTestActor.
2 parents 4ebc8c5 + dc859d1 commit 59fc98a

41 files changed

Lines changed: 2655 additions & 113 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// NSPredicate+KIFAdditions.h
3+
// KIF
4+
//
5+
// Created by Alex Odawa on 2/3/15.
6+
//
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
@interface NSPredicate (KIFAdditions)
12+
13+
@property NSString *kifPredicateDescription;
14+
15+
- (NSArray *)flatten;
16+
- (NSCompoundPredicate *)minusSubpredicatesFrom:(NSPredicate *)otherPredicate;
17+
18+
@end
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// NSPredicate+KIFAdditions.m
3+
// KIF
4+
//
5+
// Created by Alex Odawa on 2/3/15.
6+
//
7+
//
8+
9+
#import <objc/runtime.h>
10+
#import "NSPredicate+KIFAdditions.h"
11+
12+
@implementation NSPredicate (KIFAdditions)
13+
14+
- (NSArray *)flatten
15+
{
16+
NSMutableArray *result = [[NSMutableArray alloc] init];
17+
18+
if ([self isKindOfClass:[NSCompoundPredicate class]]) {
19+
for (NSPredicate *predicate in ((NSCompoundPredicate *)self).subpredicates) {
20+
[result addObjectsFromArray:[predicate flatten]];
21+
}
22+
} else {
23+
[result addObject:self];
24+
}
25+
26+
return result;
27+
}
28+
29+
- (NSCompoundPredicate *)minusSubpredicatesFrom:(NSPredicate *)otherPredicate;
30+
{
31+
if (self == otherPredicate) {
32+
return nil;
33+
}
34+
NSMutableSet *subpredicates = [NSMutableSet setWithArray:[self flatten]];
35+
NSMutableSet *otherSubpredicates = [NSMutableSet setWithArray:[otherPredicate flatten]];
36+
[subpredicates minusSet:otherSubpredicates];
37+
return [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType
38+
subpredicates:[subpredicates allObjects]];
39+
}
40+
41+
- (void)setKifPredicateDescription:(NSString *)description;
42+
{
43+
NSString *desc = description.copy;
44+
objc_setAssociatedObject(self, @selector(kifPredicateDescription), desc, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
45+
}
46+
47+
- (NSString *)kifPredicateDescription;
48+
{
49+
id object = objc_getAssociatedObject(self, @selector(kifPredicateDescription));
50+
if (object) {
51+
return object;
52+
}
53+
// Compound predicates containing subpredicates with the kifPredicateDescription set should still get our pretty formatting.
54+
if ([self isKindOfClass:[NSCompoundPredicate class]]) {
55+
NSArray *subpredicates = [self flatten];
56+
NSString *description = @"";
57+
58+
for (NSPredicate *predicate in subpredicates) {
59+
if (description.length > 0) {
60+
description = [description stringByAppendingString:@", "];
61+
}
62+
description = [description stringByAppendingString:predicate.kifPredicateDescription];
63+
}
64+
65+
return description;
66+
}
67+
68+
return self.description;
69+
}
70+
71+
@end

Additions/NSString+KIFAdditions.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// NSString+KIFAdditions.h
3+
// KIF
4+
//
5+
// Created by Alex Odawa on 1/28/16.
6+
//
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
#pragma mark - NSString
12+
@interface NSString (KIFAdditions)
13+
14+
- (BOOL)KIF_isEqualToStringOrAttributedString:(id)aString;
15+
16+
@end

Additions/NSString+KIFAdditions.m

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// NSString+KIFAdditions.m
3+
// KIF
4+
//
5+
// Created by Alex Odawa on 1/28/16.
6+
//
7+
//
8+
9+
#import "NSString+KIFAdditions.h"
10+
11+
#pragma mark - NSString
12+
@implementation NSString (KIFAdditions)
13+
14+
- (BOOL)KIF_isEqualToStringOrAttributedString:(id)aString;
15+
{
16+
// Somtimes Accessibility Elements will return an AXAttributedString.
17+
// This compares the raw backing string against a vanilla NSString, ignoring any attributes.
18+
if ([aString respondsToSelector:@selector(string)]) {
19+
return [self isEqualToString:[(id)aString string]];
20+
}
21+
return [self isEqualToString:aString];
22+
}
23+
24+
@end

Additions/UIAccessibilityElement-KIFAdditions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,10 @@
6666
*/
6767
+ (UIView *)viewContainingAccessibilityElement:(UIAccessibilityElement *)element tappable:(BOOL)mustBeTappable error:(NSError **)error;
6868

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

Additions/UIAccessibilityElement-KIFAdditions.m

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// which Square, Inc. licenses this file to you.
99

1010
#import "NSError-KIFAdditions.h"
11+
#import "NSPredicate+KIFAdditions.h"
1112
#import "UIAccessibilityElement-KIFAdditions.h"
1213
#import "UIApplication-KIFAdditions.h"
1314
#import "UIScrollView-KIFAdditions.h"
@@ -62,7 +63,7 @@ + (BOOL)accessibilityElement:(out UIAccessibilityElement **)foundElement view:(o
6263

6364
if (!element) {
6465
if (error) {
65-
*error = [NSError KIFErrorWithFormat:@"Could not find view matching: %@", predicate];
66+
*error = [self errorForFailingPredicate:predicate];
6667
}
6768
return NO;
6869
}
@@ -174,4 +175,118 @@ + (UIView *)viewContainingAccessibilityElement:(UIAccessibilityElement *)element
174175
return view;
175176
}
176177

178+
+ (NSError *)errorForFailingPredicate:(NSPredicate*)failingPredicate;
179+
{
180+
NSPredicate *closestMatchingPredicate = [self findClosestMatchingPredicate:failingPredicate];
181+
if (closestMatchingPredicate) {
182+
return [NSError KIFErrorWithFormat:@"Found element with %@ but not %@", \
183+
closestMatchingPredicate.kifPredicateDescription, \
184+
[failingPredicate minusSubpredicatesFrom:closestMatchingPredicate].kifPredicateDescription];
185+
}
186+
return [NSError KIFErrorWithFormat:@"Could not find element with %@", failingPredicate.kifPredicateDescription];
187+
}
188+
189+
+ (NSPredicate *)findClosestMatchingPredicate:(NSPredicate *)aPredicate;
190+
{
191+
if (!aPredicate) {
192+
return nil;
193+
}
194+
195+
UIAccessibilityElement *match = [[UIApplication sharedApplication] accessibilityElementMatchingBlock:^BOOL (UIAccessibilityElement *element) {
196+
return [aPredicate evaluateWithObject:element];
197+
}];
198+
if (match) {
199+
return aPredicate;
200+
}
201+
202+
// Breadth-First algorithm to match as many subpredicates as possible
203+
NSMutableArray *queue = [NSMutableArray arrayWithObject:aPredicate];
204+
while (queue.count > 0) {
205+
// Dequeuing
206+
NSPredicate *predicate = [queue firstObject];
207+
[queue removeObject:predicate];
208+
209+
// Remove one subpredicate at a time an then check if an element would match this resulting predicate
210+
for (NSPredicate *subpredicate in [predicate flatten]) {
211+
NSPredicate *predicateMinusOneCondition = [predicate minusSubpredicatesFrom:subpredicate];
212+
if (predicateMinusOneCondition) {
213+
UIAccessibilityElement *match = [[UIApplication sharedApplication] accessibilityElementMatchingBlock:^BOOL (UIAccessibilityElement *element) {
214+
return [predicateMinusOneCondition evaluateWithObject:element];
215+
}];
216+
if (match) {
217+
return predicateMinusOneCondition;
218+
}
219+
[queue addObject:predicateMinusOneCondition];
220+
}
221+
}
222+
}
223+
return nil;
224+
}
225+
226+
+ (NSString *)stringFromAccessibilityTraits:(UIAccessibilityTraits)traits;
227+
{
228+
if (traits == UIAccessibilityTraitNone) {
229+
return @"UIAccessibilityTraitNone";
230+
}
231+
232+
NSString *string = @"";
233+
234+
NSArray *allTraits = @[
235+
@(UIAccessibilityTraitButton),
236+
@(UIAccessibilityTraitLink),
237+
@(UIAccessibilityTraitHeader),
238+
@(UIAccessibilityTraitSearchField),
239+
@(UIAccessibilityTraitImage),
240+
@(UIAccessibilityTraitSelected),
241+
@(UIAccessibilityTraitPlaysSound),
242+
@(UIAccessibilityTraitKeyboardKey),
243+
@(UIAccessibilityTraitStaticText),
244+
@(UIAccessibilityTraitSummaryElement),
245+
@(UIAccessibilityTraitNotEnabled),
246+
@(UIAccessibilityTraitUpdatesFrequently),
247+
@(UIAccessibilityTraitStartsMediaSession),
248+
@(UIAccessibilityTraitAdjustable),
249+
@(UIAccessibilityTraitAllowsDirectInteraction),
250+
@(UIAccessibilityTraitCausesPageTurn)
251+
];
252+
253+
NSArray *traitNames = @[
254+
@"UIAccessibilityTraitButton",
255+
@"UIAccessibilityTraitLink",
256+
@"UIAccessibilityTraitHeader",
257+
@"UIAccessibilityTraitSearchField",
258+
@"UIAccessibilityTraitImage",
259+
@"UIAccessibilityTraitSelected",
260+
@"UIAccessibilityTraitPlaysSound",
261+
@"UIAccessibilityTraitKeyboardKey",
262+
@"UIAccessibilityTraitStaticText",
263+
@"UIAccessibilityTraitSummaryElement",
264+
@"UIAccessibilityTraitNotEnabled",
265+
@"UIAccessibilityTraitUpdatesFrequently",
266+
@"UIAccessibilityTraitStartsMediaSession",
267+
@"UIAccessibilityTraitAdjustable",
268+
@"UIAccessibilityTraitAllowsDirectInteraction",
269+
@"UIAccessibilityTraitCausesPageTurn"
270+
];
271+
272+
273+
for (NSNumber *trait in allTraits) {
274+
if ((traits & trait.longLongValue) == trait.longLongValue) {
275+
NSString *name = [traitNames objectAtIndex:[allTraits indexOfObject:trait]];
276+
if (string.length > 0) {
277+
string = [string stringByAppendingString:@", "];
278+
}
279+
string = [string stringByAppendingString:name];
280+
traits &= ~trait.longLongValue;
281+
}
282+
}
283+
if (traits != UIAccessibilityTraitNone) {
284+
if (string.length > 0) {
285+
string = [string stringByAppendingString:@", "];
286+
}
287+
string = [string stringByAppendingFormat:@"UNKNOWN ACCESSIBILITY TRAIT: %llu", traits];
288+
}
289+
return string;
290+
}
291+
177292
@end

Classes/KIF.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@
1313
#import "KIFUITestActor.h"
1414
#import "KIFUITestActor-ConditionalTests.h"
1515

16+
#import "KIFUIViewTestActor.h"
17+
#import "KIFUIObject.h"
18+
1619
#import "XCTestCase-KIFAdditions.h"

Classes/KIFTestActor.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ return KIFTestStepResultWait; \
5151
} \
5252
})
5353

54-
5554
/*!
5655
@enum KIFTestStepResult
5756
@abstract Result codes from a test step.
@@ -98,7 +97,6 @@ typedef void (^KIFTestCompletionBlock)(KIFTestStepResult result, NSError *error)
9897
- (void)runBlock:(KIFTestExecutionBlock)executionBlock timeout:(NSTimeInterval)timeout;
9998
- (void)runBlock:(KIFTestExecutionBlock)executionBlock;
10099

101-
102100
/*!
103101
@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.
104102
*/

Classes/KIFUIObject.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// KIFUIObject.h
3+
// KIF
4+
//
5+
// Created by Alex Odawa on 1/26/15.
6+
//
7+
//
8+
9+
#import <UIKit/UIKit.h>
10+
11+
@interface KIFUIObject : NSObject
12+
13+
@property (nonatomic, weak, readonly) UIView *view;
14+
@property (nonatomic, weak, readonly) UIAccessibilityElement *element;
15+
16+
- (instancetype)initWithElement:(UIAccessibilityElement *)element view:(UIView *)view;
17+
18+
@end

Classes/KIFUIObject.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// KIFUIObject.m
3+
// KIF
4+
//
5+
// Created by Alex Odawa on 1/26/15.
6+
//
7+
//
8+
9+
#import "KIFUIObject.h"
10+
11+
12+
@implementation KIFUIObject
13+
14+
- (instancetype)initWithElement:(UIAccessibilityElement *)element view:(UIView *)view;
15+
{
16+
self = [super init];
17+
if (self) {
18+
_element = element;
19+
_view = view;
20+
}
21+
return self;
22+
}
23+
24+
- (NSString *)description;
25+
{
26+
return [NSString stringWithFormat:@"<%@;\n| element=%@;\n| | view=%@>", [super description], self.element, self.view];
27+
}
28+
@end

0 commit comments

Comments
 (0)