forked from calabash/DeviceAgent.iOS
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request calabash#376 from agileseph/support_descendant_mat…
…cher [Support] add descendant matching query specifier
- Loading branch information
Showing
7 changed files
with
254 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
Server/AutomationActions/Query/Specifiers/QuerySpecifierByDescendantType.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
|
||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT license. | ||
|
||
#import <Foundation/Foundation.h> | ||
#import "QuerySpecifier.h" | ||
|
||
/** | ||
This specifier finds all descendants elements matching the descendant_type within the element matching the parent_type. | ||
## Usage: | ||
{ "descendant_element" : { "parent_type": "String", "descendant_type": "String" } } | ||
*/ | ||
@interface QuerySpecifierByDescendantType : QuerySpecifier<QuerySpecifier> | ||
@end |
35 changes: 35 additions & 0 deletions
35
Server/AutomationActions/Query/Specifiers/QuerySpecifierByDescendantType.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#import "QuerySpecifierByDescendantType.h" | ||
#import "JSONUtils.h" | ||
|
||
@implementation QuerySpecifierByDescendantType | ||
+ (NSString *)name { return @"descendant_element"; } | ||
|
||
- (XCUIElementQuery *)applyInternal:(XCUIElementQuery *)query { | ||
NSString *error_message = [NSString stringWithFormat:@"%@%@%@%@", | ||
@"Malformed descendant_element value. ", | ||
@"Expected dictionary like: ", | ||
@"{ 'descendant_type' = Button; 'parent_type' = Keyboard; }", | ||
[NSString stringWithFormat:@"but actual is '%@'", self.value]]; | ||
NSAssert([self.value isKindOfClass:[NSDictionary class]], error_message); | ||
|
||
NSDictionary *parameters = self.value; | ||
|
||
NSAssert(parameters.count == 2, | ||
@"Dictionary should have only 2 keys '%@' and '%@', but actual is '%@'", | ||
CBX_PARENT_TYPE_KEY, CBX_DESCENDANT_TYPE_KEY, parameters); | ||
NSAssert(parameters[CBX_PARENT_TYPE_KEY] != nil, | ||
@"Value for key '%@' should not be nil. Actual dictionary: %@", | ||
CBX_PARENT_TYPE_KEY, parameters); | ||
NSAssert(parameters[CBX_DESCENDANT_TYPE_KEY] != nil, | ||
@"Value for key '%@' should not be nil. Actual dictionary: %@", | ||
CBX_DESCENDANT_TYPE_KEY, parameters); | ||
|
||
XCUIElementType parentType = [JSONUtils elementTypeForString:parameters[CBX_PARENT_TYPE_KEY]]; | ||
XCUIElementQuery *contextQuery = [query matchingType:parentType identifier:nil]; | ||
|
||
XCUIElementType descendantType = [JSONUtils elementTypeForString:parameters[CBX_DESCENDANT_TYPE_KEY]]; | ||
XCUIElementQuery *resultQuery = [contextQuery descendantsMatchingType:descendantType]; | ||
|
||
return resultQuery; | ||
} | ||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
170 changes: 170 additions & 0 deletions
170
TestApp/DeviceAgentUnitTests/AutomationActions/Queries/QuerySpecifierTests.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
#import <XCTest/XCTest.h> | ||
#import "QueryConfiguration.h" | ||
#import "QueryFactory.h" | ||
#import "QuerySpecifier.h" | ||
#import "Application.h" | ||
#import "CBXConstants.h" | ||
#import "XCTest+CBXAdditions.h" | ||
#import "CBXServerUnitTestUmbrellaHeader.h" | ||
|
||
@interface QuerySpecifierTests : XCTestCase | ||
@end | ||
|
||
@implementation QuerySpecifierTests | ||
|
||
- (void)setUp { | ||
[super setUp]; | ||
} | ||
|
||
- (void)tearDown { | ||
[super tearDown]; | ||
} | ||
|
||
- (void)testQuerySpecifierByDescendantTypeThrowsExceptionForInvalidMainKeyCase { | ||
id invalidJson = @{@"descendantElement": @{@"parent_type" : @"Keyboard", | ||
@"descendant_type" : @"Button"}}; | ||
|
||
expect(^{ | ||
[QueryConfiguration withJSON:invalidJson validator:nil]; | ||
}).to.raise(@"InvalidArgumentException"); | ||
} | ||
|
||
- (void)testQuerySpecifierByDescendantTypeThrowsExceptionForInvalidParentKeyCase { | ||
// [Set Up] Stage 0: prepare query with invalid config | ||
id invalidJson = @{@"descendant_element": @{@"parent_typeeeee" : @"Keyboard", | ||
@"descendant_type" : @"Button"}}; | ||
QueryConfiguration *queryConfig = [QueryConfiguration withJSON:invalidJson validator:nil]; | ||
|
||
Query *query = [QueryFactory queryWithQueryConfiguration:queryConfig]; | ||
|
||
// [Preconditions] Stage 1: prepare mock objects for app and query | ||
id appMock = OCMClassMock([Application class]); | ||
id uiAppMock = OCMClassMock([XCUIApplication class]); | ||
|
||
id queryMock = OCMClassMock([XCUIElementQuery class]); | ||
|
||
// [Preconditions] Stage 1: mock methods called in [query execute] | ||
OCMStub([uiAppMock cbxQueryForDescendantsOfAnyType]).andReturn(queryMock); | ||
OCMStub([appMock currentApplication]).andReturn(uiAppMock); | ||
|
||
// [Check] Stage 2: throw Exception in case parent type key is malformed | ||
expect(^{ | ||
[query execute]; | ||
}).to.raise(@"NSInternalInconsistencyException"); | ||
} | ||
|
||
- (void)testQuerySpecifierByDescendantTypeThrowsExceptionForInvalidDescendantKeyCase { | ||
// [Set Up] Stage 0: prepare query with invalid config | ||
id invalidJson = @{@"descendant_element": @{@"parent_type" : @"Keyboard", | ||
@"descendantType" : @"Button"}}; | ||
QueryConfiguration *queryConfig = [QueryConfiguration withJSON:invalidJson validator:nil]; | ||
|
||
Query *query = [QueryFactory queryWithQueryConfiguration:queryConfig]; | ||
|
||
// [Preconditions] Stage 1: prepare mock objects for app and query | ||
id appMock = OCMClassMock([Application class]); | ||
id uiAppMock = OCMClassMock([XCUIApplication class]); | ||
|
||
id queryMock = OCMClassMock([XCUIElementQuery class]); | ||
|
||
// [Preconditions] Stage 1: mock methods called in [query execute] | ||
OCMStub([uiAppMock cbxQueryForDescendantsOfAnyType]).andReturn(queryMock); | ||
OCMStub([appMock currentApplication]).andReturn(uiAppMock); | ||
|
||
// [Check] Stage 2: throw Exception in case descendant type key is malformed | ||
expect(^{ | ||
[query execute]; | ||
}).to.raise(@"NSInternalInconsistencyException"); | ||
} | ||
|
||
- (void)testQuerySpecifierByDescendantTypeThrowsExceptionForInvalidMissingKeyCase { | ||
// [Set Up] Stage 0: prepare query with invalid config | ||
id invalidJson = @{@"descendant_element": @{@"parent_type" : @"Keyboard" }}; | ||
QueryConfiguration *queryConfig = [QueryConfiguration withJSON:invalidJson validator:nil]; | ||
|
||
Query *query = [QueryFactory queryWithQueryConfiguration:queryConfig]; | ||
|
||
// [Preconditions] Stage 1: prepare mock objects for app and query | ||
id appMock = OCMClassMock([Application class]); | ||
id uiAppMock = OCMClassMock([XCUIApplication class]); | ||
|
||
id queryMock = OCMClassMock([XCUIElementQuery class]); | ||
|
||
// [Preconditions] Stage 1: mock methods called in [query execute] | ||
OCMStub([uiAppMock cbxQueryForDescendantsOfAnyType]).andReturn(queryMock); | ||
OCMStub([appMock currentApplication]).andReturn(uiAppMock); | ||
|
||
// [Check] Stage 2: throw Exception in case missed descendant_type key | ||
expect(^{ | ||
[query execute]; | ||
}).to.raise(@"NSInternalInconsistencyException"); | ||
} | ||
|
||
- (void)testQuerySpecifierByDescendantTypeThrowsExceptionForInvalidParentTypeValueCase { | ||
// [Set Up] Stage 0: prepare query with invalid config | ||
id invalidJson = @{@"descendant_element": @{@"parent_type" : @"UIKeyboard", | ||
@"descendant_type" : @"Button"}}; | ||
QueryConfiguration *queryConfig = [QueryConfiguration withJSON:invalidJson validator:nil]; | ||
|
||
Query *query = [QueryFactory queryWithQueryConfiguration:queryConfig]; | ||
|
||
// [Preconditions] Stage 1: prepare mock objects for app and query | ||
id appMock = OCMClassMock([Application class]); | ||
id uiAppMock = OCMClassMock([XCUIApplication class]); | ||
|
||
id queryMock = OCMClassMock([XCUIElementQuery class]); | ||
|
||
// [Preconditions] Stage 1: mock methods called in [query execute] | ||
OCMStub([uiAppMock cbxQueryForDescendantsOfAnyType]).andReturn(queryMock); | ||
OCMStub([appMock currentApplication]).andReturn(uiAppMock); | ||
|
||
// [Check] Stage 2: throw Exception in case UIElement type value is invalid | ||
expect(^{ | ||
[query execute]; | ||
}).to.raise(@"CBXException"); | ||
} | ||
|
||
- (void)testQuerySpecifierByDescendantTypeReturnsArrayOfElementsForValidCase { | ||
// [Set Up] Stage 0: prepare query with valid config | ||
id validJson = @{@"descendant_element": @{@"parent_type" : @"Keyboard", | ||
@"descendant_type" : @"Button"}}; | ||
QueryConfiguration *queryConfig = [QueryConfiguration withJSON:validJson validator:nil]; | ||
|
||
Query *query = [QueryFactory queryWithQueryConfiguration:queryConfig]; | ||
|
||
// [Preconditions] Stage 1: prepare mock objects for app and query | ||
// (we want mock interactions with real app object and | ||
// check specific implementation of applyInternal for QuerySpecifierByDescendantType) | ||
id appMock = OCMClassMock([Application class]); | ||
id uiAppMock = OCMClassMock([XCUIApplication class]); | ||
id element = OCMClassMock([XCUIElement class]); | ||
id arrayOfElements = [NSArray arrayWithObject:element]; | ||
|
||
id queryMock = OCMClassMock([XCUIElementQuery class]); | ||
id contextQueryMock = OCMClassMock([XCUIElementQuery class]); | ||
id resultQueryMock = OCMClassMock([XCUIElementQuery class]); | ||
|
||
// [Preconditions] Stage 1: mock methods called in [query execute] | ||
OCMStub([uiAppMock cbxQueryForDescendantsOfAnyType]).andReturn(queryMock); | ||
OCMStub([appMock currentApplication]).andReturn(uiAppMock); | ||
|
||
// [Expectations] Stage 2: set up expectations for [specifier applyInternal:query] | ||
// where specifier is QuerySpecifierByDescendantType | ||
XCUIElementType keyboard = XCUIElementTypeKeyboard; | ||
XCUIElementType button = XCUIElementTypeButton; | ||
OCMExpect([queryMock matchingType:keyboard identifier:nil]).andReturn(contextQueryMock); | ||
OCMExpect([contextQueryMock descendantsMatchingType:button]).andReturn(resultQueryMock); | ||
// [Expectations] Stage 2: set up expectations for [query execute] | ||
OCMExpect([resultQueryMock allElementsBoundByIndex]).andReturn(arrayOfElements); | ||
|
||
// [Test Step] Stage 3: perform test step | ||
[query execute]; | ||
|
||
// [Check] Stage 4: verify [specifier applyInternal:query] | ||
OCMVerifyAll(queryMock); | ||
OCMVerifyAll(contextQueryMock); | ||
// [Check] Stage 4: verify [query execute] | ||
OCMVerifyAll(resultQueryMock); | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters