appium-webdriveragent 16.1.1 → 16.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/WebDriverAgentLib/Categories/XCUIApplication+FBAlert.h +20 -8
- package/WebDriverAgentLib/Categories/XCUIApplication+FBAlert.m +116 -60
- package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +15 -0
- package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +16 -0
- package/WebDriverAgentLib/Categories/XCUIElement+FBClassChain.m +146 -3
- package/WebDriverAgentLib/Commands/FBAlertViewCommands.m +10 -36
- package/WebDriverAgentLib/FBAlert.h +43 -30
- package/WebDriverAgentLib/FBAlert.m +257 -91
- package/WebDriverAgentLib/Info.plist +2 -2
- package/WebDriverAgentLib/Routing/FBExceptionHandler.m +8 -1
- package/WebDriverAgentLib/Routing/FBExceptions.h +9 -0
- package/WebDriverAgentLib/Routing/FBExceptions.m +3 -0
- package/WebDriverAgentLib/Routing/FBSession.m +11 -13
- package/WebDriverAgentLib/Utilities/FBAlertsMonitor.m +11 -9
- package/WebDriverAgentLib/Utilities/FBPasteboard.m +8 -4
- package/WebDriverAgentTests/IntegrationApp/Classes/ViewController.m +34 -0
- package/WebDriverAgentTests/IntegrationApp/Resources/Base.lproj/Main.storyboard +12 -0
- package/WebDriverAgentTests/IntegrationTests/FBAlertTests.m +32 -51
- package/WebDriverAgentTests/IntegrationTests/FBIntegrationTestCase.h +16 -0
- package/WebDriverAgentTests/IntegrationTests/FBIntegrationTestCase.m +28 -1
- package/WebDriverAgentTests/IntegrationTests/FBSafariAlertTests.m +1 -1
- package/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m +3 -3
- package/WebDriverAgentTests/IntegrationTests/XCElementSnapshotHelperTests.m +1 -7
- package/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +76 -10
- package/package.json +1 -1
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
#import "FBXCTestDaemonsProxy.h"
|
|
25
25
|
#import "XCUIApplication+FBQuiescence.h"
|
|
26
26
|
#import "XCUIElement.h"
|
|
27
|
-
#import "XCUIElement+FBClassChain.h"
|
|
28
27
|
|
|
29
28
|
/*!
|
|
30
29
|
The intial value for the default application property.
|
|
@@ -56,14 +55,10 @@ NSString *const FB_SAFARI_BUNDLE_ID = @"com.apple.mobilesafari";
|
|
|
56
55
|
NSString *autoClickAlertSelector = FBConfiguration.autoClickAlertSelector;
|
|
57
56
|
if ([autoClickAlertSelector length] > 0) {
|
|
58
57
|
@try {
|
|
59
|
-
|
|
60
|
-
shouldReturnAfterFirstMatch:YES];
|
|
61
|
-
if (matches.count > 0) {
|
|
62
|
-
[[matches objectAtIndex:0] tap];
|
|
63
|
-
}
|
|
58
|
+
[alert clickElementMatchingClassChain:autoClickAlertSelector];
|
|
64
59
|
} @catch (NSException *e) {
|
|
65
60
|
[FBLogger logFmt:@"Could not click at the alert element '%@'. Original error: %@",
|
|
66
|
-
|
|
61
|
+
autoClickAlertSelector, e.reason];
|
|
67
62
|
}
|
|
68
63
|
// This setting has priority over other settings if enabled
|
|
69
64
|
return;
|
|
@@ -73,14 +68,17 @@ NSString *const FB_SAFARI_BUNDLE_ID = @"com.apple.mobilesafari";
|
|
|
73
68
|
return;
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
NSError *error;
|
|
77
71
|
if ([self.defaultAlertAction isEqualToString:@"accept"]) {
|
|
78
|
-
|
|
79
|
-
[
|
|
72
|
+
@try {
|
|
73
|
+
[alert accept];
|
|
74
|
+
} @catch (NSException *e) {
|
|
75
|
+
[FBLogger logFmt:@"Cannot accept the alert. Original error: %@", e.reason];
|
|
80
76
|
}
|
|
81
77
|
} else if ([self.defaultAlertAction isEqualToString:@"dismiss"]) {
|
|
82
|
-
|
|
83
|
-
[
|
|
78
|
+
@try {
|
|
79
|
+
[alert dismiss];
|
|
80
|
+
} @catch (NSException *e) {
|
|
81
|
+
[FBLogger logFmt:@"Cannot dismiss the alert. Original error: %@", e.reason];
|
|
84
82
|
}
|
|
85
83
|
} else {
|
|
86
84
|
[FBLogger logFmt:@"'%@' default alert action is unsupported", self.defaultAlertAction];
|
|
@@ -213,7 +211,7 @@ static FBSession *_activeSession = nil;
|
|
|
213
211
|
XCUIApplicationState testedAppState = self.testedApplication.state;
|
|
214
212
|
if (testedAppState >= XCUIApplicationStateRunningForeground) {
|
|
215
213
|
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"%K == %@ OR %K IN {%@, %@}",
|
|
216
|
-
@"elementType", @(XCUIElementTypeAlert),
|
|
214
|
+
@"elementType", @(XCUIElementTypeAlert),
|
|
217
215
|
// To look for `SBTransientOverlayWindow` elements. See https://github.com/appium/WebDriverAgent/pull/946
|
|
218
216
|
@"identifier", @"SBTransientOverlayWindow",
|
|
219
217
|
// To look for 'criticalAlertSetting' elements https://developer.apple.com/documentation/usernotifications/unnotificationsettings/criticalalertsetting
|
|
@@ -52,26 +52,28 @@ static const NSTimeInterval FB_MONTORING_INTERVAL = 2.0;
|
|
|
52
52
|
NSArray<XCUIApplication *> *activeApps = XCUIApplication.fb_activeApplications;
|
|
53
53
|
BOOL didDetectAlert = NO;
|
|
54
54
|
for (XCUIApplication *activeApp in activeApps) {
|
|
55
|
-
XCUIElement *alertElement = nil;
|
|
56
55
|
@try {
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
[delegate didDetectAlert:
|
|
56
|
+
FBAlert *alert = [FBAlert alertWithApplication:activeApp];
|
|
57
|
+
if (alert.isPresent) {
|
|
58
|
+
[delegate didDetectAlert:alert];
|
|
59
|
+
didDetectAlert = YES;
|
|
60
60
|
}
|
|
61
61
|
} @catch (NSException *e) {
|
|
62
62
|
[FBLogger logFmt:@"Got an unexpected exception while monitoring alerts: %@\n%@", e.reason, e.callStackSymbols];
|
|
63
63
|
}
|
|
64
|
-
if (
|
|
65
|
-
didDetectAlert = YES;
|
|
64
|
+
if (didDetectAlert) {
|
|
66
65
|
break;
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
if (!didDetectAlert) {
|
|
71
70
|
@try {
|
|
72
|
-
|
|
73
|
-
if (nil !=
|
|
74
|
-
|
|
71
|
+
XCUIApplication *promptApp = XCUIApplication.fb_limitedAccessPromptApplication;
|
|
72
|
+
if (nil != promptApp) {
|
|
73
|
+
FBAlert *alert = [FBAlert alertWithApplication:promptApp];
|
|
74
|
+
if (alert.isPresent) {
|
|
75
|
+
[delegate didDetectAlert:alert];
|
|
76
|
+
}
|
|
75
77
|
}
|
|
76
78
|
} @catch (NSException *e) {
|
|
77
79
|
[FBLogger logFmt:@"Got an unexpected exception while monitoring alerts: %@\n%@", e.reason, e.callStackSymbols];
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#import <mach/mach_time.h>
|
|
12
12
|
#import "FBAlert.h"
|
|
13
13
|
#import "FBErrorBuilder.h"
|
|
14
|
+
#import "FBExceptions.h"
|
|
14
15
|
#import "FBMacros.h"
|
|
15
16
|
#import "XCUIApplication+FBHelpers.h"
|
|
16
17
|
#import "XCUIApplication+FBAlert.h"
|
|
@@ -100,10 +101,13 @@
|
|
|
100
101
|
break;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
[
|
|
104
|
+
@try {
|
|
105
|
+
[[FBAlert alertWithApplication:XCUIApplication.fb_systemApplication] accept];
|
|
106
|
+
} @catch (NSException *e) {
|
|
107
|
+
if (![e.name isEqualToString:FBAlertNotPresentException]) {
|
|
108
|
+
@throw e;
|
|
109
|
+
}
|
|
110
|
+
// No alert is present on this tick - expected on most iterations of this poll loop
|
|
107
111
|
}
|
|
108
112
|
uint64_t timeElapsed = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) - timeStarted;
|
|
109
113
|
if (timeElapsed / NSEC_PER_SEC > timeout) {
|
|
@@ -48,6 +48,40 @@
|
|
|
48
48
|
button.selected = !button.selected;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
- (IBAction)goToDeepHierarchy:(id)sender
|
|
52
|
+
{
|
|
53
|
+
// Plain UIViews with fixed frames only - no Auto Layout constraints and no
|
|
54
|
+
// specialized subclasses (e.g. UITextView) that carry their own layout/text
|
|
55
|
+
// engines, which can make a deep nested chain pathologically expensive to
|
|
56
|
+
// lay out. This page exists purely as a fixture for exercising element
|
|
57
|
+
// lookups (e.g. class chain locators) against a deep accessibility tree.
|
|
58
|
+
UIViewController *deepHierarchyViewController = [UIViewController new];
|
|
59
|
+
deepHierarchyViewController.view.backgroundColor = UIColor.whiteColor;
|
|
60
|
+
deepHierarchyViewController.view.accessibilityIdentifier = @"DeepHierarchyPage";
|
|
61
|
+
|
|
62
|
+
NSInteger depth = 70;
|
|
63
|
+
// A plain UILabel sibling, not part of the nested chain below, so the
|
|
64
|
+
// fixture stays recognizable to a human glancing at the simulator instead
|
|
65
|
+
// of showing a blank white screen.
|
|
66
|
+
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 60, CGRectGetWidth(UIScreen.mainScreen.bounds) - 40, 60)];
|
|
67
|
+
titleLabel.text = [NSString stringWithFormat:@"Deep Hierarchy\n%ld nested elements", (long)depth];
|
|
68
|
+
titleLabel.numberOfLines = 2;
|
|
69
|
+
titleLabel.textAlignment = NSTextAlignmentCenter;
|
|
70
|
+
titleLabel.font = [UIFont systemFontOfSize:20];
|
|
71
|
+
[deepHierarchyViewController.view addSubview:titleLabel];
|
|
72
|
+
|
|
73
|
+
UIView *parent = deepHierarchyViewController.view;
|
|
74
|
+
for (NSInteger i = 0; i < depth; i++) {
|
|
75
|
+
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
|
|
76
|
+
view.accessibilityIdentifier = [NSString stringWithFormat:@"view_%ld", (long)i];
|
|
77
|
+
view.accessibilityLabel = [NSString stringWithFormat:@"View %ld", (long)i];
|
|
78
|
+
[parent addSubview:view];
|
|
79
|
+
parent = view;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
[self.navigationController pushViewController:deepHierarchyViewController animated:NO];
|
|
83
|
+
}
|
|
84
|
+
|
|
51
85
|
- (void)viewDidLayoutSubviews
|
|
52
86
|
{
|
|
53
87
|
[super viewDidLayoutSubviews];
|
|
@@ -74,6 +74,16 @@
|
|
|
74
74
|
<segue destination="XaE-eF-eIt" kind="show" id="q3u-B9-6R6"/>
|
|
75
75
|
</connections>
|
|
76
76
|
</button>
|
|
77
|
+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dHi-01-Btn">
|
|
78
|
+
<rect key="frame" x="150" y="380" width="114" height="30"/>
|
|
79
|
+
<constraints>
|
|
80
|
+
<constraint firstAttribute="height" constant="30" id="dHi-02-Hgt"/>
|
|
81
|
+
</constraints>
|
|
82
|
+
<state key="normal" title="DeepHierarchy"/>
|
|
83
|
+
<connections>
|
|
84
|
+
<action selector="goToDeepHierarchy:" destination="BYZ-38-t0r" eventType="touchUpInside" id="dHi-03-Act"/>
|
|
85
|
+
</connections>
|
|
86
|
+
</button>
|
|
77
87
|
</subviews>
|
|
78
88
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
79
89
|
<accessibility key="accessibilityConfiguration" label="MainView"/>
|
|
@@ -90,6 +100,8 @@
|
|
|
90
100
|
<constraint firstItem="uiD-R4-b34" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="nFx-Xr-rGC"/>
|
|
91
101
|
<constraint firstItem="S56-6U-3gG" firstAttribute="top" secondItem="M2N-Yn-ytb" secondAttribute="bottom" constant="8" id="rMW-LW-ejO"/>
|
|
92
102
|
<constraint firstItem="M2N-Yn-ytb" firstAttribute="top" secondItem="YgP-SF-TkS" secondAttribute="bottom" constant="8" id="tMN-gl-smg"/>
|
|
103
|
+
<constraint firstItem="dHi-01-Btn" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="dHi-04-Cnt"/>
|
|
104
|
+
<constraint firstItem="dHi-01-Btn" firstAttribute="top" secondItem="1Ht-AF-MGW" secondAttribute="bottom" constant="8" id="dHi-05-Top"/>
|
|
93
105
|
</constraints>
|
|
94
106
|
</view>
|
|
95
107
|
<navigationItem key="navigationItem" id="dmu-Fe-aoT"/>
|
|
@@ -69,60 +69,54 @@
|
|
|
69
69
|
|
|
70
70
|
- (void)testAlertPresence
|
|
71
71
|
{
|
|
72
|
-
|
|
73
|
-
XCTAssertFalse(alert.isPresent);
|
|
72
|
+
XCTAssertFalse([FBAlert alertWithApplication:self.testedApplication].isPresent);
|
|
74
73
|
[self showApplicationAlert];
|
|
75
|
-
XCTAssertTrue(
|
|
74
|
+
XCTAssertTrue([FBAlert alertWithApplication:self.testedApplication].isPresent);
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
- (void)testAlertText
|
|
79
78
|
{
|
|
80
|
-
|
|
81
|
-
XCTAssertNil(alert.text);
|
|
79
|
+
XCTAssertNil([FBAlert alertWithApplication:self.testedApplication].text);
|
|
82
80
|
[self showApplicationAlert];
|
|
83
|
-
|
|
84
|
-
XCTAssertTrue([
|
|
81
|
+
NSString *text = [FBAlert alertWithApplication:self.testedApplication].text;
|
|
82
|
+
XCTAssertTrue([text containsString:@"Magic"]);
|
|
83
|
+
XCTAssertTrue([text containsString:@"Should read"]);
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
- (void)testAlertLabels
|
|
88
87
|
{
|
|
89
|
-
|
|
90
|
-
XCTAssertNil(alert.buttonLabels);
|
|
88
|
+
XCTAssertNil([FBAlert alertWithApplication:self.testedApplication].buttonLabels);
|
|
91
89
|
[self showApplicationAlert];
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
NSArray *labels = [FBAlert alertWithApplication:self.testedApplication].buttonLabels;
|
|
91
|
+
XCTAssertNotNil(labels);
|
|
92
|
+
XCTAssertEqual(1, labels.count);
|
|
93
|
+
XCTAssertEqualObjects(@"Will do", labels[0]);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
- (void)testClickAlertButton
|
|
98
97
|
{
|
|
99
|
-
|
|
100
|
-
XCTAssertFalse([alert clickAlertButton:@"Invalid" error:nil]);
|
|
98
|
+
XCTAssertThrows([[FBAlert alertWithApplication:self.testedApplication] clickAlertButton:@"Invalid"]);
|
|
101
99
|
[self showApplicationAlert];
|
|
102
|
-
|
|
103
|
-
FBAssertWaitTillBecomesTrue(
|
|
104
|
-
|
|
105
|
-
FBAssertWaitTillBecomesTrue(!
|
|
100
|
+
XCTAssertThrows([[FBAlert alertWithApplication:self.testedApplication] clickAlertButton:@"Invalid"]);
|
|
101
|
+
FBAssertWaitTillBecomesTrue([FBAlert alertWithApplication:self.testedApplication].isPresent);
|
|
102
|
+
XCTAssertNoThrow([[FBAlert alertWithApplication:self.testedApplication] clickAlertButton:@"Will do"]);
|
|
103
|
+
FBAssertWaitTillBecomesTrue(![FBAlert alertWithApplication:self.testedApplication].isPresent);
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
- (void)testAcceptingAlert
|
|
109
107
|
{
|
|
110
|
-
NSError *error;
|
|
111
108
|
[self showApplicationAlert];
|
|
112
|
-
|
|
109
|
+
XCTAssertNoThrow([[FBAlert alertWithApplication:self.testedApplication] accept]);
|
|
113
110
|
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
|
|
114
|
-
XCTAssertNil(error);
|
|
115
111
|
}
|
|
116
112
|
|
|
117
113
|
- (void)testAcceptingAlertWithCustomLocator
|
|
118
114
|
{
|
|
119
|
-
NSError *error;
|
|
120
115
|
[self showApplicationAlert];
|
|
121
116
|
[FBConfiguration setAcceptAlertButtonSelector:@"**/XCUIElementTypeButton[-1]"];
|
|
122
117
|
@try {
|
|
123
|
-
|
|
118
|
+
XCTAssertNoThrow([[FBAlert alertWithApplication:self.testedApplication] accept]);
|
|
124
119
|
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
|
|
125
|
-
XCTAssertNil(error);
|
|
126
120
|
} @finally {
|
|
127
121
|
[FBConfiguration setAcceptAlertButtonSelector:@""];
|
|
128
122
|
}
|
|
@@ -130,65 +124,52 @@
|
|
|
130
124
|
|
|
131
125
|
- (void)testDismissingAlert
|
|
132
126
|
{
|
|
133
|
-
NSError *error;
|
|
134
127
|
[self showApplicationAlert];
|
|
135
|
-
|
|
128
|
+
XCTAssertNoThrow([[FBAlert alertWithApplication:self.testedApplication] dismiss]);
|
|
136
129
|
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
|
|
137
|
-
XCTAssertNil(error);
|
|
138
130
|
}
|
|
139
131
|
|
|
140
132
|
- (void)testDismissingAlertWithCustomLocator
|
|
141
133
|
{
|
|
142
|
-
NSError *error;
|
|
143
134
|
[self showApplicationAlert];
|
|
144
135
|
[FBConfiguration setDismissAlertButtonSelector:@"**/XCUIElementTypeButton[-1]"];
|
|
145
136
|
@try {
|
|
146
|
-
|
|
137
|
+
XCTAssertNoThrow([[FBAlert alertWithApplication:self.testedApplication] dismiss]);
|
|
147
138
|
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
|
|
148
|
-
XCTAssertNil(error);
|
|
149
139
|
} @finally {
|
|
150
140
|
[FBConfiguration setDismissAlertButtonSelector:@""];
|
|
151
141
|
}
|
|
152
142
|
}
|
|
153
143
|
|
|
154
|
-
- (void)testAlertElement
|
|
155
|
-
{
|
|
156
|
-
[self showApplicationAlert];
|
|
157
|
-
XCUIElement *alertElement = [FBAlert alertWithApplication:self.testedApplication].alertElement;
|
|
158
|
-
XCTAssertTrue(alertElement.exists);
|
|
159
|
-
XCTAssertTrue(alertElement.elementType == XCUIElementTypeAlert);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
144
|
- (void)testNotificationAlert
|
|
163
145
|
{
|
|
164
|
-
|
|
165
|
-
XCTAssertNil(alert.text);
|
|
146
|
+
XCTAssertNil([FBAlert alertWithApplication:self.testedApplication].text);
|
|
166
147
|
[self.testedApplication.buttons[@"Create Notification Alert"] tap];
|
|
167
|
-
FBAssertWaitTillBecomesTrue(
|
|
148
|
+
FBAssertWaitTillBecomesTrue([FBAlert alertWithApplication:self.testedApplication].isPresent);
|
|
168
149
|
|
|
169
|
-
|
|
170
|
-
XCTAssertTrue([
|
|
150
|
+
NSString *text = [FBAlert alertWithApplication:self.testedApplication].text;
|
|
151
|
+
XCTAssertTrue([text containsString:@"Would Like to Send You Notifications"]);
|
|
152
|
+
XCTAssertTrue([text containsString:@"Notifications may include"]);
|
|
171
153
|
}
|
|
172
154
|
|
|
173
155
|
- (void)testCameraRollAlert
|
|
174
156
|
{
|
|
175
|
-
|
|
176
|
-
XCTAssertNil(alert.text);
|
|
157
|
+
XCTAssertNil([FBAlert alertWithApplication:self.testedApplication].text);
|
|
177
158
|
|
|
178
159
|
[self.testedApplication.buttons[@"Create Camera Roll Alert"] tap];
|
|
179
|
-
FBAssertWaitTillBecomesTrue(
|
|
160
|
+
FBAssertWaitTillBecomesTrue([FBAlert alertWithApplication:self.testedApplication].isPresent);
|
|
180
161
|
}
|
|
181
162
|
|
|
182
163
|
- (void)testGPSAccessAlert
|
|
183
164
|
{
|
|
184
|
-
|
|
185
|
-
XCTAssertNil(alert.text);
|
|
165
|
+
XCTAssertNil([FBAlert alertWithApplication:self.testedApplication].text);
|
|
186
166
|
|
|
187
167
|
[self.testedApplication.buttons[@"Create GPS access Alert"] tap];
|
|
188
|
-
FBAssertWaitTillBecomesTrue(
|
|
168
|
+
FBAssertWaitTillBecomesTrue([FBAlert alertWithApplication:self.testedApplication].isPresent);
|
|
189
169
|
|
|
190
|
-
|
|
191
|
-
XCTAssertTrue([
|
|
170
|
+
NSString *text = [FBAlert alertWithApplication:self.testedApplication].text;
|
|
171
|
+
XCTAssertTrue([text containsString:@"location"]);
|
|
172
|
+
XCTAssertTrue([text containsString:@"Yo Yo"]);
|
|
192
173
|
}
|
|
193
174
|
|
|
194
175
|
@end
|
|
@@ -14,6 +14,13 @@ extern NSString *const FBShowAlertForceTouchButtonName;
|
|
|
14
14
|
extern NSString *const FBTouchesCountLabelIdentifier;
|
|
15
15
|
extern NSString *const FBTapsCountLabelIdentifier;
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
Labels of the buttons on the integration app's main page, in their on-screen
|
|
19
|
+
(top to bottom) order. Update this in one place - WebDriverAgentTests/IntegrationApp/Resources/Base.lproj/Main.storyboard -
|
|
20
|
+
and here, rather than duplicating the list/count across individual tests.
|
|
21
|
+
*/
|
|
22
|
+
extern NSArray<NSString *> *const FBMainViewButtonLabels;
|
|
23
|
+
|
|
17
24
|
/**
|
|
18
25
|
XCTestCase helper class used for integration tests
|
|
19
26
|
*/
|
|
@@ -62,6 +69,15 @@ extern NSString *const FBTapsCountLabelIdentifier;
|
|
|
62
69
|
*/
|
|
63
70
|
- (void)goToScrollPageWithCells:(BOOL)showCells;
|
|
64
71
|
|
|
72
|
+
/**
|
|
73
|
+
Navigates integration app to a page containing a 70-level-deep chain of
|
|
74
|
+
nested elements (otherElements[@"view_0"]...otherElements[@"view_69"]),
|
|
75
|
+
each nested directly inside the previous one. Intended as a fixture for
|
|
76
|
+
performance testing of element lookups (e.g. class chain locators) against
|
|
77
|
+
a deep accessibility tree.
|
|
78
|
+
*/
|
|
79
|
+
- (void)goToDeepHierarchyPage;
|
|
80
|
+
|
|
65
81
|
/**
|
|
66
82
|
Verifies no alerts are present on the page.
|
|
67
83
|
If an alert exists then it is going to be dismissed.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
#import "FBAlert.h"
|
|
12
12
|
#import "FBTestMacros.h"
|
|
13
|
+
#import "FBExceptions.h"
|
|
13
14
|
#import "FBIntegrationTestCase.h"
|
|
14
15
|
#import "FBConfiguration.h"
|
|
15
16
|
#import "FBMacros.h"
|
|
@@ -26,6 +27,14 @@ NSString *const FBShowSheetAlertButtonName = @"Create Sheet Alert";
|
|
|
26
27
|
NSString *const FBShowAlertForceTouchButtonName = @"Create Alert (Force Touch)";
|
|
27
28
|
NSString *const FBTouchesCountLabelIdentifier = @"numberOfTouchesLabel";
|
|
28
29
|
NSString *const FBTapsCountLabelIdentifier = @"numberOfTapsLabel";
|
|
30
|
+
NSArray<NSString *> *const FBMainViewButtonLabels = @[
|
|
31
|
+
@"Alerts",
|
|
32
|
+
@"Deadlock app",
|
|
33
|
+
@"Attributes",
|
|
34
|
+
@"Scrolling",
|
|
35
|
+
@"Touch",
|
|
36
|
+
@"DeepHierarchy",
|
|
37
|
+
];
|
|
29
38
|
|
|
30
39
|
@interface FBIntegrationTestCase ()
|
|
31
40
|
@property (nonatomic, strong) XCUIApplication *testedApplication;
|
|
@@ -129,10 +138,28 @@ NSString *const FBTapsCountLabelIdentifier = @"numberOfTapsLabel";
|
|
|
129
138
|
FBAssertWaitTillBecomesTrue(self.testedApplication.staticTexts[@"3"].fb_isVisible);
|
|
130
139
|
}
|
|
131
140
|
|
|
141
|
+
- (void)goToDeepHierarchyPage
|
|
142
|
+
{
|
|
143
|
+
[self.testedApplication.buttons[@"DeepHierarchy"] tap];
|
|
144
|
+
[self.testedApplication fb_waitUntilStable];
|
|
145
|
+
// Not fb_isVisible: that runs a native visibility computation which is
|
|
146
|
+
// pathologically slow to resolve for a view nested 70 levels deep at a
|
|
147
|
+
// 1x1 frame. Existence in the accessibility tree is all this fixture
|
|
148
|
+
// actually needs to confirm navigation succeeded.
|
|
149
|
+
FBAssertWaitTillBecomesTrue(self.testedApplication.otherElements[@"view_0"].exists);
|
|
150
|
+
}
|
|
151
|
+
|
|
132
152
|
- (void)clearAlert
|
|
133
153
|
{
|
|
134
154
|
[self.testedApplication fb_waitUntilStable];
|
|
135
|
-
|
|
155
|
+
@try {
|
|
156
|
+
[[FBAlert alertWithApplication:self.testedApplication] dismiss];
|
|
157
|
+
} @catch (NSException *e) {
|
|
158
|
+
if (![e.name isEqualToString:FBAlertNotPresentException]) {
|
|
159
|
+
@throw e;
|
|
160
|
+
}
|
|
161
|
+
// No alert is present, nothing to clear
|
|
162
|
+
}
|
|
136
163
|
[self.testedApplication fb_waitUntilStable];
|
|
137
164
|
FBAssertWaitTillBecomesTrue(self.testedApplication.alerts.count == 0);
|
|
138
165
|
}
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
XCTAssertEqualObjects(buttonLabels.firstObject, @"Close");
|
|
68
68
|
XCTAssertNotNil([self.safariApp fb_descendantsMatchingXPathQuery:@"//XCUIElementTypeButton[@label='Close']"
|
|
69
69
|
shouldReturnAfterFirstMatch:YES].firstObject);
|
|
70
|
-
|
|
70
|
+
XCTAssertNoThrow([alert accept]);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
@end
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
- (void)testFindMatchesInElement
|
|
131
131
|
{
|
|
132
132
|
NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [FBXPath matchesWithRootElement:self.testedApplication forQuery:@"//XCUIElementTypeButton"];
|
|
133
|
-
XCTAssertEqual([matchingSnapshots count],
|
|
133
|
+
XCTAssertEqual([matchingSnapshots count], FBMainViewButtonLabels.count);
|
|
134
134
|
for (id<FBXCElementSnapshot> element in matchingSnapshots) {
|
|
135
135
|
XCTAssertTrue([[FBXCElementSnapshotWrapper ensureWrapped:element].wdType isEqualToString:@"XCUIElementTypeButton"]);
|
|
136
136
|
}
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
- (void)testFindMatchesInElementWithDotNotation
|
|
175
175
|
{
|
|
176
176
|
NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [FBXPath matchesWithRootElement:self.testedApplication forQuery:@".//XCUIElementTypeButton"];
|
|
177
|
-
XCTAssertEqual([matchingSnapshots count],
|
|
177
|
+
XCTAssertEqual([matchingSnapshots count], FBMainViewButtonLabels.count);
|
|
178
178
|
for (id<FBXCElementSnapshot> element in matchingSnapshots) {
|
|
179
179
|
XCTAssertTrue([[FBXCElementSnapshotWrapper ensureWrapped:element].wdType isEqualToString:@"XCUIElementTypeButton"]);
|
|
180
180
|
}
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
- (void)testFindMultipleMatchesWithMatchesFunction
|
|
233
233
|
{
|
|
234
234
|
[self assertXPathQuery:@"//XCUIElementTypeButton[matches(@label, '.*')]"
|
|
235
|
-
findsButtonLabels
|
|
235
|
+
findsButtonLabels:FBMainViewButtonLabels];
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
- (void)testInvalidXPathExtensionFunctionViaElementLookup
|
|
@@ -36,13 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
- (void)testDescendantsMatchingType
|
|
38
38
|
{
|
|
39
|
-
NSSet<NSString *> *expectedLabels = [NSSet setWithArray
|
|
40
|
-
@"Alerts",
|
|
41
|
-
@"Attributes",
|
|
42
|
-
@"Scrolling",
|
|
43
|
-
@"Deadlock app",
|
|
44
|
-
@"Touch",
|
|
45
|
-
]];
|
|
39
|
+
NSSet<NSString *> *expectedLabels = [NSSet setWithArray:FBMainViewButtonLabels];
|
|
46
40
|
NSArray<id<FBXCElementSnapshot>> *matchingSnapshots = [[FBXCElementSnapshotWrapper ensureWrapped:
|
|
47
41
|
[self.testedView fb_customSnapshot]]
|
|
48
42
|
fb_descendantsMatchingType:XCUIElementTypeButton];
|
|
@@ -41,13 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
- (void)testDescendantsWithClassName
|
|
43
43
|
{
|
|
44
|
-
NSSet<NSString *> *expectedLabels = [NSSet setWithArray
|
|
45
|
-
@"Alerts",
|
|
46
|
-
@"Attributes",
|
|
47
|
-
@"Scrolling",
|
|
48
|
-
@"Deadlock app",
|
|
49
|
-
@"Touch",
|
|
50
|
-
]];
|
|
44
|
+
NSSet<NSString *> *expectedLabels = [NSSet setWithArray:FBMainViewButtonLabels];
|
|
51
45
|
NSArray<XCUIElement *> *matchingSnapshots = [self.testedView fb_descendantsMatchingClassName:@"XCUIElementTypeButton"
|
|
52
46
|
shouldReturnAfterFirstMatch:NO];
|
|
53
47
|
XCTAssertEqual(matchingSnapshots.count, expectedLabels.count);
|
|
@@ -273,7 +267,7 @@
|
|
|
273
267
|
NSString *queryString =@"XCUIElementTypeWindow/XCUIElementTypeOther/**/XCUIElementTypeButton";
|
|
274
268
|
matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString
|
|
275
269
|
shouldReturnAfterFirstMatch:NO];
|
|
276
|
-
XCTAssertEqual(matchingSnapshots.count,
|
|
270
|
+
XCTAssertEqual(matchingSnapshots.count, FBMainViewButtonLabels.count); // /XCUIElementTypeButton
|
|
277
271
|
for (XCUIElement *matchingSnapshot in matchingSnapshots) {
|
|
278
272
|
XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton);
|
|
279
273
|
}
|
|
@@ -292,7 +286,7 @@
|
|
|
292
286
|
matchingSnapshots = [self.testedApplication fb_descendantsMatchingClassChain:queryString
|
|
293
287
|
shouldReturnAfterFirstMatch:NO];
|
|
294
288
|
}
|
|
295
|
-
XCTAssertEqual(matchingSnapshots.count,
|
|
289
|
+
XCTAssertEqual(matchingSnapshots.count, FBMainViewButtonLabels.count); // /XCUIElementTypeButton
|
|
296
290
|
for (XCUIElement *matchingSnapshot in matchingSnapshots) {
|
|
297
291
|
XCTAssertEqual(matchingSnapshot.elementType, XCUIElementTypeButton);
|
|
298
292
|
}
|
|
@@ -376,7 +370,7 @@
|
|
|
376
370
|
|
|
377
371
|
XCTAssertEqual(matchingSnapshots.count, 1);
|
|
378
372
|
XCTAssertEqual(matchingSnapshots.lastObject.elementType, XCUIElementTypeButton);
|
|
379
|
-
|
|
373
|
+
XCTAssertEqualObjects(matchingSnapshots.lastObject.label, FBMainViewButtonLabels.lastObject);
|
|
380
374
|
|
|
381
375
|
matchingSnapshots = [self.testedView fb_descendantsMatchingClassChain:@"XCUIElementTypeButton[-10]"
|
|
382
376
|
shouldReturnAfterFirstMatch:YES];
|
|
@@ -470,3 +464,75 @@
|
|
|
470
464
|
}
|
|
471
465
|
|
|
472
466
|
@end
|
|
467
|
+
|
|
468
|
+
@interface XCUIElementFBFindTests_DeepHierarchyPage : FBIntegrationTestCase
|
|
469
|
+
@end
|
|
470
|
+
@implementation XCUIElementFBFindTests_DeepHierarchyPage
|
|
471
|
+
|
|
472
|
+
- (void)setUp
|
|
473
|
+
{
|
|
474
|
+
[super setUp];
|
|
475
|
+
static dispatch_once_t onceToken;
|
|
476
|
+
dispatch_once(&onceToken, ^{
|
|
477
|
+
[self launchApplication];
|
|
478
|
+
[self goToDeepHierarchyPage];
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// No intermediate segment has a position here (only the final one does), so
|
|
483
|
+
// this exercises the query-based class chain strategy - the fast path for
|
|
484
|
+
// the common case. See fb_hasIntermediatePosition: in XCUIElement+FBClassChain.m.
|
|
485
|
+
- (void)testClassChainWithoutIntermediatePositionOnDeepHierarchy
|
|
486
|
+
{
|
|
487
|
+
NSString *query = @"**/XCUIElementTypeOther[`label BEGINSWITH \"View 19\"`]/XCUIElementTypeOther[1]";
|
|
488
|
+
NSArray<XCUIElement *> *matches = [self.testedApplication fb_descendantsMatchingClassChain:query
|
|
489
|
+
shouldReturnAfterFirstMatch:NO];
|
|
490
|
+
XCTAssertEqual(matches.count, 1);
|
|
491
|
+
XCTAssertEqualObjects(matches.firstObject.label, @"View 20");
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// The first segment here has an explicit position and is not the last
|
|
495
|
+
// segment in the chain, so this exercises the snapshot-walk class chain
|
|
496
|
+
// strategy - the one that avoids paying an extra accessibility round trip
|
|
497
|
+
// per intermediate indexed segment.
|
|
498
|
+
- (void)testClassChainWithIntermediatePositionOnDeepHierarchy
|
|
499
|
+
{
|
|
500
|
+
NSString *query = @"**/XCUIElementTypeOther[`label == \"View 10\"`][1]/**/XCUIElementTypeOther[`label BEGINSWITH \"View 19\"`]";
|
|
501
|
+
NSArray<XCUIElement *> *matches = [self.testedApplication fb_descendantsMatchingClassChain:query
|
|
502
|
+
shouldReturnAfterFirstMatch:NO];
|
|
503
|
+
XCTAssertEqual(matches.count, 1);
|
|
504
|
+
XCTAssertEqualObjects(matches.firstObject.label, @"View 19");
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Exercises the snapshot-walk strategy with several intermediate positions
|
|
508
|
+
// (the shape it exists for), as opposed to testPerformanceOfClassChainLookupOnDeepHierarchy
|
|
509
|
+
// above, which only has a position on the final segment and stays on the
|
|
510
|
+
// query-based strategy. Kept within the default snapshotMaxDepth (50)
|
|
511
|
+
// combined with the app's own chrome depth above this fixture's root -
|
|
512
|
+
// going deeper hit a native kAXErrorIllegalArgument even after raising
|
|
513
|
+
// snapshotMaxDepth, which looks like a platform-level limitation
|
|
514
|
+
// independent of this lookup, not something to route around here.
|
|
515
|
+
- (void)testPerformanceOfMultiCheckpointClassChainLookupOnDeepHierarchy
|
|
516
|
+
{
|
|
517
|
+
NSString *query = @"**/XCUIElementTypeOther[`label == \"View 5\"`][1]/**/XCUIElementTypeOther[`label == \"View 15\"`][1]/**/XCUIElementTypeOther[`label == \"View 25\"`][1]/**/XCUIElementTypeOther[`label BEGINSWITH \"View 30\"`]";
|
|
518
|
+
[self measureBlock:^{
|
|
519
|
+
NSArray<XCUIElement *> *matches = [self.testedApplication fb_descendantsMatchingClassChain:query
|
|
520
|
+
shouldReturnAfterFirstMatch:NO];
|
|
521
|
+
XCTAssertEqual(matches.count, 1);
|
|
522
|
+
}];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Not a strict pass/fail assertion (timings vary across machines/CI) - this
|
|
526
|
+
// records a measurement baseline so future regressions on this lookup show
|
|
527
|
+
// up in Xcode's test reports.
|
|
528
|
+
- (void)testPerformanceOfClassChainLookupOnDeepHierarchy
|
|
529
|
+
{
|
|
530
|
+
NSString *query = @"**/XCUIElementTypeOther[`label BEGINSWITH \"View 19\"`]/XCUIElementTypeOther[1]";
|
|
531
|
+
[self measureBlock:^{
|
|
532
|
+
NSArray<XCUIElement *> *matches = [self.testedApplication fb_descendantsMatchingClassChain:query
|
|
533
|
+
shouldReturnAfterFirstMatch:NO];
|
|
534
|
+
XCTAssertEqual(matches.count, 1);
|
|
535
|
+
}];
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
@end
|