17

I am making a "before each" step and I am wanting to do steps to logout. I can't find anything about checking if an element exists before I try to touch it, then if it doesn't exist do something else. Is it possible to do this with KIF without having a reference to the object I want to check for?

Something like:

if([tester elementExistsWithAccesibilityLabel:@"backButton"])
{
    [tester tapViewWithAccessibilityLabel:@"backButton"];
}
else
{
    [tester tapViewwithAccesibilityLabel:@"Logout"];
}
SirRupertIII
  • 12,324
  • 20
  • 72
  • 121

8 Answers8

19

I would suggest to try this approach:

if([[[UIApplication sharedApplication] keyWindow] accessibilityElementWithLabel:@"backButton"] != nil) {
     [tester tapViewWithAccessibilityLabel:@"backButton"];
} else {
     [tester tapViewWithAccessibilityLabel:@"Logout"];
}
Lukas Spieß
  • 2,478
  • 2
  • 19
  • 24
user2738882
  • 1,161
  • 1
  • 20
  • 38
  • This doesn't work if the view is in the view hierarchy but is not currently viewable on the screen when another VC has been presented. – BergQuester Nov 30 '16 at 20:54
5

These methods do the trick. Add them to a category for KIFUITestActor.

#import "UIApplication-KIFAdditions.h" 
#import "UIAccessibilityElement-KIFAdditions.h"
#import "NSError-KIFAdditions.h"

- (BOOL)existsViewWithAccessibilityLabel:(NSString *)label
{
    UIView *view = nil;
    UIAccessibilityElement *element = nil;
    return [self existsAccessibilityElement:&element view:&view withLabel:label value:nil traits:UIAccessibilityTraitNone tappable:YES];
}

- (BOOL)existsAccessibilityElement:(UIAccessibilityElement **)element view:(out UIView **)view withLabel:(NSString *)label value:(NSString *)value traits:(UIAccessibilityTraits)traits tappable:(BOOL)mustBeTappable
{
    KIFTestStepResult (^executionBlock)(NSError **) = ^(NSError **error) {
        return [UIAccessibilityElement accessibilityElement:element view:view withLabel:label value:value traits:traits tappable:mustBeTappable error:error] ? KIFTestStepResultSuccess : KIFTestStepResultWait;
    };

    NSDate *startDate = [NSDate date];
    KIFTestStepResult result;
    NSError *error = nil;
    NSTimeInterval timeout = 10.0;

    while ((result = executionBlock(&error)) == KIFTestStepResultWait && -[startDate timeIntervalSinceNow] < timeout) {
        CFRunLoopRunInMode([[UIApplication sharedApplication] currentRunLoopMode] ?: kCFRunLoopDefaultMode, 0.1, false);
    }

    if (result == KIFTestStepResultWait) {
        error = [NSError KIFErrorWithUnderlyingError:error format:@"The step timed out after %.2f seconds: %@", timeout, error.localizedDescription];
        result = KIFTestStepResultFailure;
    }

    return (result == KIFTestStepResultSuccess) ? YES : NO;
}

It works for me very well.

Duncan Babbage
  • 19,972
  • 4
  • 56
  • 93
kuglisb
  • 131
  • 2
  • 5
3

In case anyone is still looking for an answer, there is a family of methods in KIF that does just that KIFUITestActor-ConditionalTests.h:

- (BOOL)tryFindingViewWithAccessibilityLabel:(NSString *)label error:(out NSError **)error;

- (BOOL)tryFindingViewWithAccessibilityLabel:(NSString *)label traits:(UIAccessibilityTraits)traits error:(out NSError **)error;

- (BOOL)tryFindingViewWithAccessibilityLabel:(NSString *)label value:(NSString *)value traits:(UIAccessibilityTraits)traits error:(out NSError **)error;

- (BOOL)tryFindingTappableViewWithAccessibilityLabel:(NSString *)label error:(out NSError **)error;

- (BOOL)tryFindingTappableViewWithAccessibilityLabel:(NSString *)label traits:(UIAccessibilityTraits)traits error:(out NSError **)error;

- (BOOL)tryFindingTappableViewWithAccessibilityLabel:(NSString *)label value:(NSString *)value traits:(UIAccessibilityTraits)traits error:(out NSError **)error;

- (BOOL)tryFindingAccessibilityElement:(out UIAccessibilityElement **)element view:(out UIView **)view withIdentifier:(NSString *)identifier tappable:(BOOL)mustBeTappable error:(out NSError **)error;

- (BOOL)tryFindingAccessibilityElement:(out UIAccessibilityElement **)element view:(out UIView **)view withElementMatchingPredicate:(NSPredicate *)predicate tappable:(BOOL)mustBeTappable error:(out NSError **)error;

If you're using the Accessibility Identifier additions (pod 'KIF/IdentifierTests') there's also the very handy equivalent method: - (BOOL) tryFindingViewWithAccessibilityIdentifier:(NSString *) accessibilityIdentifier;

user3099609
  • 2,318
  • 18
  • 20
2

For swift 3:

/** return true when the view is found */
func searchForElement(_ label:String) -> Bool{
    do {
        try tester().tryFindingView(withAccessibilityLabel: label)
        return true
    } catch {
        return false

    }
}
Bill Chan
  • 3,199
  • 36
  • 32
1

You could try something like this:

@try {
   if([tester waitForViewWithAccessibilityLabel:@"backButton"])
   {
      [tester tapViewWithAccessibilityLabel:@"backButton"];
   }
@catch (NSException *exception )
{
   [tester tapViewwithAccesibilityLabel:@"Logout"];
}
@finally 
{
   NSLOG(@"User logged out.");
}
Ru Cindrea
  • 703
  • 2
  • 5
  • 16
  • 2
    This doesn't work because the exception is handled first my KIF's code and never reaches the `@try` block in your code. – Xander Dunn May 17 '14 at 01:58
0

I made this function that seems to do the trick in a pinch, but it would have some issues if multiple integration tests were simultaneously relying on the global default timeout. So far, seems to work for me.

static CGFloat CPKIFDefaultTimeout = 2

- (BOOL)elementExistsWithAccessibilityLabel:(NSString *)accessibilityLabel {
    [KIFTestActor setDefaultTimeout:0.0];
    BOOL result = [tester waitForViewWithAccessibilityLabel:accessibilityLabel] ? YES : NO;
    [KIFTestActor CPKIFDefaultTimeout];
    return result;
}
  • Not work for Xocde8.2. The test stops at the line: tester waitForViewWithAccessibilityLabel:accessibilityLabel. – Bill Chan Nov 08 '16 at 12:53
0

Alright, none of these worked for me... However I have a solution

Checkout this gist.

What I've done is taken KIF's code and just removed their error checking - works like a charm for me.

This won't fail your tests when it can't find an element!

Max Chuquimia
  • 7,494
  • 2
  • 40
  • 59
0

If using the Swift you can simply use the viewTester().tryFindingView():

if viewTester().usingLabel("backButton").tryFindingView() {
    // back button exists
    viewTester().usingLabel("backButton").tap()
} else {
    // back button does not exist
    viewTester().usingLabel("Logout").tap()
}
Tadej Magajna
  • 2,765
  • 1
  • 25
  • 41