blob: cb6cc92e6e0a508998a097e07607413b854b595c [file] [log] [blame]
//
// Copyright 2021 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "GTXTestIntegrationTestCase.h"
#include <algorithm>
#import "NSString+GTXAdditions.h"
#import "XCUIElement+GTXAdditions.h"
#import "GTXTestViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface GTXTestXCUIElementProtoTests : GTXTestIntegrationTestCase
@end
@implementation GTXTestXCUIElementProtoTests
- (void)testToProtoNonAccessibilityElement {
// The testing area has a subview when the test app launches. Remove it so there is only one
// element.
[self clearTestArea];
XCUIElement *actionContainer = [[[self.app descendantsMatchingType:XCUIElementTypeAny]
matchingIdentifier:kGTXTestTestingAreaID] element];
UIElementProto proto = [actionContainer gtx_toProto];
XCTAssertFalse(proto.has_parent_id());
XCTAssertEqual(proto.child_ids_size(), 0);
// TODO: Assert is_ax_element is false once a reliable way to collect this property
// is determined.
XCTAssertTrue(proto.has_ax_identifier());
XCTAssertEqualObjects([NSString gtx_stringFromSTDString:proto.ax_identifier()],
kGTXTestTestingAreaID);
// XCUIElement label is never nil. If they don't exist, they return the empty string.
XCTAssertEqual(proto.ax_label(), "");
XCTAssertFalse(proto.has_ax_hint());
ElementTypeProto expectedElementType =
[XCUIElement gtxtest_elementTypeFromXCUIElementType:actionContainer.elementType];
XCTAssertEqual(proto.element_type(), expectedElementType);
}
- (void)testToProtoIsAccessibilityElement {
[self performTestActionNamed:kAddNonpunctuatedLabelElementActionName];
XCUIElement *label = [[[self.app descendantsMatchingType:XCUIElementTypeAny]
matchingIdentifier:kGTXTestTestingElementID] element];
UIElementProto proto = [label gtx_toProto];
XCTAssertFalse(proto.has_parent_id());
XCTAssertEqual(proto.child_ids_size(), 0);
// TODO: Assert is_ax_element is true once a reliable way to collect this property is
// determined.
XCTAssertEqual(proto.ax_identifier(), [kGTXTestTestingElementID gtx_stdString]);
XCTAssertTrue(proto.has_ax_label());
XCTAssertEqualObjects([NSString gtx_stringFromSTDString:proto.ax_label()], label.label);
XCTAssertFalse(proto.has_ax_hint());
ElementTypeProto expectedElementType =
[XCUIElement gtxtest_elementTypeFromXCUIElementType:label.elementType];
XCTAssertEqual(proto.element_type(), expectedElementType);
}
- (void)testToHierarchyProtoWithNoChildren {
XCUIElement *actionContainer = [[[self.app descendantsMatchingType:XCUIElementTypeAny]
matchingIdentifier:kGTXTestTestingAreaID] element];
XCTAssertNotNil(actionContainer);
AccessibilityHierarchyProto hierarchy = [actionContainer gtx_toHierarchyProto];
XCTAssertEqual(hierarchy.elements_size(), 1);
}
- (void)testToHierarchyProtoWithOneChild {
[self performTestActionNamed:kAddNoLabelElementActionName];
XCUIElement *actionContainer = [[[self.app descendantsMatchingType:XCUIElementTypeAny]
matchingIdentifier:kGTXTestTestingAreaID] element];
XCTAssertNotNil(actionContainer);
AccessibilityHierarchyProto hierarchy = [actionContainer gtx_toHierarchyProto];
XCTAssertEqual(hierarchy.elements_size(), 2);
XCTAssertEqual(hierarchy.elements(0).child_ids_size(), 1);
XCTAssertEqual(hierarchy.elements(1).child_ids_size(), 0);
[self gtxtest_assertRelationBetweenParent:hierarchy.elements(0) andChild:hierarchy.elements(1)];
}
- (void)testToHierarchyProtoWithManyChildrenAndSubchildren {
[self performTestActionNamed:kAddMultipleElementsWithChildren];
XCUIElement *actionContainer = [[[self.app descendantsMatchingType:XCUIElementTypeAny]
matchingIdentifier:kGTXTestTestingAreaID] element];
XCTAssertNotNil(actionContainer);
AccessibilityHierarchyProto hierarchy = [actionContainer gtx_toHierarchyProto];
// Add one because the containing element is also included.
int expectedSize = (int)kGTXTestMultipleTestingElementIDsCount + 1;
XCTAssertEqual(hierarchy.elements_size(), expectedSize);
// Elements retrieved from XCUIElementQuery are not guaranteed to be in the same order
// they were added to the view hierarchy. Search for them based on accessibility identifier
// to ensure the correct elements are being referenced.
UIElementProto testArea = [self gtx_elementInHierarchy:hierarchy
withIdentifier:kGTXTestTestingAreaID];
UIElementProto parent = [self gtx_elementInHierarchy:hierarchy
withIdentifier:kGTXTestMultipleTestingElementsIDs[0]];
UIElementProto child1 = [self gtx_elementInHierarchy:hierarchy
withIdentifier:kGTXTestMultipleTestingElementsIDs[1]];
UIElementProto child2 = [self gtx_elementInHierarchy:hierarchy
withIdentifier:kGTXTestMultipleTestingElementsIDs[2]];
UIElementProto subchild1 = [self gtx_elementInHierarchy:hierarchy
withIdentifier:kGTXTestMultipleTestingElementsIDs[3]];
[self gtxtest_assertRelationBetweenParent:testArea andChild:parent];
[self gtxtest_assertRelationBetweenParent:parent andChild:child1];
[self gtxtest_assertRelationBetweenParent:parent andChild:child2];
[self gtxtest_assertRelationBetweenParent:child1 andChild:subchild1];
}
#pragma mark - Private
/**
* Finds the element in @c elements with ax_identifier @c identifier. Fails the test if no such
* element is found.
* @param hierarchy A hierarchy of @c UIElements
* @param identifier The accessibility identifier to search for.
* @return A copy of the UIElement in @c elements with ax_identifier equal to @c identifier, or an
* empty element if none could be found.
*/
- (UIElementProto)gtx_elementInHierarchy:(const AccessibilityHierarchyProto &)hierarchy
withIdentifier:(NSString *)identifier {
std::string stdIdentifier = [identifier gtx_stdString];
for (const UIElementProto &element : hierarchy.elements()) {
if (element.ax_identifier() == stdIdentifier) {
return element;
}
}
XCTFail(@"could not find element");
// Return dummy element to satisfy compiler.
return UIElementProto();
}
/**
* Asserts that @c parent contains @c child as a child and @c child has @c parent as its parent.
* @param parent The parent element of child.
* @param child The child element of parent.
*/
- (void)gtxtest_assertRelationBetweenParent:(const UIElementProto &)parent
andChild:(const UIElementProto &)child {
[self gtxtest_assertField:parent.child_ids() containsElementExactlyOnce:child.id()];
XCTAssertEqual(parent.id(), child.parent_id());
}
/**
* Determines if a repeated proto field contains @c value exactly once. Passes if @c field contains
* @c value exactly one time, fails the test if it contains @c value 0 or more than 1 times.
* @param field A repeated integer proto field.
* @param value The value to search for.
*/
- (void)gtxtest_assertField:(const proto2::RepeatedField<int32> &)field
containsElementExactlyOnce:(int32)value {
auto first = std::find(field.cbegin(), field.cend(), value);
if (first == field.cend()) {
XCTFail(@"%d value was not found in repeated field", value);
return;
}
auto second = std::find(first + 1, field.cend(), value);
// If searching the subsequence of the vector after the first match
// finds no match, then there must be only one match. If it finds
// a second match separate from the first, there are multiple matches.
XCTAssert(second == field.cend(), @"%d value was found in repeated field multiple times", value);
}
@end
NS_ASSUME_NONNULL_END