Can I check if a view exists on the screen with KIF?
Asked Answered
S

8

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"];
}
Sociability answered 13/9, 2013 at 20:52 Comment(0)
P
19

I would suggest to try this approach:

if([[[UIApplication sharedApplication] keyWindow] accessibilityElementWithLabel:@"backButton"] != nil) {
     [tester tapViewWithAccessibilityLabel:@"backButton"];
} else {
     [tester tapViewWithAccessibilityLabel:@"Logout"];
}
Philpot answered 10/2, 2014 at 6:53 Comment(1)
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.Rheta
P
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.

Paracelsus answered 26/2, 2014 at 14:8 Comment(2)
This worked for me, but remember to put it within an @autorelease{ ... }.Godmother
Added necessary header imports to make the code compile correctly.Hysterectomize
P
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;

Propagation answered 27/1, 2016 at 16:34 Comment(0)
P
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

    }
}
Parisparish answered 13/2, 2017 at 10:40 Comment(0)
P
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.");
}
Psychopathy answered 3/10, 2013 at 13:32 Comment(1)
This doesn't work because the exception is handled first my KIF's code and never reaches the @try block in your code.Protactinium
E
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;
}
Epidemic answered 11/2, 2014 at 15:26 Comment(1)
Not work for Xocde8.2. The test stops at the line: tester waitForViewWithAccessibilityLabel:accessibilityLabel.Parisparish
L
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!

Linesman answered 13/5, 2014 at 10:34 Comment(0)
B
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()
}
Barthol answered 7/2, 2020 at 10:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.