appium-mac2-driver 3.0.1 → 3.2.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/CHANGELOG.md +14 -0
- package/README.md +27 -0
- package/WebDriverAgentMac/IntegrationTests/AMEditElementTests.m +2 -4
- package/WebDriverAgentMac/IntegrationTests/AMPasteboardTests.m +111 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Categories/XCUIElement+AMAttributes.h +4 -2
- package/WebDriverAgentMac/WebDriverAgentLib/Categories/XCUIElement+AMAttributes.m +30 -15
- package/WebDriverAgentMac/WebDriverAgentLib/Categories/XCUIElementQuery+AMHelpers.h +8 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Categories/XCUIElementQuery+AMHelpers.m +17 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Commands/FBCustomCommands.m +33 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Commands/FBSessionCommands.m +4 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMPasteboard.h +47 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMPasteboard.m +128 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMSettings.h +3 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMSettings.m +1 -1
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/FBConfiguration.h +3 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/FBConfiguration.m +11 -0
- package/WebDriverAgentMac/WebDriverAgentMac.xcodeproj/project.pbxproj +12 -0
- package/build/lib/commands/clipboard.d.ts +19 -0
- package/build/lib/commands/clipboard.d.ts.map +1 -0
- package/build/lib/commands/clipboard.js +31 -0
- package/build/lib/commands/clipboard.js.map +1 -0
- package/build/lib/driver.d.ts +24 -1
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +10 -0
- package/build/lib/driver.js.map +1 -1
- package/build/lib/execute-method-map.d.ts +13 -0
- package/build/lib/execute-method-map.d.ts.map +1 -1
- package/build/lib/execute-method-map.js +11 -0
- package/build/lib/execute-method-map.js.map +1 -1
- package/lib/commands/clipboard.ts +35 -0
- package/lib/driver.js +11 -0
- package/lib/execute-method-map.ts +11 -0
- package/npm-shrinkwrap.json +2221 -213
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [3.2.0](https://github.com/appium/appium-mac2-driver/compare/v3.1.0...v3.2.0) (2025-11-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Add a setting that allows to fetch full element text ([#347](https://github.com/appium/appium-mac2-driver/issues/347)) ([a8b9e7e](https://github.com/appium/appium-mac2-driver/commit/a8b9e7ee1c0b576c04eaafb8af0d8649330ffc3b))
|
|
7
|
+
|
|
8
|
+
## [3.1.0](https://github.com/appium/appium-mac2-driver/compare/v3.0.1...v3.1.0) (2025-10-01)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* Add clipboard support ([#346](https://github.com/appium/appium-mac2-driver/issues/346)) ([a3652fd](https://github.com/appium/appium-mac2-driver/commit/a3652fd2e29c68a30f677200ad8a2dd9148898d8))
|
|
14
|
+
|
|
1
15
|
## [3.0.1](https://github.com/appium/appium-mac2-driver/compare/v3.0.0...v3.0.1) (2025-09-28)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -702,6 +702,32 @@ Name | Type | Description | Example
|
|
|
702
702
|
id | number | Display identifier | 12345
|
|
703
703
|
isMain | boolean | Is `true` if the display is configured as a main system display | false
|
|
704
704
|
|
|
705
|
+
### macos: setClipboard
|
|
706
|
+
|
|
707
|
+
Set the macOS clipboard content as base64-encoded string. The existing clipboard content (if present) will be cleared.
|
|
708
|
+
|
|
709
|
+
#### Arguments
|
|
710
|
+
|
|
711
|
+
Name | Type | Required | Description | Example
|
|
712
|
+
--- | --- | --- | --- | ---
|
|
713
|
+
content | string | yes | The content to be set as base64 encoded string | hello
|
|
714
|
+
contentType | string | no | The type of the content to set. Only `plaintext` (default), `image` and `url` are supported. If set to `url`, then `content` must be a valid URL. If set to `image`, then `content` must contain a valid PNG or TIFF image payload. | url
|
|
715
|
+
|
|
716
|
+
### macos: getClipboard
|
|
717
|
+
|
|
718
|
+
Get the macOS clipboard content as base64-encoded string.
|
|
719
|
+
|
|
720
|
+
#### Arguments
|
|
721
|
+
|
|
722
|
+
Name | Type | Required | Description | Example
|
|
723
|
+
--- | --- | --- | --- | ---
|
|
724
|
+
contentType | string | no | The type of the content to get. Only `plaintext` (default), `image` and `url` are supported. | image
|
|
725
|
+
|
|
726
|
+
#### Returns
|
|
727
|
+
|
|
728
|
+
The actual clipboard content encoded into base64 string. An empty string is returned if the clipboard
|
|
729
|
+
contains no data for the given content type.
|
|
730
|
+
|
|
705
731
|
|
|
706
732
|
## Application Under Test Concept
|
|
707
733
|
|
|
@@ -823,6 +849,7 @@ Name | Type | Description
|
|
|
823
849
|
--- | --- | ---
|
|
824
850
|
boundElementsByIndex | boolean | Whether to use elements binding by index (`true`) or by accessibility identifier (the default setting, `false`). It makes sense to switch the binding strategy to workaround stale element reference errors containing `Identity Binding` text in their descriptions. See the corresponding [Stack Overflow discussion](https://stackoverflow.com/questions/49307513/meaning-of-allelementsboundbyaccessibilityelement) to know more details on the difference between these two binding strategies.
|
|
825
851
|
useDefaultUiInterruptionsHandling | boolean | Whether to use the default XCTest UI interruptions handling (`true`, the default setting) or to disable it for the [Application Under Test](#application-under-test-concept) (`false`). It makes sense to disable the default handler if it is necessary to validate the interrupting element's presence in your test or do some other actions on it rather than just closing the view implicitly. Check [this WWDC presentation](https://developer.apple.com/videos/play/wwdc2020/10220/) from Apple to get more details on the UI interruptions handling.
|
|
852
|
+
fetchFullText | boolean | Whether to use custom snapshotting mechanism to fetch full element's text payload instead of the first 512 chars. By default, this setting is set to false, which makes the driver to use the standard XCTest's snapshotting mechanism. It's fast, but may also cut long textual payloads of various UI element's in order to optimize internal UI hierarchy snapshotting performance. If the performance is less important for you that the ability to validate full element's text then consider enabling this setting while calling [Get Element Text](https://www.w3.org/TR/webdriver1/#dfn-get-element-text) API.
|
|
826
853
|
|
|
827
854
|
|
|
828
855
|
## Scripts
|
|
@@ -38,8 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
- (void)testSendingTextIntoNonFocusedEdit
|
|
40
40
|
{
|
|
41
|
-
|
|
42
|
-
XCUIElement *edit = [self.testedApplication.textFields matchingPredicate:predicate].firstMatch;
|
|
41
|
+
XCUIElement *edit = self.testedApplication.textFields.firstMatch;
|
|
43
42
|
NSString *text = @"yolo😎";
|
|
44
43
|
[edit am_setValue:text];
|
|
45
44
|
XCTAssertTrue(edit.am_hasKeyboardInputFocus);
|
|
@@ -50,8 +49,7 @@
|
|
|
50
49
|
|
|
51
50
|
- (void)testSendingTextIntoNonFocusedEditWithPlaceholderText
|
|
52
51
|
{
|
|
53
|
-
|
|
54
|
-
XCUIElement *edit = [self.testedApplication.textFields matchingPredicate:predicate].firstMatch;
|
|
52
|
+
XCUIElement *edit = self.testedApplication.textFields.firstMatch;
|
|
55
53
|
NSString *text = @"yolo😎";
|
|
56
54
|
[edit am_setValue:text];
|
|
57
55
|
XCTAssertTrue(edit.am_hasKeyboardInputFocus);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#import <XCTest/XCTest.h>
|
|
18
|
+
|
|
19
|
+
#import "AMIntegrationTestCase.h"
|
|
20
|
+
#import "AMPasteboard.h"
|
|
21
|
+
|
|
22
|
+
@interface AMPasteboardTests : AMIntegrationTestCase
|
|
23
|
+
@end
|
|
24
|
+
|
|
25
|
+
NSData *dataFromImage(NSImage *image) {
|
|
26
|
+
if (!image) return nil;
|
|
27
|
+
|
|
28
|
+
NSSize size = [image size];
|
|
29
|
+
NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc]
|
|
30
|
+
initWithBitmapDataPlanes:NULL
|
|
31
|
+
pixelsWide:size.width
|
|
32
|
+
pixelsHigh:size.height
|
|
33
|
+
bitsPerSample:8
|
|
34
|
+
samplesPerPixel:4
|
|
35
|
+
hasAlpha:YES
|
|
36
|
+
isPlanar:NO
|
|
37
|
+
colorSpaceName:NSCalibratedRGBColorSpace
|
|
38
|
+
bytesPerRow:0
|
|
39
|
+
bitsPerPixel:0];
|
|
40
|
+
if (!bitmapRep) return nil;
|
|
41
|
+
|
|
42
|
+
NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmapRep];
|
|
43
|
+
if (!context) return nil;
|
|
44
|
+
|
|
45
|
+
[NSGraphicsContext saveGraphicsState];
|
|
46
|
+
[NSGraphicsContext setCurrentContext:context];
|
|
47
|
+
[image drawInRect:NSMakeRect(0, 0, size.width, size.height)
|
|
48
|
+
fromRect:NSZeroRect
|
|
49
|
+
operation:NSCompositingOperationCopy
|
|
50
|
+
fraction:1.0];
|
|
51
|
+
[NSGraphicsContext restoreGraphicsState];
|
|
52
|
+
|
|
53
|
+
return [bitmapRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@implementation AMPasteboardTests
|
|
58
|
+
|
|
59
|
+
- (void)testPasteboardPlaintex
|
|
60
|
+
{
|
|
61
|
+
NSError *error;
|
|
62
|
+
NSString *expected = @"test";
|
|
63
|
+
XCTAssertTrue([AMPasteboard setData:[expected dataUsingEncoding:NSUTF8StringEncoding]
|
|
64
|
+
forType:@"plaintext"
|
|
65
|
+
error:&error]);
|
|
66
|
+
XCTAssertNil(error);
|
|
67
|
+
NSData *actual = [AMPasteboard dataForType:@"plaintext" error:&error];
|
|
68
|
+
XCTAssertNotNil(actual);
|
|
69
|
+
XCTAssertNil(error);
|
|
70
|
+
XCTAssertEqualObjects([[NSString alloc] initWithData:actual encoding:NSUTF8StringEncoding], expected);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
- (void)testPasteboardUrl
|
|
74
|
+
{
|
|
75
|
+
NSError *error;
|
|
76
|
+
NSString *expected = @"https://appium.io";
|
|
77
|
+
XCTAssertTrue([AMPasteboard setData:[expected dataUsingEncoding:NSUTF8StringEncoding]
|
|
78
|
+
forType:@"url"
|
|
79
|
+
error:&error]);
|
|
80
|
+
XCTAssertNil(error);
|
|
81
|
+
NSData *actual = [AMPasteboard dataForType:@"url" error:&error];
|
|
82
|
+
XCTAssertNotNil(actual);
|
|
83
|
+
XCTAssertNil(error);
|
|
84
|
+
XCTAssertEqualObjects([[NSString alloc] initWithData:actual encoding:NSUTF8StringEncoding], expected);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
- (void)testPasteboardImage
|
|
88
|
+
{
|
|
89
|
+
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)];
|
|
90
|
+
[image lockFocus];
|
|
91
|
+
[[NSColor clearColor] setFill];
|
|
92
|
+
NSRectFill(NSMakeRect(0, 0, 1, 1));
|
|
93
|
+
[image unlockFocus];
|
|
94
|
+
|
|
95
|
+
NSData *expected = dataFromImage(image);
|
|
96
|
+
if (nil == expected) {
|
|
97
|
+
XCTFail(@"imageData cannot be nil");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
NSError *error;
|
|
101
|
+
XCTAssertTrue([AMPasteboard setData:expected
|
|
102
|
+
forType:@"image"
|
|
103
|
+
error:&error]);
|
|
104
|
+
XCTAssertNil(error);
|
|
105
|
+
NSData *actual = [AMPasteboard dataForType:@"image" error:&error];
|
|
106
|
+
XCTAssertNotNil(actual);
|
|
107
|
+
XCTAssertNil(error);
|
|
108
|
+
XCTAssertEqualObjects(actual, expected);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@end
|
|
@@ -34,9 +34,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
34
34
|
- (NSDictionary<NSString *, NSNumber *> *)am_rect;
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
Element text
|
|
37
|
+
Element text. If no text is found then an empty string is returned.
|
|
38
|
+
|
|
39
|
+
@throws Error if `fetchFullText` setting is enabled and there was an error while fetching the snapshot
|
|
38
40
|
*/
|
|
39
|
-
- (
|
|
41
|
+
- (NSString *)am_text;
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
Element type represented as string
|
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
#import "XCUIElement+AMAttributes.h"
|
|
18
18
|
|
|
19
19
|
#import "AMGeometryUtils.h"
|
|
20
|
+
#import "FBConfiguration.h"
|
|
20
21
|
#import "FBElementTypeTransformer.h"
|
|
21
22
|
#import "FBElementUtils.h"
|
|
22
23
|
#import "FBExceptions.h"
|
|
23
24
|
#import "FBMacros.h"
|
|
25
|
+
#import "XCUIElementQuery+AMHelpers.h"
|
|
24
26
|
|
|
25
27
|
@implementation XCUIElement (AMAttributes)
|
|
26
28
|
|
|
@@ -64,23 +66,20 @@
|
|
|
64
66
|
|
|
65
67
|
- (NSString *)am_text
|
|
66
68
|
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return value;
|
|
69
|
+
if (!FBConfiguration.sharedConfiguration.fetchFullText) {
|
|
70
|
+
return [self am_textWithSource:self];
|
|
70
71
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return placeholderValue;
|
|
78
|
-
}
|
|
79
|
-
NSString *title = self.title;
|
|
80
|
-
if (nil != title && title.length > 0) {
|
|
81
|
-
return title;
|
|
72
|
+
|
|
73
|
+
NSError *error;
|
|
74
|
+
XCUIElementQuery *query = [self valueForKey:@"query"];
|
|
75
|
+
id<XCUIElementAttributes> snapshot = [query am_uniqueSnapshotWithError:&error];
|
|
76
|
+
if (nil != snapshot) {
|
|
77
|
+
return [self am_textWithSource:snapshot];
|
|
82
78
|
}
|
|
83
|
-
|
|
79
|
+
|
|
80
|
+
NSString *reason = [NSString stringWithFormat:@"Cannot extract the full text of '%@' element. Original error: %@",
|
|
81
|
+
self.description, error.description];
|
|
82
|
+
@throw [NSException exceptionWithName:FBInvalidElementStateException reason:reason userInfo:@{}];
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
- (NSString *)am_type
|
|
@@ -94,4 +93,20 @@
|
|
|
94
93
|
return [predicate evaluateWithObject:self];
|
|
95
94
|
}
|
|
96
95
|
|
|
96
|
+
- (NSString *)am_textWithSource:(id<XCUIElementAttributes>)source
|
|
97
|
+
{
|
|
98
|
+
NSArray<NSString *> *candidates = @[
|
|
99
|
+
[FBElementUtils stringValueWithValue:source.value],
|
|
100
|
+
source.label,
|
|
101
|
+
source.placeholderValue,
|
|
102
|
+
source.title
|
|
103
|
+
];
|
|
104
|
+
for (NSString *text in candidates) {
|
|
105
|
+
if (nil != text && text.length > 0) {
|
|
106
|
+
return text;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return @"";
|
|
110
|
+
}
|
|
111
|
+
|
|
97
112
|
@end
|
|
@@ -34,6 +34,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
34
34
|
*/
|
|
35
35
|
- (NSArray<XCUIElement *> *)am_allMatches;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
Returns single unique matching snapshot for the given query
|
|
39
|
+
|
|
40
|
+
@param error The error instance if there was a failure while retrieveing the snapshot
|
|
41
|
+
@returns The unqiue snapshot or nil if the element is stale
|
|
42
|
+
*/
|
|
43
|
+
- (nullable id<XCUIElementSnapshot>)am_uniqueSnapshotWithError:(NSError **)error;
|
|
44
|
+
|
|
37
45
|
@end
|
|
38
46
|
|
|
39
47
|
NS_ASSUME_NONNULL_END
|
|
@@ -32,4 +32,21 @@
|
|
|
32
32
|
: self.allElementsBoundByAccessibilityElement;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
- (id<XCUIElementSnapshot>)am_uniqueSnapshotWithError:(NSError **)error
|
|
36
|
+
{
|
|
37
|
+
SEL selector = NSSelectorFromString(@"uniqueMatchingSnapshotWithError:");
|
|
38
|
+
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
|
|
39
|
+
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
|
40
|
+
invocation.target = self;
|
|
41
|
+
invocation.selector = selector;
|
|
42
|
+
|
|
43
|
+
[invocation setArgument:&error atIndex:2]; // index 0 = self, 1 = _cmd, 2 = first real argument
|
|
44
|
+
|
|
45
|
+
[invocation invoke];
|
|
46
|
+
|
|
47
|
+
__unsafe_unretained id returnValue = nil;
|
|
48
|
+
[invocation getReturnValue:&returnValue];
|
|
49
|
+
return returnValue;
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
@end
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#import "FBCustomCommands.h"
|
|
11
11
|
|
|
12
|
+
#import "AMPasteboard.h"
|
|
12
13
|
#import "FBResponsePayload.h"
|
|
13
14
|
#import "FBRoute.h"
|
|
14
15
|
#import "FBRouteRequest.h"
|
|
@@ -21,6 +22,10 @@
|
|
|
21
22
|
return
|
|
22
23
|
@[
|
|
23
24
|
[[FBRoute POST:@"/timeouts"] respondWithTarget:self action:@selector(handleTimeouts:)],
|
|
25
|
+
[[FBRoute POST:@"/wda/setPasteboard"] respondWithTarget:self action:@selector(handleSetPasteboard:)],
|
|
26
|
+
[[FBRoute POST:@"/wda/setPasteboard"].withoutSession respondWithTarget:self action:@selector(handleSetPasteboard:)],
|
|
27
|
+
[[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)],
|
|
28
|
+
[[FBRoute POST:@"/wda/getPasteboard"].withoutSession respondWithTarget:self action:@selector(handleGetPasteboard:)],
|
|
24
29
|
[[FBRoute OPTIONS:@"/*"].withoutSession respondWithTarget:self action:@selector(handlePingCommand:)],
|
|
25
30
|
];
|
|
26
31
|
}
|
|
@@ -34,6 +39,34 @@
|
|
|
34
39
|
return FBResponseWithOK();
|
|
35
40
|
}
|
|
36
41
|
|
|
42
|
+
+ (id<FBResponsePayload>)handleSetPasteboard:(FBRouteRequest *)request
|
|
43
|
+
{
|
|
44
|
+
NSString *contentType = request.arguments[@"contentType"] ?: @"plaintext";
|
|
45
|
+
NSData *content = [[NSData alloc] initWithBase64EncodedString:(NSString *)request.arguments[@"content"]
|
|
46
|
+
options:NSDataBase64DecodingIgnoreUnknownCharacters];
|
|
47
|
+
if (nil == content) {
|
|
48
|
+
return FBResponseWithStatus([FBCommandStatus
|
|
49
|
+
invalidArgumentErrorWithMessage:@"Cannot decode the pasteboard content from base64"
|
|
50
|
+
traceback:nil]);
|
|
51
|
+
}
|
|
52
|
+
NSError *error;
|
|
53
|
+
if (![AMPasteboard setData:content forType:contentType error:&error]) {
|
|
54
|
+
return FBResponseWithUnknownError(error);
|
|
55
|
+
}
|
|
56
|
+
return FBResponseWithOK();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
+ (id<FBResponsePayload>)handleGetPasteboard:(FBRouteRequest *)request
|
|
60
|
+
{
|
|
61
|
+
NSString *contentType = request.arguments[@"contentType"] ?: @"plaintext";
|
|
62
|
+
NSError *error;
|
|
63
|
+
id result = [AMPasteboard dataForType:contentType error:&error];
|
|
64
|
+
if (nil == result) {
|
|
65
|
+
return FBResponseWithUnknownError(error);
|
|
66
|
+
}
|
|
67
|
+
return FBResponseWithObject([result base64EncodedStringWithOptions:0]);
|
|
68
|
+
}
|
|
69
|
+
|
|
37
70
|
+ (id<FBResponsePayload>)handlePingCommand:(FBRouteRequest *)request
|
|
38
71
|
{
|
|
39
72
|
return FBResponseWithOK();
|
|
@@ -237,6 +237,7 @@ const static NSString *CAPABILITIES_KEY = @"capabilities";
|
|
|
237
237
|
@{
|
|
238
238
|
AM_BOUND_ELEMENTS_BY_INDEX_SETTING: @(FBSession.activeSession.boundElementsByIndex),
|
|
239
239
|
AM_USE_DEFAULT_UI_INTERRUPTIONS_HANDLING_SETTING: @(!application.am_doesNotHandleUIInterruptions),
|
|
240
|
+
AM_FETCH_FULL_TEXT: @(FBConfiguration.sharedConfiguration.fetchFullText),
|
|
240
241
|
}
|
|
241
242
|
);
|
|
242
243
|
}
|
|
@@ -252,6 +253,9 @@ const static NSString *CAPABILITIES_KEY = @"capabilities";
|
|
|
252
253
|
XCUIApplication *application = FBSession.activeSession.currentApplication;
|
|
253
254
|
application.am_doesNotHandleUIInterruptions = ![[settings objectForKey:AM_USE_DEFAULT_UI_INTERRUPTIONS_HANDLING_SETTING] boolValue];
|
|
254
255
|
}
|
|
256
|
+
if (nil != [settings objectForKey:AM_FETCH_FULL_TEXT]) {
|
|
257
|
+
FBConfiguration.sharedConfiguration.fetchFullText = [settings objectForKey:AM_FETCH_FULL_TEXT];
|
|
258
|
+
}
|
|
255
259
|
|
|
256
260
|
return [self handleGetSettings:request];
|
|
257
261
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
#import <XCTest/XCTest.h>
|
|
19
|
+
|
|
20
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
21
|
+
|
|
22
|
+
@interface AMPasteboard : NSObject
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
Sets data to the general pasteboard
|
|
26
|
+
|
|
27
|
+
@param data base64-encoded string containing the data chunk which is going to be written to the pasteboard
|
|
28
|
+
@param type one of the possible data types to set: plaintext, url, image
|
|
29
|
+
@param error If there is an error, upon return contains an NSError object that describes the problem
|
|
30
|
+
@return YES if the operation was successful
|
|
31
|
+
*/
|
|
32
|
+
+ (BOOL)setData:(NSData *)data forType:(NSString *)type error:(NSError **)error;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
Gets the data contained in the general pasteboard
|
|
36
|
+
|
|
37
|
+
@param type one of the possible data types to get: plaintext, url, image
|
|
38
|
+
@param error If there is an error, upon return contains an NSError object that describes the problem
|
|
39
|
+
@return NSData object, containing the pasteboard content or an empty string if the pasteboard is empty.
|
|
40
|
+
nil is returned if there was an error while getting the data from the pasteboard
|
|
41
|
+
*/
|
|
42
|
+
+ (nullable NSData *)dataForType:(NSString *)type error:(NSError **)error;
|
|
43
|
+
|
|
44
|
+
@end
|
|
45
|
+
|
|
46
|
+
NS_ASSUME_NONNULL_END
|
|
47
|
+
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
#import "AMPasteboard.h"
|
|
19
|
+
|
|
20
|
+
#import <mach/mach_time.h>
|
|
21
|
+
#import "FBErrorBuilder.h"
|
|
22
|
+
#import "FBMacros.h"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@implementation AMPasteboard
|
|
26
|
+
|
|
27
|
+
+ (BOOL)setData:(NSData *)data forType:(NSString *)type error:(NSError **)error
|
|
28
|
+
{
|
|
29
|
+
NSPasteboard *pb = NSPasteboard.generalPasteboard;
|
|
30
|
+
if ([type.lowercaseString isEqualToString:@"plaintext"]) {
|
|
31
|
+
[pb clearContents];
|
|
32
|
+
if (![pb setString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]
|
|
33
|
+
forType:NSPasteboardTypeString]) {
|
|
34
|
+
NSString *description = @"Failed to copy string to the clipboard";
|
|
35
|
+
if (error) {
|
|
36
|
+
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
37
|
+
}
|
|
38
|
+
return NO;
|
|
39
|
+
}
|
|
40
|
+
} else if ([type.lowercaseString isEqualToString:@"image"]) {
|
|
41
|
+
NSImage *image = [[NSImage alloc] initWithData:data];
|
|
42
|
+
if (nil == image) {
|
|
43
|
+
NSString *description = @"No image can be parsed from the given pasteboard data";
|
|
44
|
+
if (error) {
|
|
45
|
+
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
46
|
+
}
|
|
47
|
+
return NO;
|
|
48
|
+
}
|
|
49
|
+
[pb clearContents];
|
|
50
|
+
if (![pb writeObjects:@[image]]) {
|
|
51
|
+
NSString *description = @"Failed to copy image to the clipboard";
|
|
52
|
+
if (error) {
|
|
53
|
+
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
54
|
+
}
|
|
55
|
+
return NO;
|
|
56
|
+
}
|
|
57
|
+
} else if ([type.lowercaseString isEqualToString:@"url"]) {
|
|
58
|
+
NSString *urlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
59
|
+
NSURL *url = [[NSURL alloc] initWithString:urlString];
|
|
60
|
+
if (nil == url) {
|
|
61
|
+
NSString *description = @"No URL can be parsed from the given data";
|
|
62
|
+
if (error) {
|
|
63
|
+
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
64
|
+
}
|
|
65
|
+
return NO;
|
|
66
|
+
}
|
|
67
|
+
[pb clearContents];
|
|
68
|
+
if (![pb setString:[url absoluteString] forType:NSPasteboardTypeURL]) {
|
|
69
|
+
NSString *description = @"Failed to copy URL to the clipboard";
|
|
70
|
+
if (error) {
|
|
71
|
+
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
72
|
+
}
|
|
73
|
+
return NO;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
NSString *description = [NSString stringWithFormat:@"Unsupported content type: %@", type];
|
|
77
|
+
if (error) {
|
|
78
|
+
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
79
|
+
}
|
|
80
|
+
return NO;
|
|
81
|
+
}
|
|
82
|
+
return YES;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
+ (NSData *)dataForType:(NSString *)type error:(NSError **)error
|
|
86
|
+
{
|
|
87
|
+
NSPasteboard *pb = NSPasteboard.generalPasteboard;
|
|
88
|
+
if ([type.lowercaseString isEqualToString:@"plaintext"]) {
|
|
89
|
+
if ([pb.types containsObject:NSPasteboardTypeString]) {
|
|
90
|
+
NSString* result = [pb stringForType:NSPasteboardTypeString];
|
|
91
|
+
return [result dataUsingEncoding:NSUTF8StringEncoding];
|
|
92
|
+
}
|
|
93
|
+
} else if ([type.lowercaseString isEqualToString:@"image"]) {
|
|
94
|
+
NSData *imageData = nil;
|
|
95
|
+
|
|
96
|
+
for (NSPasteboardType pbType in @[NSPasteboardTypePNG, NSPasteboardTypeTIFF]) {
|
|
97
|
+
if ([pb.types containsObject:pbType]) {
|
|
98
|
+
imageData = [pb dataForType:pbType];
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (nil != imageData) {
|
|
104
|
+
NSImage *image = [[NSImage alloc] initWithData:imageData];
|
|
105
|
+
if (nil != image) {
|
|
106
|
+
NSArray *reps = [image representations];
|
|
107
|
+
if ([reps count] > 0 && [reps[0] isKindOfClass:[NSBitmapImageRep class]]) {
|
|
108
|
+
NSBitmapImageRep *bitmapRep = (NSBitmapImageRep *)reps[0];
|
|
109
|
+
return [bitmapRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else if ([type.lowercaseString isEqualToString:@"url"]) {
|
|
114
|
+
if ([pb.types containsObject:NSPasteboardTypeURL]) {
|
|
115
|
+
NSString* result = [pb stringForType:NSPasteboardTypeURL];
|
|
116
|
+
return [result dataUsingEncoding:NSUTF8StringEncoding];
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
NSString *description = [NSString stringWithFormat:@"Unsupported content type: %@", type];
|
|
120
|
+
if (error) {
|
|
121
|
+
*error = [[FBErrorBuilder.builder withDescription:description] build];
|
|
122
|
+
}
|
|
123
|
+
return nil;
|
|
124
|
+
}
|
|
125
|
+
return [@"" dataUsingEncoding:NSUTF8StringEncoding];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@end
|
|
@@ -25,4 +25,7 @@ extern NSString* const AM_BOUND_ELEMENTS_BY_INDEX_SETTING;
|
|
|
25
25
|
See https://developer.apple.com/videos/play/wwdc2020/10220/ for more details */
|
|
26
26
|
extern NSString* const AM_USE_DEFAULT_UI_INTERRUPTIONS_HANDLING_SETTING;
|
|
27
27
|
|
|
28
|
+
/*! Whether to use custom snapshotting mechanism to fetch full element's text payload instead of the first 512 chars */
|
|
29
|
+
extern NSString* const AM_FETCH_FULL_TEXT;
|
|
30
|
+
|
|
28
31
|
NS_ASSUME_NONNULL_END
|
|
@@ -17,5 +17,5 @@
|
|
|
17
17
|
#import "AMSettings.h"
|
|
18
18
|
|
|
19
19
|
NSString* const AM_BOUND_ELEMENTS_BY_INDEX_SETTING = @"boundElementsByIndex";
|
|
20
|
-
|
|
21
20
|
NSString* const AM_USE_DEFAULT_UI_INTERRUPTIONS_HANDLING_SETTING = @"useDefaultUiInterruptionsHandling";
|
|
21
|
+
NSString* const AM_FETCH_FULL_TEXT = @"fetchFullText";
|
|
@@ -30,6 +30,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
30
30
|
/*! Enables XCTest automated screen recordings taking in Xcode 15+ */
|
|
31
31
|
@property BOOL automaticScreenRecordings;
|
|
32
32
|
|
|
33
|
+
/*! Whether to use custom snapshotting mechanism to fetch full element's text payload instead of the first 512 chars */
|
|
34
|
+
@property BOOL fetchFullText;
|
|
35
|
+
|
|
33
36
|
/**
|
|
34
37
|
The range of ports that the HTTP Server should attempt to bind on launch
|
|
35
38
|
*/
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
static NSUInteger const DefaultStartingPort = 10100;
|
|
13
13
|
static NSUInteger const DefaultPortRange = 100;
|
|
14
|
+
static BOOL FBFetchFullText = NO;
|
|
14
15
|
|
|
15
16
|
@implementation FBConfiguration
|
|
16
17
|
|
|
@@ -75,6 +76,16 @@ static FBConfiguration *instance;
|
|
|
75
76
|
forKey:@"DisableDiagnosticScreenRecordings"];
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
- (BOOL)fetchFullText
|
|
80
|
+
{
|
|
81
|
+
return FBFetchFullText;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
- (void)setFetchFullText:(BOOL)fetchFullText
|
|
85
|
+
{
|
|
86
|
+
FBFetchFullText = fetchFullText;
|
|
87
|
+
}
|
|
88
|
+
|
|
78
89
|
- (NSRange)bindingPortRange
|
|
79
90
|
{
|
|
80
91
|
// 'WebDriverAgent --port 8080' can be passed via the arguments to the process
|