summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2016-04-25 15:54:55 -0700
committerMinh Nguyễn <mxn@1ec5.org>2016-05-05 01:07:29 -0700
commit1a565a5fb72658c91428e108230e72c5fb2cf4d1 (patch)
tree570b8df11cd70f30ed398ac790db1580b2dd0a93
parent10e522d10b33e5268b619296eae0ccad899809d9 (diff)
downloadqtlocation-mapboxgl-1a565a5fb72658c91428e108230e72c5fb2cf4d1.tar.gz
[ios] iosapp UI tests
Added a UI test bundle that uses XCUITest to test iosapp. The bundle contains some basic tests of annotation functionality. Gather code coverage data in the iosapp and dynamic schemes. Added accessibility identifiers for key controls in the SDK and iosapp. Working towards #4794.
-rw-r--r--platform/ios/app/Main.storyboard4
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj107
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme10
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme3
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme13
-rw-r--r--platform/ios/src/MGLMapView.mm10
-rw-r--r--platform/ios/src/MGLUserLocationAnnotationView.m5
-rw-r--r--platform/ios/uitest/Info.plist24
-rw-r--r--platform/ios/uitest/MGLAnnotationTests.m120
9 files changed, 294 insertions, 2 deletions
diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard
index 1190070d8e..fb9b0c6d39 100644
--- a/platform/ios/app/Main.storyboard
+++ b/platform/ios/app/Main.storyboard
@@ -41,6 +41,7 @@
<barButtonItem key="leftBarButtonItem" image="settings.png" id="Jw8-JP-CaZ" userLabel="Map Settings">
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityLabel" value="Map settings"/>
+ <userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="MBXSettingsButton"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="showSettings:" destination="WaX-pd-UZQ" id="X2C-Ee-Qvt"/>
@@ -174,6 +175,9 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="ONr-CS-J5X">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="MBXNavigationBar"/>
+ </userDefinedRuntimeAttributes>
</navigationBar>
<nil name="viewControllers"/>
<connections>
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index f97263f2cd..20024c8749 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -49,6 +49,7 @@
DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
+ DA4A0DF01CCE82500045352A /* MGLAnnotationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4A0DEF1CCE82500045352A /* MGLAnnotationTests.m */; };
DA821D061CCC6D59007508D4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */; };
DA821D071CCC6D59007508D4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA821D051CCC6D59007508D4 /* Main.storyboard */; };
DA8847D91CBAF91600AB86E3 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; };
@@ -253,6 +254,13 @@
remoteGlobalIDString = DA8847D11CBAF91600AB86E3;
remoteInfo = dynamic;
};
+ DA4A0DF21CCE82500045352A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA1DC9491CB6C1C2006E619F;
+ remoteInfo = iosapp;
+ };
DA8847D71CBAF91600AB86E3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
@@ -344,6 +352,9 @@
DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = "<group>"; };
DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = "<group>"; };
DA35A2D11CCAB25200E826B2 /* jazzy.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = jazzy.yml; sourceTree = "<group>"; };
+ DA4A0DED1CCE82500045352A /* uitest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = uitest.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA4A0DEF1CCE82500045352A /* MGLAnnotationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationTests.m; sourceTree = "<group>"; };
+ DA4A0DF11CCE82500045352A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DA4A26961CB6E795000B7809 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
DA821D051CCC6D59007508D4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
@@ -481,6 +492,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ DA4A0DEA1CCE82500045352A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
DA8847CE1CBAF91600AB86E3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -518,6 +536,7 @@
DABCABA91CB80692000A7C39 /* Benchmarking App */,
DA8847D31CBAF91600AB86E3 /* SDK */,
DA2E88521CC036F400F24E7B /* SDK Tests */,
+ DA4A0DEE1CCE82500045352A /* uitest */,
DA1DC9921CB6DF24006E619F /* Frameworks */,
DAC07C951CBB2CAD000CB309 /* Configuration */,
DA1DC94B1CB6C1C2006E619F /* Products */,
@@ -534,6 +553,7 @@
DA2E88511CC036F400F24E7B /* test.xctest */,
DA8933D51CCD306400E68420 /* Mapbox.bundle */,
DA25D5B91CCD9EDE00607828 /* Settings.bundle */,
+ DA4A0DED1CCE82500045352A /* uitest.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -612,6 +632,15 @@
path = test;
sourceTree = "<group>";
};
+ DA4A0DEE1CCE82500045352A /* uitest */ = {
+ isa = PBXGroup;
+ children = (
+ DA4A0DEF1CCE82500045352A /* MGLAnnotationTests.m */,
+ DA4A0DF11CCE82500045352A /* Info.plist */,
+ );
+ path = uitest;
+ sourceTree = "<group>";
+ };
DA8847D31CBAF91600AB86E3 /* SDK */ = {
isa = PBXGroup;
children = (
@@ -983,6 +1012,24 @@
productReference = DA2E88511CC036F400F24E7B /* test.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
+ DA4A0DEC1CCE82500045352A /* uitest */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DA4A0DF61CCE82500045352A /* Build configuration list for PBXNativeTarget "uitest" */;
+ buildPhases = (
+ DA4A0DE91CCE82500045352A /* Sources */,
+ DA4A0DEA1CCE82500045352A /* Frameworks */,
+ DA4A0DEB1CCE82500045352A /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DA4A0DF31CCE82500045352A /* PBXTargetDependency */,
+ );
+ name = uitest;
+ productName = uitest;
+ productReference = DA4A0DED1CCE82500045352A /* uitest.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
DA8847D11CBAF91600AB86E3 /* dynamic */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA8847DD1CBAF91600AB86E3 /* Build configuration list for PBXNativeTarget "dynamic" */;
@@ -1075,6 +1122,10 @@
DA2E88501CC036F400F24E7B = {
CreatedOnToolsVersion = 7.3;
};
+ DA4A0DEC1CCE82500045352A = {
+ CreatedOnToolsVersion = 7.3;
+ TestTargetID = DA1DC9491CB6C1C2006E619F;
+ };
DA8847D11CBAF91600AB86E3 = {
CreatedOnToolsVersion = 7.3;
};
@@ -1109,6 +1160,7 @@
DA8933D41CCD306400E68420 /* bundle */,
DA25D5B81CCD9EDE00607828 /* settings */,
DA2E88501CC036F400F24E7B /* test */,
+ DA4A0DEC1CCE82500045352A /* uitest */,
);
};
/* End PBXProject section */
@@ -1145,6 +1197,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ DA4A0DEB1CCE82500045352A /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
DA8847D01CBAF91600AB86E3 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1235,6 +1294,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ DA4A0DE91CCE82500045352A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA4A0DF01CCE82500045352A /* MGLAnnotationTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
DA8847CD1CBAF91600AB86E3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1343,6 +1410,11 @@
target = DA8847D11CBAF91600AB86E3 /* dynamic */;
targetProxy = DA2E88571CC036F400F24E7B /* PBXContainerItemProxy */;
};
+ DA4A0DF31CCE82500045352A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA1DC9491CB6C1C2006E619F /* iosapp */;
+ targetProxy = DA4A0DF21CCE82500045352A /* PBXContainerItemProxy */;
+ };
DA8847D81CBAF91600AB86E3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DA8847D11CBAF91600AB86E3 /* dynamic */;
@@ -1567,6 +1639,32 @@
};
name = Release;
};
+ DA4A0DF41CCE82500045352A /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ INFOPLIST_FILE = uitest/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.uitest;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_TARGET_NAME = iosapp;
+ };
+ name = Debug;
+ };
+ DA4A0DF51CCE82500045352A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ INFOPLIST_FILE = uitest/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.uitest;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_TARGET_NAME = iosapp;
+ };
+ name = Release;
+ };
DA8847DB1CBAF91600AB86E3 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
@@ -1792,6 +1890,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ DA4A0DF61CCE82500045352A /* Build configuration list for PBXNativeTarget "uitest" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA4A0DF41CCE82500045352A /* Debug */,
+ DA4A0DF51CCE82500045352A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
DA8847DD1CBAF91600AB86E3 /* Build configuration list for PBXNativeTarget "dynamic" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
index 26982c0747..6e2c2a5c79 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
@@ -66,6 +66,16 @@
ReferencedContainer = "container:ios.xcodeproj">
</BuildableReference>
</TestableReference>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA4A0DEC1CCE82500045352A"
+ BuildableName = "uitest.xctest"
+ BlueprintName = "uitest"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
index b77beb34aa..38794b6c7e 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
@@ -40,7 +40,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme
index 064add0fea..9f5e2355c3 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme
@@ -26,8 +26,19 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ codeCoverageEnabled = "YES">
<Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA4A0DEC1CCE82500045352A"
+ BuildableName = "uitest.xctest"
+ BlueprintName = "uitest"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 3d2679498a..ce49ae9997 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -148,6 +148,11 @@ mbgl::Color MGLColorObjectFromUIColor(UIColor *color)
return self;
}
+- (NSString *)accessibilityIdentifier
+{
+ return [NSString stringWithFormat:@"MGLMapViewAnnotation %d", self.tag];
+}
+
- (void)accessibilityIncrement
{
[self.accessibilityContainer accessibilityIncrement];
@@ -181,6 +186,7 @@ public:
if (self = [super initWithAccessibilityContainer:container])
{
self.accessibilityTraits = UIAccessibilityTraitButton;
+ self.accessibilityIdentifier = NSStringFromClass([self class]);
self.accessibilityLabel = [self.accessibilityContainer accessibilityLabel];
self.accessibilityHint = @"Returns to the map";
}
@@ -361,6 +367,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// setup accessibility
//
// self.isAccessibilityElement = YES;
+ self.accessibilityIdentifier = NSStringFromClass([self class]);
self.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"MAP_A11Y_LABEL", nil, nil, @"Map", @"Accessibility label");
self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction | UIAccessibilityTraitAdjustable;
_accessibilityCompassFormatter = [[MGLCompassDirectionFormatter alloc] init];
@@ -410,6 +417,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
//
UIImage *logo = [[MGLMapView resourceImageNamed:@"mapbox.png"] imageWithAlignmentRectInsets:UIEdgeInsetsMake(1.5, 4, 3.5, 2)];
_logoView = [[UIImageView alloc] initWithImage:logo];
+ _logoView.accessibilityIdentifier = @"MGLMapViewLogo";
_logoView.accessibilityTraits = UIAccessibilityTraitStaticText;
_logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label");
_logoView.translatesAutoresizingMaskIntoConstraints = NO;
@@ -419,6 +427,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// setup attribution
//
_attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
+ _attributionButton.accessibilityIdentifier = @"MGLMapViewInfo";
_attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label");
_attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint");
[_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside];
@@ -435,6 +444,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
_compassView.userInteractionEnabled = YES;
[_compassView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleCompassTapGesture:)]];
_compassView.accessibilityTraits = UIAccessibilityTraitButton;
+ _compassView.accessibilityIdentifier = @"MGLMapViewCompass";
_compassView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_LABEL", nil, nil, @"Compass", @"Accessibility label");
_compassView.accessibilityHint = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_HINT", nil, nil, @"Rotates the map to face due north", @"Accessibility hint");
UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
diff --git a/platform/ios/src/MGLUserLocationAnnotationView.m b/platform/ios/src/MGLUserLocationAnnotationView.m
index c514026e42..590234f516 100644
--- a/platform/ios/src/MGLUserLocationAnnotationView.m
+++ b/platform/ios/src/MGLUserLocationAnnotationView.m
@@ -76,6 +76,11 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
return !self.hidden;
}
+- (NSString *)accessibilityIdentifier
+{
+ return NSStringFromClass([self class]);
+}
+
- (NSString *)accessibilityLabel
{
return self.annotation.title;
diff --git a/platform/ios/uitest/Info.plist b/platform/ios/uitest/Info.plist
new file mode 100644
index 0000000000..ba72822e87
--- /dev/null
+++ b/platform/ios/uitest/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/platform/ios/uitest/MGLAnnotationTests.m b/platform/ios/uitest/MGLAnnotationTests.m
new file mode 100644
index 0000000000..0420fd3b59
--- /dev/null
+++ b/platform/ios/uitest/MGLAnnotationTests.m
@@ -0,0 +1,120 @@
+#import <XCTest/XCTest.h>
+
+@interface MGLAnnotationTests : XCTestCase
+
+@end
+
+@implementation MGLAnnotationTests
+
+- (void)setUp {
+ [super setUp];
+
+ self.continueAfterFailure = NO;
+
+ [XCUIDevice sharedDevice].orientation = UIDeviceOrientationPortrait;
+ [[[XCUIApplication alloc] init] launch];
+}
+
+- (void)testDropPin {
+ XCUIElement *mapElement = [[XCUIApplication alloc] init].otherElements[@"MGLMapView"];
+
+ // Drop a pin.
+ XCUIElement *mapProxyElement = mapElement.buttons[@"MGLMapViewProxyAccessibilityElement"];
+ XCTAssertFalse(mapElement.buttons[@"MGLMapViewProxyAccessibilityElement"].exists, @"Map proxy element should be absent until opening callout view.");
+ [self expectationForPredicate:[NSPredicate predicateWithFormat:@"exists == true"] evaluatedWithObject:mapProxyElement handler:nil];
+ [mapElement pressForDuration:1.1];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+
+ // Inspect the callout view.
+ NSString *annotationTitle = @"Dropped Pin";
+ XCUIElement *calloutTitleText = mapElement.staticTexts[annotationTitle];
+ XCTAssertTrue(calloutTitleText.exists, @"Callout title should be present after opening callout.");
+ NSString *annotationSubtitle = @"17°0′56″ south, 0° west";
+ XCUIElement *calloutSubtitleText = mapElement.staticTexts[annotationSubtitle];
+ XCTAssertTrue(calloutSubtitleText.exists, @"Callout subtitle should be present after opening callout.");
+ XCUIElement *calloutLeftAccessoryButton = mapElement.buttons[@"Left"];
+ XCTAssertTrue(calloutLeftAccessoryButton.exists, @"Callout left accessory button should be present after opening callout.");
+ XCUIElement *calloutRightAccessoryButton = mapElement.buttons[@"Right"];
+ XCTAssertTrue(calloutRightAccessoryButton.exists, @"Callout right accessory button should be present after opening callout.");
+
+ // Close the callout view by tapping on the map proxy element. Note that the map proxy element has a gaping hole in the middle to accommodate the callout view.
+ [self expectationForPredicate:[NSPredicate predicateWithFormat:@"exists == false"] evaluatedWithObject:mapProxyElement handler:nil];
+ XCUICoordinate *coordinate = [[mapProxyElement coordinateWithNormalizedOffset:CGVectorMake(0, 0)] coordinateWithOffset:CGVectorMake(100, 100)];
+ [coordinate tap];
+ [self waitForExpectationsWithTimeout:2 handler:nil];
+
+ XCTAssertFalse(calloutTitleText.exists, @"Callout title should be absent after closing callout.");
+ XCTAssertFalse(calloutSubtitleText.exists, @"Callout subtitle should be absent after closing callout.");
+ XCTAssertFalse(calloutLeftAccessoryButton.exists, @"Callout left accessory button should be absent after closing callout.");
+ XCTAssertFalse(calloutRightAccessoryButton.exists, @"Callout right accessory button should be absent after closing callout.");
+
+ // Inspect the annotation itself.
+ XCUIElement *annotation = mapElement.buttons[@"MGLMapViewAnnotation 0"];
+ XCTAssertTrue(annotation.exists, @"Annotation accessibility element should be present after closing callout view.");
+ XCTAssertEqualObjects(annotation.label, annotationTitle, @"Annotation label should be title.");
+ XCTAssertEqualObjects(annotation.value, annotationSubtitle, @"Annotation value should be subtitle.");
+}
+
+- (void)testFrameWithGestures {
+ XCUIElement *mapElement = [[XCUIApplication alloc] init].otherElements[@"MGLMapView"];
+
+ // Drop a pin.
+ XCUIElement *mapProxyElement = mapElement.buttons[@"MGLMapViewProxyAccessibilityElement"];
+ [self expectationForPredicate:[NSPredicate predicateWithFormat:@"exists == true"] evaluatedWithObject:mapProxyElement handler:nil];
+ [mapElement pressForDuration:1.1];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+
+ // Close the callout view.
+ [self expectationForPredicate:[NSPredicate predicateWithFormat:@"exists == false"] evaluatedWithObject:mapProxyElement handler:nil];
+ XCUICoordinate *coordinate = [[mapProxyElement coordinateWithNormalizedOffset:CGVectorMake(0, 0)] coordinateWithOffset:CGVectorMake(100, 100)];
+ [coordinate tap];
+ [self waitForExpectationsWithTimeout:2 handler:nil];
+
+ XCUIElement *annotation = mapElement.buttons[@"MGLMapViewAnnotation 0"];
+ CGRect frame = annotation.frame;
+
+ // Make sure the annotation stays in place when zooming in.
+ [mapElement doubleTap];
+ CGRect frameAfterZoomingIn = annotation.frame;
+ XCTAssertEqualWithAccuracy(frame.origin.x, frameAfterZoomingIn.origin.x, 0.1, @"Annotation moved after zooming in.");
+ XCTAssertEqualWithAccuracy(frame.origin.y, frameAfterZoomingIn.origin.y, 0.1, @"Annotation moved after zooming in.");
+ XCTAssertEqualWithAccuracy(frame.size.width, frameAfterZoomingIn.size.width, 0.1, @"Annotation resized after zooming in.");
+ XCTAssertEqualWithAccuracy(frame.size.height, frameAfterZoomingIn.size.height, 0.1, @"Annotation resized after zooming in.");
+
+ // Make sure the annotation moves after panning.
+ CGVector offset = CGVectorMake(50, 0);
+ XCUICoordinate *startCoordinate = [[mapElement coordinateWithNormalizedOffset:CGVectorMake(0, 0)]
+ coordinateWithOffset:CGVectorMake(100, 100)];
+ XCUICoordinate *endCoordinate = [startCoordinate coordinateWithOffset:offset];
+ [startCoordinate pressForDuration:0 thenDragToCoordinate:endCoordinate];
+ [mapElement tap];
+ CGRect frameAfterPanning = annotation.frame;
+ XCTAssertLessThanOrEqual(frame.origin.x + offset.dx, frameAfterPanning.origin.x, @"Annotation moved after panning right.");
+ XCTAssertEqualWithAccuracy(frame.origin.y + offset.dy, frameAfterPanning.origin.y, 0.1, @"Annotation moved after panning right.");
+ XCTAssertEqualWithAccuracy(frame.size.width, frameAfterPanning.size.width, 0.1, @"Annotation resized after panning right.");
+ XCTAssertEqualWithAccuracy(frame.size.height, frameAfterPanning.size.height, 0.1, @"Annotation resized after panning right.");
+}
+
+- (void)testRemoveAnnotations {
+ XCUIApplication *app = [[XCUIApplication alloc] init];
+
+ // Drop a pin.
+ XCUIElement *mapElement = [[XCUIApplication alloc] init].otherElements[@"MGLMapView"];
+ XCUIElement *mapProxyElement = mapElement.buttons[@"MGLMapViewProxyAccessibilityElement"];
+ [self expectationForPredicate:[NSPredicate predicateWithFormat:@"exists == true"] evaluatedWithObject:mapProxyElement handler:nil];
+ [mapElement pressForDuration:1.1];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+
+ // Remove all annotations, closing the callout view.
+ [app.navigationBars[@"MBXNavigationBar"].buttons[@"MBXSettingsButton"] tap];
+ [app.sheets[@"Map Settings"] swipeUp];
+ XCUIElementQuery *settingsCollectionViewsQuery = app.sheets[@"Map Settings"].collectionViews;
+ XCUIElement *removeAnnotationsButton = settingsCollectionViewsQuery.buttons[@"Remove Annotations"];
+ [self expectationForPredicate:[NSPredicate predicateWithFormat:@"exists == false"] evaluatedWithObject:mapProxyElement handler:nil];
+ [removeAnnotationsButton tap];
+ [self waitForExpectationsWithTimeout:2 handler:nil];
+
+ XCTAssertFalse(mapElement.buttons[@"MGLMapViewAnnotation 0"].exists, @"Annotation should be gone after removing all annotations.");
+}
+
+@end