/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #import #import "FBIntegrationTestCase.h" #import "FBElementUtils.h" #import "FBExceptions.h" #import "FBTestMacros.h" #import "XCUIElement.h" #import "XCUIElement+FBFind.h" #import "XCUIElement+FBUID.h" #import "FBXCElementSnapshotWrapper+Helpers.h" #import "XCUIElement+FBIsVisible.h" #import "XCUIElement+FBClassChain.h" #import "XCUIElement+FBResolve.h" #import "FBXPath.h" #import "FBXCodeCompatibility.h" @interface XCUIElementFBFindTests : FBIntegrationTestCase @property (nonatomic, strong) XCUIElement *testedView; @end @implementation XCUIElementFBFindTests - (void)setUp { [super setUp]; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self launchApplication]; }); self.testedView = self.testedApplication.otherElements[@"MainView"]; FBAssertWaitTillBecomesTrue(self.testedView.exists); } - (void)testDescendantsWithClassName { NSSet *expectedLabels = [NSSet setWithArray:@[ @"Alerts", @"Attributes", @"Scrolling", @"Deadlock app", @"Touch", ]]; NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingClassName:@"XCUIElementTypeButton" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, expectedLabels.count); NSArray *labels = [matchingSnapshots valueForKeyPath:@"@distinctUnionOfObjects.label"]; XCTAssertEqualObjects([NSSet setWithArray:labels], expectedLabels); NSArray *types = [matchingSnapshots valueForKeyPath:@"@distinctUnionOfObjects.elementType"]; XCTAssertEqual(types.count, 1, @"matchingSnapshots should contain only one type"); XCTAssertEqualObjects(types.lastObject, @(XCUIElementTypeButton), @"matchingSnapshots should contain only one type"); } - (void)testSingleDescendantWithClassName { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingClassName:@"XCUIElementTypeButton" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton); } - (void)testDescendantsWithIdentifier { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingIdentifier:@"Alerts" shouldReturnAfterFirstMatch:NO]; int snapshotsCount = 2; XCTAssertEqual(matchingSnapshots.count, snapshotsCount); XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts"); NSArray *selfElementsById = [matchingSnapshots.lastObject fb_descendantsMatchingIdentifier:@"Alerts" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(selfElementsById.count, 1); } - (void)testSingleDescendantWithIdentifier { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingIdentifier:@"Alerts" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts"); } - (void)testStableInstance { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingIdentifier:@"Alerts" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 1); for (XCUIElement *el in @[ matchingSnapshots.lastObject, [matchingSnapshots.lastObject fb_stableInstanceWithUid:[matchingSnapshots.lastObject fb_uid]] ]) { XCTAssertEqual(el.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(el.label, @"Alerts"); } } - (void)testSingleDescendantWithMissingIdentifier { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingIdentifier:@"blabla" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 0); } - (void)testDescendantsWithXPathQuery { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@label='Alerts']" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts"); } - (void)testSelfWithXPathQuery { NSArray *matchingSnapshots = [self.testedApplication fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeApplication" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeApplication); } - (void)testSingleDescendantWithXPathQuery { NSArray *matchingSnapshots = [self.testedApplication fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@hittable='true']" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 1); XCUIElement *matchingSnapshot = [matchingSnapshots firstObject]; XCTAssertNotNil(matchingSnapshot); XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(matchingSnapshot.label, @"Alerts"); } - (void)testSingleDescendantWithXPathQueryNoMatches { XCUIElement *matchingSnapshot = [[self.testedView fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButtonnn" shouldReturnAfterFirstMatch:YES] firstObject]; XCTAssertNil(matchingSnapshot); } - (void)testSingleLastDescendantWithXPathQuery { XCUIElement *matchingSnapshot = [[self.testedView fb_descendantsMatchingXPathQuery:@"(//XCUIElementTypeButton)[last()]" shouldReturnAfterFirstMatch:YES] firstObject]; XCTAssertNotNil(matchingSnapshot); XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton); } - (void)testDescendantsWithXPathQueryNoMatches { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@label='Alerts1']" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 0); } - (void)testDescendantsWithComplexXPathQuery { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingXPathQuery:@"//*[@label='Scrolling']/preceding::*[boolean(string(@label))]" shouldReturnAfterFirstMatch:NO]; int snapshotsCount = 6; XCTAssertEqual(matchingSnapshots.count, snapshotsCount); } - (void)testDescendantsWithWrongXPathQuery { XCTAssertThrowsSpecificNamed([self.testedView fb_descendantsMatchingXPathQuery:@"//*[blabla(@label, Scrolling')]" shouldReturnAfterFirstMatch:NO], NSException, FBInvalidXPathException); } - (void)testFirstDescendantWithWrongXPathQuery { XCTAssertThrowsSpecificNamed([self.testedView fb_descendantsMatchingXPathQuery:@"//*[blabla(@label, Scrolling')]" shouldReturnAfterFirstMatch:YES], NSException, FBInvalidXPathException); } - (void)testVisibleDescendantWithXPathQuery { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@name='Alerts' and @enabled='true' and @visible='true']" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton); XCTAssertTrue(matchingSnapshots.lastObject.isEnabled); XCTAssertTrue(matchingSnapshots.lastObject.fb_isVisible); XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts"); } - (void)testDescendantsWithPredicateString { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label = 'Alerts'"]; NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingPredicate:predicate shouldReturnAfterFirstMatch:NO]; int snapshotsCount = 2; XCTAssertEqual(matchingSnapshots.count, snapshotsCount); XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts"); NSPredicate *selfPredicate = [NSPredicate predicateWithFormat:@"label == 'Alerts'"]; NSArray *selfElementsByPredicate = [matchingSnapshots.lastObject fb_descendantsMatchingPredicate:selfPredicate shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(selfElementsByPredicate.count, 1); } - (void)testSelfWithPredicateString { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"type == 'XCUIElementTypeApplication'"]; NSArray *matchingSnapshots = [self.testedApplication fb_descendantsMatchingPredicate:predicate shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeApplication); } - (void)testSingleDescendantWithPredicateString { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"type = 'XCUIElementTypeButton'"]; NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingPredicate:predicate shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton); } - (void)testSingleDescendantWithPredicateStringByIndex { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"type == 'XCUIElementTypeButton' AND index == 2"]; NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingPredicate:predicate shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton); } - (void)testDescendantsWithPropertyStrict { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingProperty:@"label" value:@"Alert" partialSearch:NO]; XCTAssertEqual(matchingSnapshots.count, 0); matchingSnapshots = [self.testedView fb_descendantsMatchingProperty:@"label" value:@"Alerts" partialSearch:NO]; int snapshotsCount = 2; XCTAssertEqual(matchingSnapshots.count, snapshotsCount); XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts"); } - (void)testGlobalWithPropertyStrict { NSArray *matchingSnapshots = [self.testedApplication fb_descendantsMatchingProperty:@"label" value:@"Alert" partialSearch:NO]; XCTAssertEqual(matchingSnapshots.count, 0); matchingSnapshots = [self.testedApplication fb_descendantsMatchingProperty:@"label" value:@"Alerts" partialSearch:NO]; int snapshotsCount = 2; XCTAssertEqual(matchingSnapshots.count, snapshotsCount); XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts"); } - (void)testDescendantsWithPropertyPartial { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingProperty:@"label" value:@"Alerts" partialSearch:NO]; int snapshotsCount = 2; XCTAssertEqual(matchingSnapshots.count, snapshotsCount); XCTAssertEqual(matchingSnapshots.firstObject.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(matchingSnapshots.lastObject.label, @"Alerts"); } - (void)testDescendantsWithClassChain { NSArray *matchingSnapshots; NSString *queryString =@"XCUIElementTypeWindow/XCUIElementTypeOther/**/XCUIElementTypeButton"; matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 5); // /XCUIElementTypeButton for (XCUIElement *matchingSnapshot in matchingSnapshots) { XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton); } } - (void)testDescendantsWithClassChainWithIndex { NSArray *matchingSnapshots; // iPhone NSString *queryString = @"XCUIElementTypeWindow/*/*/*/*[2]/*/*/XCUIElementTypeButton"; matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString shouldReturnAfterFirstMatch:NO]; if (matchingSnapshots.count == 0) { // iPad queryString = @"XCUIElementTypeWindow/*/*/*/*/*[2]/*/*/XCUIElementTypeButton"; matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString shouldReturnAfterFirstMatch:NO]; } XCTAssertEqual(matchingSnapshots.count, 5); // /XCUIElementTypeButton for (XCUIElement *matchingSnapshot in matchingSnapshots) { XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton); } } - (void)testDescendantsWithClassChainAndPredicates { NSArray *matchingSnapshots; NSString *queryString = @"XCUIElementTypeWindow/**/XCUIElementTypeButton[`label BEGINSWITH 'A'`]"; matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 2); XCTAssertEqualObjects([matchingSnapshots firstObject].label, @"Alerts"); XCTAssertEqualObjects([matchingSnapshots lastObject].label, @"Attributes"); } - (void)testDescendantsWithIndirectClassChainAndPredicates { NSString *queryString = @"XCUIElementTypeWindow/**/XCUIElementTypeButton[`label BEGINSWITH 'A'`]"; NSArray *simpleQueryMatches = [self.testedApplication fb_descendantsMatchingClassChain:queryString shouldReturnAfterFirstMatch:NO]; NSArray *deepQueryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"XCUIElementTypeWindow/**/XCUIElementTypeButton[`label BEGINSWITH 'A'`]" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(simpleQueryMatches.count, deepQueryMatches.count); XCTAssertEqualObjects([simpleQueryMatches firstObject].label, [deepQueryMatches firstObject].label); XCTAssertEqualObjects([simpleQueryMatches lastObject].label, [deepQueryMatches lastObject].label); } - (void)testClassChainWithDescendantPredicate { NSArray *simpleQueryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"XCUIElementTypeWindow/*/*[1]" shouldReturnAfterFirstMatch:NO]; NSArray *predicateQueryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"XCUIElementTypeWindow/*/*[$type == 'XCUIElementTypeButton' AND label BEGINSWITH 'A'$]" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(simpleQueryMatches.count, predicateQueryMatches.count); XCTAssertEqual([simpleQueryMatches firstObject].elementType, [predicateQueryMatches firstObject].elementType); XCTAssertEqual([simpleQueryMatches lastObject].elementType, [predicateQueryMatches lastObject].elementType); } - (void)testSingleDescendantWithComplexIndirectClassChain { NSArray *queryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"**/*/XCUIElementTypeButton[2]" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(queryMatches.count, 1); XCTAssertEqual(queryMatches.lastObject.elementType, XCUIElementTypeButton); XCTAssertEqualObjects(queryMatches.lastObject.label, @"Deadlock app"); } - (void)testSingleDescendantWithComplexIndirectClassChainAndZeroMatches { NSArray *queryMatches = [self.testedApplication fb_descendantsMatchingClassChain:@"**/*/XCUIElementTypeWindow" shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(queryMatches.count, 0); } - (void)testDescendantsWithClassChainAndPredicatesAndIndexes { NSArray *matchingSnapshots; NSString *queryString = @"XCUIElementTypeWindow[`name != 'bla'`]/**/XCUIElementTypeButton[`label BEGINSWITH \"A\"`][1]"; matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqualObjects([matchingSnapshots firstObject].label, @"Alerts"); } - (void)testSingleDescendantWithClassChain { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingClassChain:@"XCUIElementTypeButton" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton); XCTAssertTrue([matchingSnapshots.lastObject.label isEqualToString:@"Alerts"]); } - (void)testSingleDescendantWithClassChainAndNegativeIndex { NSArray *matchingSnapshots; matchingSnapshots = [self.testedView fb_descendantsMatchingClassChain:@"XCUIElementTypeButton[-1]" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton); XCTAssertTrue([matchingSnapshots.lastObject.label isEqualToString:@"Touch"]); matchingSnapshots = [self.testedView fb_descendantsMatchingClassChain:@"XCUIElementTypeButton[-10]" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 0); } - (void)testInvalidQueryWithClassChain { XCTAssertThrowsSpecificNamed([self.testedView fb_descendantsMatchingClassChain:@"NoXCUIElementTypePrefix" shouldReturnAfterFirstMatch:YES], NSException, FBClassChainQueryParseException); } - (void)testHandleInvalidQueryWithClassChainAsNoElementWithoutError { NSArray *matchingSnapshots = [self.testedView fb_descendantsMatchingClassChain:@"XCUIElementTypeBlabla" shouldReturnAfterFirstMatch:YES]; XCTAssertEqual(matchingSnapshots.count, 0); } - (void)testClassChainWithInvalidPredicate { XCTAssertThrowsSpecificNamed([self.testedApplication fb_descendantsMatchingClassChain:@"XCUIElementTypeWindow[`bla != 'bla'`]" shouldReturnAfterFirstMatch:NO], NSException, FBUnknownAttributeException);; } @end @interface XCUIElementFBFindTests_AttributesPage : FBIntegrationTestCase @end @implementation XCUIElementFBFindTests_AttributesPage - (void)setUp { [super setUp]; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self launchApplication]; [self goToAttributesPage]; }); } - (void)testNestedQueryWithClassChain { NSString *queryString = @"XCUIElementTypePicker"; FBAssertWaitTillBecomesTrue(self.testedApplication.buttons[@"Button"].fb_isVisible); XCUIElement *datePicker = [self.testedApplication descendantsMatchingType:XCUIElementTypeDatePicker].allElementsBoundByIndex.firstObject; NSArray *matches = [datePicker fb_descendantsMatchingClassChain:queryString shouldReturnAfterFirstMatch:NO]; XCTAssertEqual(matches.count, 1); XCUIElementType expectedType = XCUIElementTypePicker; XCTAssertEqual([matches firstObject].elementType, expectedType); } @end @interface XCUIElementFBFindTests_ScrollPage : FBIntegrationTestCase @end @implementation XCUIElementFBFindTests_ScrollPage - (void)setUp { [super setUp]; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self launchApplication]; [self goToScrollPageWithCells:YES]; }); } - (void)testInvisibleDescendantWithXPathQuery { NSArray *matchingSnapshots = [self.testedApplication fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeStaticText[@visible='false']" shouldReturnAfterFirstMatch:NO]; XCTAssertGreaterThan(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeStaticText); XCTAssertFalse(matchingSnapshots.lastObject.fb_isVisible); } - (void)testNonHittableDescendantWithXPathQuery { NSArray *matchingSnapshots = [self.testedApplication fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeStaticText[@hittable='false']" shouldReturnAfterFirstMatch:NO]; XCTAssertGreaterThan(matchingSnapshots.count, 1); XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeStaticText); XCTAssertFalse(matchingSnapshots.lastObject.fb_isVisible); } @end