appium-webdriveragent 3.15.0 → 3.17.0
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/PrivateHeaders/XCTest/XCTestManager_ManagerInterface-Protocol.h +4 -2
- package/PrivateHeaders/XCTest/XCUIScreen.h +4 -3
- package/WebDriverAgent.xcodeproj/project.pbxproj +36 -0
- package/WebDriverAgent.xcodeproj/project.xcworkspace/xcuserdata/kazuaki.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/WebDriverAgent.xcodeproj/xcuserdata/kazuaki.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +1369 -500
- package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +9 -6
- package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +4 -6
- package/WebDriverAgentLib/Categories/XCUIApplication+FBUIInterruptions.h +23 -0
- package/WebDriverAgentLib/Categories/XCUIApplication+FBUIInterruptions.m +32 -0
- package/WebDriverAgentLib/Commands/FBDebugCommands.m +6 -3
- package/WebDriverAgentLib/Utilities/FBConfiguration.h +5 -0
- package/WebDriverAgentLib/Utilities/FBConfiguration.m +6 -0
- package/WebDriverAgentLib/Utilities/FBMjpegServer.m +1 -1
- package/WebDriverAgentLib/Utilities/FBReflectionUtils.h +24 -0
- package/WebDriverAgentLib/Utilities/FBReflectionUtils.m +32 -0
- package/WebDriverAgentLib/Utilities/FBScreenshot.h +1 -1
- package/WebDriverAgentLib/Utilities/FBScreenshot.m +3 -3
- package/WebDriverAgentLib/Utilities/FBXCAXClientProxy.m +5 -23
- package/WebDriverAgentLib/Utilities/FBXMLGenerationOptions.h +46 -0
- package/WebDriverAgentLib/Utilities/FBXMLGenerationOptions.m +26 -0
- package/WebDriverAgentLib/Utilities/FBXPath.h +4 -3
- package/WebDriverAgentLib/Utilities/FBXPath.m +65 -28
- package/WebDriverAgentRunner/UITestingUITests.m +1 -0
- package/WebDriverAgentRunner-Runner.app.zip +0 -0
- package/WebDriverAgentTests/IntegrationTests/FBAlertTests.m +1 -0
- package/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m +16 -2
- package/WebDriverAgentTests/UnitTests/FBXPathTests.m +28 -15
- package/package.json +2 -2
- package/WebDriverAgentLib/.DS_Store +0 -0
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
@class XCElementSnapshot;
|
|
13
13
|
@class XCAccessibilityElement;
|
|
14
|
+
@class FBXMLGenerationOptions;
|
|
14
15
|
|
|
15
16
|
NS_ASSUME_NONNULL_BEGIN
|
|
16
17
|
|
|
@@ -45,18 +46,20 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
45
46
|
- (NSDictionary *)fb_accessibilityTree;
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
|
-
Return application elements tree in form of xml string
|
|
49
|
+
Return application elements tree in a form of xml string
|
|
50
|
+
with default options.
|
|
51
|
+
|
|
52
|
+
@return nil if there was a failure while retriveing the page source.
|
|
49
53
|
*/
|
|
50
54
|
- (nullable NSString *)fb_xmlRepresentation;
|
|
51
55
|
|
|
52
56
|
/**
|
|
53
|
-
Return application elements tree in form of xml string
|
|
57
|
+
Return application elements tree in a form of xml string
|
|
54
58
|
|
|
55
|
-
@param
|
|
56
|
-
|
|
57
|
-
@returns The XML representation of the current element as a string
|
|
59
|
+
@param options Optional values that affect the resulting XML generation process.
|
|
60
|
+
@return nil if there was a failure while retriveing the page source.
|
|
58
61
|
*/
|
|
59
|
-
- (NSString *)
|
|
62
|
+
- (nullable NSString *)fb_xmlRepresentationWithOptions:(nullable FBXMLGenerationOptions *)options;
|
|
60
63
|
|
|
61
64
|
/**
|
|
62
65
|
Return application elements tree in form of internal XCTest debugDescription string
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
#import "FBXPath.h"
|
|
21
21
|
#import "FBXCTestDaemonsProxy.h"
|
|
22
22
|
#import "FBXCAXClientProxy.h"
|
|
23
|
+
#import "FBXMLGenerationOptions.h"
|
|
23
24
|
#import "XCAccessibilityElement.h"
|
|
24
25
|
#import "XCElementSnapshot+FBHelpers.h"
|
|
25
26
|
#import "XCUIDevice+FBHelpers.h"
|
|
@@ -180,15 +181,12 @@ static NSString* const FBUnknownBundleId = @"unknown";
|
|
|
180
181
|
|
|
181
182
|
- (NSString *)fb_xmlRepresentation
|
|
182
183
|
{
|
|
183
|
-
return [
|
|
184
|
+
return [self fb_xmlRepresentationWithOptions:nil];
|
|
184
185
|
}
|
|
185
186
|
|
|
186
|
-
- (NSString *)
|
|
187
|
+
- (NSString *)fb_xmlRepresentationWithOptions:(FBXMLGenerationOptions *)options
|
|
187
188
|
{
|
|
188
|
-
|
|
189
|
-
#pragma clang diagnostic ignored "-Wnullable-to-nonnull-conversion"
|
|
190
|
-
return [FBXPath xmlStringWithRootElement:self excludingAttributes:excludedAttributes];
|
|
191
|
-
#pragma clang diagnostic pop
|
|
189
|
+
return [FBXPath xmlStringWithRootElement:self options:options];
|
|
192
190
|
}
|
|
193
191
|
|
|
194
192
|
- (NSString *)fb_descriptionRepresentation
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#import <XCTest/XCTest.h>
|
|
11
|
+
|
|
12
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
13
|
+
|
|
14
|
+
@interface XCUIApplication (FBUIInterruptions)
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Disables automatic UI interruptions handling for all applications.
|
|
18
|
+
*/
|
|
19
|
+
+ (void)fb_disableUIInterruptionsHandling;
|
|
20
|
+
|
|
21
|
+
@end
|
|
22
|
+
|
|
23
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#import "XCUIApplication+FBUIInterruptions.h"
|
|
11
|
+
|
|
12
|
+
#import "FBReflectionUtils.h"
|
|
13
|
+
#import "XCUIApplication.h"
|
|
14
|
+
|
|
15
|
+
@implementation XCUIApplication (FBUIInterruptions)
|
|
16
|
+
|
|
17
|
+
- (BOOL)fb_doesNotHandleUIInterruptions
|
|
18
|
+
{
|
|
19
|
+
return YES;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
+ (void)fb_disableUIInterruptionsHandling
|
|
23
|
+
{
|
|
24
|
+
static dispatch_once_t onceToken;
|
|
25
|
+
dispatch_once(&onceToken, ^{
|
|
26
|
+
FBReplaceMethod([self class],
|
|
27
|
+
@selector(doesNotHandleUIInterruptions),
|
|
28
|
+
@selector(fb_doesNotHandleUIInterruptions));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@end
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#import "FBApplication.h"
|
|
13
13
|
#import "FBRouteRequest.h"
|
|
14
14
|
#import "FBSession.h"
|
|
15
|
+
#import "FBXMLGenerationOptions.h"
|
|
15
16
|
#import "XCUIApplication+FBHelpers.h"
|
|
16
17
|
#import "XCUIElement+FBUtilities.h"
|
|
17
18
|
#import "FBXPath.h"
|
|
@@ -43,14 +44,16 @@ static NSString *const SOURCE_FORMAT_DESCRIPTION = @"description";
|
|
|
43
44
|
// This method might be called without session
|
|
44
45
|
FBApplication *application = request.session.activeApplication ?: FBApplication.fb_activeApplication;
|
|
45
46
|
NSString *sourceType = request.parameters[@"format"] ?: SOURCE_FORMAT_XML;
|
|
47
|
+
NSString *sourceScope = request.parameters[@"scope"];
|
|
46
48
|
id result;
|
|
47
49
|
if ([sourceType caseInsensitiveCompare:SOURCE_FORMAT_XML] == NSOrderedSame) {
|
|
48
50
|
NSArray<NSString *> *excludedAttributes = nil == request.parameters[@"excluded_attributes"]
|
|
49
51
|
? nil
|
|
50
52
|
: [request.parameters[@"excluded_attributes"] componentsSeparatedByString:@","];
|
|
51
|
-
result =
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
result = [application fb_xmlRepresentationWithOptions:
|
|
54
|
+
[[[FBXMLGenerationOptions new]
|
|
55
|
+
withExcludedAttributes:excludedAttributes]
|
|
56
|
+
withScope:sourceScope]];
|
|
54
57
|
} else if ([sourceType caseInsensitiveCompare:SOURCE_FORMAT_JSON] == NSOrderedSame) {
|
|
55
58
|
result = application.fb_tree;
|
|
56
59
|
} else if ([sourceType caseInsensitiveCompare:SOURCE_FORMAT_DESCRIPTION] == NSOrderedSame) {
|
|
@@ -118,6 +118,11 @@ extern NSString *const FBSnapshotMaxDepthKey;
|
|
|
118
118
|
*/
|
|
119
119
|
+ (BOOL)verboseLoggingEnabled;
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
Disables automatic handling of XCTest UI interruptions.
|
|
123
|
+
*/
|
|
124
|
+
+ (void)disableApplicationUIInterruptionsHandling;
|
|
125
|
+
|
|
121
126
|
/**
|
|
122
127
|
* Configure keyboards preference to make test running stable
|
|
123
128
|
*/
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
#import "XCTestPrivateSymbols.h"
|
|
18
18
|
#import "XCElementSnapshot.h"
|
|
19
19
|
#import "XCTestConfiguration.h"
|
|
20
|
+
#import "XCUIApplication+FBUIInterruptions.h"
|
|
20
21
|
|
|
21
22
|
static NSUInteger const DefaultStartingPort = 8100;
|
|
22
23
|
static NSUInteger const DefaultMjpegServerPort = 9100;
|
|
@@ -74,6 +75,11 @@ static UIInterfaceOrientation FBScreenshotOrientation = UIInterfaceOrientationUn
|
|
|
74
75
|
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"XCTDisableRemoteQueryEvaluation"];
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
+ (void)disableApplicationUIInterruptionsHandling
|
|
79
|
+
{
|
|
80
|
+
[XCUIApplication fb_disableUIInterruptionsHandling];
|
|
81
|
+
}
|
|
82
|
+
|
|
77
83
|
+ (void)enableXcTestDebugLogs
|
|
78
84
|
{
|
|
79
85
|
((XCTestConfiguration *)XCTestConfiguration.activeTestConfiguration).emitOSLogs = YES;
|
|
@@ -33,7 +33,7 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
|
|
|
33
33
|
@property (nonatomic, readonly) NSMutableArray<GCDAsyncSocket *> *listeningClients;
|
|
34
34
|
@property (nonatomic, readonly) mach_timebase_info_data_t timebaseInfo;
|
|
35
35
|
@property (nonatomic, readonly) FBImageIOScaler *imageScaler;
|
|
36
|
-
@property (nonatomic, readonly)
|
|
36
|
+
@property (nonatomic, readonly) long long mainScreenID;
|
|
37
37
|
|
|
38
38
|
@end
|
|
39
39
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#import <Foundation/Foundation.h>
|
|
11
|
+
|
|
12
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Swizzles the implemntation of originalSelector with the swizzledSelector for the given class.
|
|
16
|
+
* Both methods must belong to this class.
|
|
17
|
+
*
|
|
18
|
+
* @param cls The class where to swizzle
|
|
19
|
+
* @param originalSelector original method selector
|
|
20
|
+
* @paramswizzledSelector swizzled method selector
|
|
21
|
+
*/
|
|
22
|
+
void FBReplaceMethod(Class cls, SEL originalSelector, SEL swizzledSelector);
|
|
23
|
+
|
|
24
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#import "FBReflectionUtils.h"
|
|
11
|
+
|
|
12
|
+
#import <objc/runtime.h>
|
|
13
|
+
|
|
14
|
+
void FBReplaceMethod(Class class, SEL originalSelector, SEL swizzledSelector) {
|
|
15
|
+
Method originalMethod = class_getInstanceMethod(class, originalSelector);
|
|
16
|
+
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
|
|
17
|
+
|
|
18
|
+
BOOL didAddMethod =
|
|
19
|
+
class_addMethod(class,
|
|
20
|
+
originalSelector,
|
|
21
|
+
method_getImplementation(swizzledMethod),
|
|
22
|
+
method_getTypeEncoding(swizzledMethod));
|
|
23
|
+
|
|
24
|
+
if (didAddMethod) {
|
|
25
|
+
class_replaceMethod(class,
|
|
26
|
+
swizzledSelector,
|
|
27
|
+
method_getImplementation(originalMethod),
|
|
28
|
+
method_getTypeEncoding(originalMethod));
|
|
29
|
+
} else {
|
|
30
|
+
method_exchangeImplementations(originalMethod, swizzledMethod);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -51,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
51
51
|
@param error If there is an error, upon return contains an NSError object that describes the problem.
|
|
52
52
|
@return Device screenshot as PNG- or JPG-encoded data or nil in case of failure
|
|
53
53
|
*/
|
|
54
|
-
+ (nullable NSData *)takeInOriginalResolutionWithScreenID:(
|
|
54
|
+
+ (nullable NSData *)takeInOriginalResolutionWithScreenID:(long long)screenID
|
|
55
55
|
compressionQuality:(CGFloat)compressionQuality
|
|
56
56
|
uti:(NSString *)uti
|
|
57
57
|
timeout:(NSTimeInterval)timeout
|
|
@@ -123,7 +123,7 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
|
|
|
123
123
|
return screenshotData;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
+ (NSData *)takeWithScreenID:(
|
|
126
|
+
+ (NSData *)takeWithScreenID:(long long)screenID
|
|
127
127
|
scale:(CGFloat)scale
|
|
128
128
|
compressionQuality:(CGFloat)compressionQuality
|
|
129
129
|
rect:(CGRect)rect
|
|
@@ -146,7 +146,7 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
|
|
|
146
146
|
error:error];
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
+ (NSData *)takeInOriginalResolutionWithScreenID:(
|
|
149
|
+
+ (NSData *)takeInOriginalResolutionWithScreenID:(long long)screenID
|
|
150
150
|
compressionQuality:(CGFloat)compressionQuality
|
|
151
151
|
uti:(NSString *)uti
|
|
152
152
|
timeout:(NSTimeInterval)timeout
|
|
@@ -254,7 +254,7 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
|
|
|
254
254
|
return imageEncoding;
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
+ (nullable id)screenshotRequestWithScreenID:(
|
|
257
|
+
+ (nullable id)screenshotRequestWithScreenID:(long long)screenID
|
|
258
258
|
rect:(struct CGRect)rect
|
|
259
259
|
uti:(NSString *)uti
|
|
260
260
|
compressionQuality:(CGFloat)compressionQuality
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
#import "FBConfiguration.h"
|
|
15
15
|
#import "FBLogger.h"
|
|
16
|
+
#import "FBMacros.h"
|
|
17
|
+
#import "FBReflectionUtils.h"
|
|
16
18
|
#import "XCAXClient_iOS.h"
|
|
17
19
|
#import "XCUIDevice.h"
|
|
18
|
-
#import "FBMacros.h"
|
|
19
20
|
|
|
20
21
|
static id FBAXClient = nil;
|
|
21
22
|
|
|
@@ -35,28 +36,9 @@ static id FBAXClient = nil;
|
|
|
35
36
|
{
|
|
36
37
|
static dispatch_once_t onceToken;
|
|
37
38
|
dispatch_once(&onceToken, ^{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
SEL swizzledSelector = @selector(fb_getParametersForElementSnapshot);
|
|
42
|
-
|
|
43
|
-
Method originalMethod = class_getInstanceMethod(class, originalSelector);
|
|
44
|
-
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
|
|
45
|
-
|
|
46
|
-
BOOL didAddMethod =
|
|
47
|
-
class_addMethod(class,
|
|
48
|
-
originalSelector,
|
|
49
|
-
method_getImplementation(swizzledMethod),
|
|
50
|
-
method_getTypeEncoding(swizzledMethod));
|
|
51
|
-
|
|
52
|
-
if (didAddMethod) {
|
|
53
|
-
class_replaceMethod(class,
|
|
54
|
-
swizzledSelector,
|
|
55
|
-
method_getImplementation(originalMethod),
|
|
56
|
-
method_getTypeEncoding(originalMethod));
|
|
57
|
-
} else {
|
|
58
|
-
method_exchangeImplementations(originalMethod, swizzledMethod);
|
|
59
|
-
}
|
|
39
|
+
SEL originalParametersSelector = @selector(defaultParameters);
|
|
40
|
+
SEL swizzledParametersSelector = @selector(fb_getParametersForElementSnapshot);
|
|
41
|
+
FBReplaceMethod([self class], originalParametersSelector, swizzledParametersSelector);
|
|
60
42
|
});
|
|
61
43
|
}
|
|
62
44
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#import <Foundation/Foundation.h>
|
|
11
|
+
|
|
12
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
13
|
+
|
|
14
|
+
@interface FBXMLGenerationOptions : NSObject
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
XML buidling scope. Passing nil means the XML should be built in the default scope,
|
|
18
|
+
i.e no changes to the original tree structore. If the scope is provided then the resulting
|
|
19
|
+
XML tree will be put under the root, which name is equal to the given scope value.
|
|
20
|
+
*/
|
|
21
|
+
@property (nonatomic, nullable) NSString *scope;
|
|
22
|
+
/**
|
|
23
|
+
The list of attribute names to exclude from the resulting document.
|
|
24
|
+
Passing nil means all the available attributes should be included
|
|
25
|
+
*/
|
|
26
|
+
@property (nonatomic, nullable) NSArray<NSString *> *excludedAttributes;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
Allows to provide XML scope.
|
|
30
|
+
|
|
31
|
+
@param scope See the property description above
|
|
32
|
+
@return self instance for chaining
|
|
33
|
+
*/
|
|
34
|
+
- (FBXMLGenerationOptions *)withScope:(nullable NSString *)scope;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
Allows to provide a list of excluded XML attributes.
|
|
38
|
+
|
|
39
|
+
@param excludedAttributes See the property description above
|
|
40
|
+
@return self instance for chaining
|
|
41
|
+
*/
|
|
42
|
+
- (FBXMLGenerationOptions *)withExcludedAttributes:(nullable NSArray<NSString *> *)excludedAttributes;
|
|
43
|
+
|
|
44
|
+
@end
|
|
45
|
+
|
|
46
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#import "FBXMLGenerationOptions.h"
|
|
11
|
+
|
|
12
|
+
@implementation FBXMLGenerationOptions
|
|
13
|
+
|
|
14
|
+
- (FBXMLGenerationOptions *)withScope:(NSString *)scope
|
|
15
|
+
{
|
|
16
|
+
self.scope = scope;
|
|
17
|
+
return self;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
- (FBXMLGenerationOptions *)withExcludedAttributes:(NSArray<NSString *> *)excludedAttributes
|
|
21
|
+
{
|
|
22
|
+
self.excludedAttributes = excludedAttributes;
|
|
23
|
+
return self;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@end
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
#pragma clang diagnostic pop
|
|
28
28
|
#endif
|
|
29
29
|
|
|
30
|
+
@class FBXMLGenerationOptions;
|
|
31
|
+
|
|
30
32
|
NS_ASSUME_NONNULL_BEGIN
|
|
31
33
|
|
|
32
34
|
@interface FBXPath : NSObject
|
|
@@ -47,12 +49,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
47
49
|
representation, which is used for XPath search
|
|
48
50
|
|
|
49
51
|
@param root the root element
|
|
50
|
-
@param
|
|
51
|
-
Passing nil means all the available attributes should be included
|
|
52
|
+
@param options Optional values that affect the resulting XML creation process
|
|
52
53
|
@return valid XML document as string or nil in case of failure
|
|
53
54
|
*/
|
|
54
55
|
+ (nullable NSString *)xmlStringWithRootElement:(id<FBElement>)root
|
|
55
|
-
|
|
56
|
+
options:(nullable FBXMLGenerationOptions *)options;
|
|
56
57
|
|
|
57
58
|
@end
|
|
58
59
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#import "FBConfiguration.h"
|
|
13
13
|
#import "FBExceptions.h"
|
|
14
14
|
#import "FBLogger.h"
|
|
15
|
+
#import "FBXMLGenerationOptions.h"
|
|
15
16
|
#import "NSString+FBXMLSafeString.h"
|
|
16
17
|
#import "XCElementSnapshot+FBHelpers.h"
|
|
17
18
|
#import "XCUIElement.h"
|
|
@@ -117,15 +118,45 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
+ (nullable NSString *)xmlStringWithRootElement:(id<FBElement>)root
|
|
120
|
-
|
|
121
|
+
options:(nullable FBXMLGenerationOptions *)options
|
|
121
122
|
{
|
|
122
123
|
xmlDocPtr doc;
|
|
123
124
|
xmlTextWriterPtr writer = xmlNewTextWriterDoc(&doc, 0);
|
|
124
|
-
int rc =
|
|
125
|
+
int rc = xmlTextWriterStartDocument(writer, NULL, _UTF8Encoding, NULL);
|
|
126
|
+
if (rc < 0) {
|
|
127
|
+
[FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartDocument. Error code: %d", rc];
|
|
128
|
+
} else {
|
|
129
|
+
BOOL hasScope = nil != options.scope && [options.scope length] > 0;
|
|
130
|
+
if (hasScope) {
|
|
131
|
+
rc = xmlTextWriterStartElement(writer,
|
|
132
|
+
(xmlChar *)[[self safeXmlStringWithString:options.scope] UTF8String]);
|
|
133
|
+
if (rc < 0) {
|
|
134
|
+
[FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartElement for the tag value '%@'. Error code: %d", options.scope, rc];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (rc >= 0) {
|
|
139
|
+
rc = [self xmlRepresentationWithRootElement:root
|
|
125
140
|
writer:writer
|
|
126
141
|
elementStore:nil
|
|
127
142
|
query:nil
|
|
128
|
-
excludingAttributes:excludedAttributes];
|
|
143
|
+
excludingAttributes:options.excludedAttributes];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (rc >= 0 && hasScope) {
|
|
147
|
+
rc = xmlTextWriterEndElement(writer);
|
|
148
|
+
if (rc < 0) {
|
|
149
|
+
[FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterEndElement. Error code: %d", rc];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (rc >= 0) {
|
|
154
|
+
rc = xmlTextWriterEndDocument(writer);
|
|
155
|
+
if (rc < 0) {
|
|
156
|
+
[FBLogger logFmt:@"Failed to invoke libxml2>xmlXPathNewContext. Error code: %d", rc];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
129
160
|
if (rc < 0) {
|
|
130
161
|
xmlFreeTextWriter(writer);
|
|
131
162
|
xmlFreeDoc(doc);
|
|
@@ -141,7 +172,8 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
141
172
|
return result;
|
|
142
173
|
}
|
|
143
174
|
|
|
144
|
-
+ (NSArray<XCElementSnapshot *> *)matchesWithRootElement:(id<FBElement>)root
|
|
175
|
+
+ (NSArray<XCElementSnapshot *> *)matchesWithRootElement:(id<FBElement>)root
|
|
176
|
+
forQuery:(NSString *)xpathQuery
|
|
145
177
|
{
|
|
146
178
|
xmlDocPtr doc;
|
|
147
179
|
|
|
@@ -151,11 +183,22 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
151
183
|
return [self throwException:FBXPathQueryEvaluationException forQuery:xpathQuery];
|
|
152
184
|
}
|
|
153
185
|
NSMutableDictionary *elementStore = [NSMutableDictionary dictionary];
|
|
154
|
-
int rc =
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
186
|
+
int rc = xmlTextWriterStartDocument(writer, NULL, _UTF8Encoding, NULL);
|
|
187
|
+
if (rc < 0) {
|
|
188
|
+
[FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterStartDocument. Error code: %d", rc];
|
|
189
|
+
} else {
|
|
190
|
+
rc = [self xmlRepresentationWithRootElement:root
|
|
191
|
+
writer:writer
|
|
192
|
+
elementStore:elementStore
|
|
193
|
+
query:xpathQuery
|
|
194
|
+
excludingAttributes:nil];
|
|
195
|
+
if (rc >= 0) {
|
|
196
|
+
rc = xmlTextWriterEndDocument(writer);
|
|
197
|
+
if (rc < 0) {
|
|
198
|
+
[FBLogger logFmt:@"Failed to invoke libxml2>xmlTextWriterEndDocument. Error code: %d", rc];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
159
202
|
if (rc < 0) {
|
|
160
203
|
xmlFreeTextWriter(writer);
|
|
161
204
|
xmlFreeDoc(doc);
|
|
@@ -169,7 +212,8 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
169
212
|
return [self throwException:FBInvalidXPathException forQuery:xpathQuery];
|
|
170
213
|
}
|
|
171
214
|
|
|
172
|
-
NSArray *matchingSnapshots = [self collectMatchingSnapshots:queryResult->nodesetval
|
|
215
|
+
NSArray *matchingSnapshots = [self collectMatchingSnapshots:queryResult->nodesetval
|
|
216
|
+
elementStore:elementStore];
|
|
173
217
|
xmlXPathFreeObject(queryResult);
|
|
174
218
|
xmlFreeTextWriter(writer);
|
|
175
219
|
xmlFreeDoc(doc);
|
|
@@ -179,7 +223,8 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
179
223
|
return matchingSnapshots;
|
|
180
224
|
}
|
|
181
225
|
|
|
182
|
-
+ (NSArray *)collectMatchingSnapshots:(xmlNodeSetPtr)nodeSet
|
|
226
|
+
+ (NSArray *)collectMatchingSnapshots:(xmlNodeSetPtr)nodeSet
|
|
227
|
+
elementStore:(NSMutableDictionary *)elementStore
|
|
183
228
|
{
|
|
184
229
|
if (xmlXPathNodeSetIsEmpty(nodeSet)) {
|
|
185
230
|
return @[];
|
|
@@ -243,26 +288,15 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
243
288
|
}
|
|
244
289
|
[FBLogger logFmt:@"The following attributes were requested to be included into the XML: %@", includedAttributes];
|
|
245
290
|
|
|
246
|
-
int rc =
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
rc = [self writeXmlWithRootElement:root
|
|
253
|
-
indexPath:(elementStore != nil ? topNodeIndexPath : nil)
|
|
254
|
-
elementStore:elementStore
|
|
255
|
-
includedAttributes:includedAttributes.copy
|
|
256
|
-
writer:writer];
|
|
291
|
+
int rc = [self writeXmlWithRootElement:root
|
|
292
|
+
indexPath:(elementStore != nil ? topNodeIndexPath : nil)
|
|
293
|
+
elementStore:elementStore
|
|
294
|
+
includedAttributes:includedAttributes.copy
|
|
295
|
+
writer:writer];
|
|
257
296
|
if (rc < 0) {
|
|
258
297
|
[FBLogger log:@"Failed to generate XML presentation of a screen element"];
|
|
259
298
|
return rc;
|
|
260
299
|
}
|
|
261
|
-
rc = xmlTextWriterEndDocument(writer);
|
|
262
|
-
if (rc < 0) {
|
|
263
|
-
[FBLogger logFmt:@"Failed to invoke libxml2>xmlXPathNewContext. Error code: %d", rc];
|
|
264
|
-
return rc;
|
|
265
|
-
}
|
|
266
300
|
return 0;
|
|
267
301
|
}
|
|
268
302
|
|
|
@@ -290,7 +324,10 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
290
324
|
return [str fb_xmlSafeStringWithReplacement:@""];
|
|
291
325
|
}
|
|
292
326
|
|
|
293
|
-
+ (int)recordElementAttributes:(xmlTextWriterPtr)writer
|
|
327
|
+
+ (int)recordElementAttributes:(xmlTextWriterPtr)writer
|
|
328
|
+
forElement:(XCElementSnapshot *)element
|
|
329
|
+
indexPath:(nullable NSString *)indexPath
|
|
330
|
+
includedAttributes:(nullable NSSet<Class> *)includedAttributes
|
|
294
331
|
{
|
|
295
332
|
for (Class attributeCls in FBElementAttribute.supportedAttributes) {
|
|
296
333
|
// include all supported attributes by default unless enumerated explicitly
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
[FBDebugLogDelegateDecorator decorateXCTestLogger];
|
|
26
26
|
[FBConfiguration disableRemoteQueryEvaluation];
|
|
27
27
|
[FBConfiguration configureDefaultKeyboardPreferences];
|
|
28
|
+
[FBConfiguration disableApplicationUIInterruptionsHandling];
|
|
28
29
|
if (NSProcessInfo.processInfo.environment[@"ENABLE_AUTOMATIC_SCREENSHOTS"]) {
|
|
29
30
|
[FBConfiguration enableScreenshots];
|
|
30
31
|
} else {
|
|
Binary file
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#import "FBTestMacros.h"
|
|
14
14
|
#import "FBXPath.h"
|
|
15
15
|
#import "FBXCodeCompatibility.h"
|
|
16
|
+
#import "FBXMLGenerationOptions.h"
|
|
16
17
|
#import "XCUIElement.h"
|
|
17
18
|
#import "XCUIElement+FBFind.h"
|
|
18
19
|
#import "XCUIElement+FBUtilities.h"
|
|
@@ -52,16 +53,29 @@
|
|
|
52
53
|
- (void)testSingleDescendantXMLRepresentation
|
|
53
54
|
{
|
|
54
55
|
XCElementSnapshot *snapshot = self.destinationSnapshot;
|
|
55
|
-
NSString *xmlStr = [FBXPath xmlStringWithRootElement:snapshot
|
|
56
|
+
NSString *xmlStr = [FBXPath xmlStringWithRootElement:snapshot options:nil];
|
|
56
57
|
XCTAssertNotNil(xmlStr);
|
|
57
58
|
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ type=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\"/>\n", snapshot.wdType, snapshot.wdType, snapshot.wdName, snapshot.wdLabel, snapshot.wdEnabled ? @"true" : @"false", snapshot.wdVisible ? @"true" : @"false", snapshot.wdAccessible ? @"true" : @"false", [snapshot.wdRect[@"x"] stringValue], [snapshot.wdRect[@"y"] stringValue], [snapshot.wdRect[@"width"] stringValue], [snapshot.wdRect[@"height"] stringValue], snapshot.wdIndex];
|
|
58
59
|
XCTAssertEqualObjects(xmlStr, expectedXml);
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
- (void)testSingleDescendantXMLRepresentationWithScope
|
|
63
|
+
{
|
|
64
|
+
XCElementSnapshot *snapshot = self.destinationSnapshot;
|
|
65
|
+
NSString *scope = @"AppiumAUT";
|
|
66
|
+
FBXMLGenerationOptions *options = [[FBXMLGenerationOptions new] withScope:scope];
|
|
67
|
+
NSString *xmlStr = [FBXPath xmlStringWithRootElement:snapshot options:options];
|
|
68
|
+
XCTAssertNotNil(xmlStr);
|
|
69
|
+
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@>\n <%@ type=\"%@\" name=\"%@\" label=\"%@\" enabled=\"%@\" visible=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\" index=\"%lu\"/>\n</%@>\n", scope, snapshot.wdType, snapshot.wdType, snapshot.wdName, snapshot.wdLabel, snapshot.wdEnabled ? @"true" : @"false", snapshot.wdVisible ? @"true" : @"false", snapshot.wdAccessible ? @"true" : @"false", [snapshot.wdRect[@"x"] stringValue], [snapshot.wdRect[@"y"] stringValue], [snapshot.wdRect[@"width"] stringValue], [snapshot.wdRect[@"height"] stringValue], snapshot.wdIndex, scope];
|
|
70
|
+
XCTAssertEqualObjects(xmlStr, expectedXml);
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
- (void)testSingleDescendantXMLRepresentationWithoutAttributes
|
|
62
74
|
{
|
|
63
75
|
XCElementSnapshot *snapshot = self.destinationSnapshot;
|
|
64
|
-
|
|
76
|
+
FBXMLGenerationOptions *options = [[FBXMLGenerationOptions new]
|
|
77
|
+
withExcludedAttributes:@[@"visible", @"enabled", @"index", @"blabla"]];
|
|
78
|
+
NSString *xmlStr = [FBXPath xmlStringWithRootElement:snapshot options:options];
|
|
65
79
|
XCTAssertNotNil(xmlStr);
|
|
66
80
|
NSString *expectedXml = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<%@ type=\"%@\" name=\"%@\" label=\"%@\" accessible=\"%@\" x=\"%@\" y=\"%@\" width=\"%@\" height=\"%@\"/>\n", snapshot.wdType, snapshot.wdType, snapshot.wdName, snapshot.wdLabel, snapshot.wdAccessible ? @"true" : @"false", [snapshot.wdRect[@"x"] stringValue], [snapshot.wdRect[@"y"] stringValue], [snapshot.wdRect[@"width"] stringValue], [snapshot.wdRect[@"height"] stringValue]];
|
|
67
81
|
XCTAssertEqualObjects(xmlStr, expectedXml);
|