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.
Files changed (34) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +27 -0
  3. package/WebDriverAgentMac/IntegrationTests/AMEditElementTests.m +2 -4
  4. package/WebDriverAgentMac/IntegrationTests/AMPasteboardTests.m +111 -0
  5. package/WebDriverAgentMac/WebDriverAgentLib/Categories/XCUIElement+AMAttributes.h +4 -2
  6. package/WebDriverAgentMac/WebDriverAgentLib/Categories/XCUIElement+AMAttributes.m +30 -15
  7. package/WebDriverAgentMac/WebDriverAgentLib/Categories/XCUIElementQuery+AMHelpers.h +8 -0
  8. package/WebDriverAgentMac/WebDriverAgentLib/Categories/XCUIElementQuery+AMHelpers.m +17 -0
  9. package/WebDriverAgentMac/WebDriverAgentLib/Commands/FBCustomCommands.m +33 -0
  10. package/WebDriverAgentMac/WebDriverAgentLib/Commands/FBSessionCommands.m +4 -0
  11. package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMPasteboard.h +47 -0
  12. package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMPasteboard.m +128 -0
  13. package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMSettings.h +3 -0
  14. package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMSettings.m +1 -1
  15. package/WebDriverAgentMac/WebDriverAgentLib/Utilities/FBConfiguration.h +3 -0
  16. package/WebDriverAgentMac/WebDriverAgentLib/Utilities/FBConfiguration.m +11 -0
  17. package/WebDriverAgentMac/WebDriverAgentMac.xcodeproj/project.pbxproj +12 -0
  18. package/build/lib/commands/clipboard.d.ts +19 -0
  19. package/build/lib/commands/clipboard.d.ts.map +1 -0
  20. package/build/lib/commands/clipboard.js +31 -0
  21. package/build/lib/commands/clipboard.js.map +1 -0
  22. package/build/lib/driver.d.ts +24 -1
  23. package/build/lib/driver.d.ts.map +1 -1
  24. package/build/lib/driver.js +10 -0
  25. package/build/lib/driver.js.map +1 -1
  26. package/build/lib/execute-method-map.d.ts +13 -0
  27. package/build/lib/execute-method-map.d.ts.map +1 -1
  28. package/build/lib/execute-method-map.js +11 -0
  29. package/build/lib/execute-method-map.js.map +1 -1
  30. package/lib/commands/clipboard.ts +35 -0
  31. package/lib/driver.js +11 -0
  32. package/lib/execute-method-map.ts +11 -0
  33. package/npm-shrinkwrap.json +2221 -213
  34. 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
- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"placeholderValue.length == 0"];
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
- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"placeholderValue.length > 0"];
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
- - (nullable NSString *)am_text;
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
- NSString *value = [FBElementUtils stringValueWithValue:self.value];
68
- if (nil != value && value.length > 0) {
69
- return value;
69
+ if (!FBConfiguration.sharedConfiguration.fetchFullText) {
70
+ return [self am_textWithSource:self];
70
71
  }
71
- NSString *label = self.label;
72
- if (nil != label && label.length > 0) {
73
- return label;
74
- }
75
- NSString *placeholderValue = self.placeholderValue;
76
- if (nil != placeholderValue && placeholderValue.length > 0) {
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
- return @"";
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