appium-mac2-driver 2.0.0 → 2.1.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 +7 -0
- package/README.md +81 -2
- package/WebDriverAgentMac/IntegrationTests/AMVideoRecordingTests.m +57 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Commands/AMVideoCommands.h +27 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Commands/AMVideoCommands.m +120 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Commands/FBDebugCommands.m +17 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Commands/FBScreenshotCommands.m +8 -8
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMXCTRunnerDaemonSession.h +31 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMXCTRunnerDaemonSessionWrapper.h +39 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMXCTRunnerDaemonSessionWrapper.m +43 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBScreenRecordingContainer.h +59 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBScreenRecordingContainer.m +74 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBScreenRecordingPromise.h +34 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBScreenRecordingPromise.m +32 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBScreenRecordingRequest.h +44 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBScreenRecordingRequest.m +97 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBSession.m +14 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMScreenUtils.h +49 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMScreenUtils.m +49 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMVideoRecorder.h +50 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Utilities/AMVideoRecorder.m +112 -0
- package/WebDriverAgentMac/WebDriverAgentMac.xcodeproj/project.pbxproj +64 -0
- package/build/lib/commands/helpers.d.ts +4 -0
- package/build/lib/commands/helpers.d.ts.map +1 -0
- package/build/lib/commands/helpers.js +28 -0
- package/build/lib/commands/helpers.js.map +1 -0
- package/build/lib/commands/native-record-screen.d.ts +75 -0
- package/build/lib/commands/native-record-screen.d.ts.map +1 -0
- package/build/lib/commands/native-record-screen.js +139 -0
- package/build/lib/commands/native-record-screen.js.map +1 -0
- package/build/lib/commands/record-screen.d.ts.map +1 -1
- package/build/lib/commands/record-screen.js +2 -28
- package/build/lib/commands/record-screen.js.map +1 -1
- package/build/lib/driver.d.ts +25 -0
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +13 -0
- package/build/lib/driver.js.map +1 -1
- package/build/lib/execute-method-map.d.ts +18 -0
- package/build/lib/execute-method-map.d.ts.map +1 -1
- package/build/lib/execute-method-map.js +30 -0
- package/build/lib/execute-method-map.js.map +1 -1
- package/lib/commands/helpers.ts +30 -0
- package/lib/commands/native-record-screen.ts +180 -0
- package/lib/commands/record-screen.js +2 -30
- package/lib/driver.js +18 -0
- package/lib/execute-method-map.ts +30 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.1.0](https://github.com/appium/appium-mac2-driver/compare/v2.0.0...v2.1.0) (2025-02-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Add support for native screen recording ([#330](https://github.com/appium/appium-mac2-driver/issues/330)) ([d2f5b25](https://github.com/appium/appium-mac2-driver/commit/d2f5b2564f4b25fefbcce60d77718031d438bf60))
|
|
7
|
+
|
|
1
8
|
## [2.0.0](https://github.com/appium/appium-mac2-driver/compare/v1.20.5...v2.0.0) (2025-02-06)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -586,7 +586,7 @@ Retrieves a screenshot of each display available to macOS.
|
|
|
586
586
|
|
|
587
587
|
Name | Type | Required | Description | Example
|
|
588
588
|
--- | --- | --- | --- | ---
|
|
589
|
-
displayId | number | no | Display identifier to take a screenshot for. If not provided then all display screenshots are going to be returned. If no matches were found then an error is thrown. | 1
|
|
589
|
+
displayId | number | no | Display identifier to take a screenshot for. If not provided then all display screenshots are going to be returned. If no matches were found then an error is thrown. Use the `system_profiler -json SPDisplaysDataType` Terminal command to list IDs of connected displays or the [macos: listDisplays](#macos-listdisplays) API. | 1
|
|
590
590
|
|
|
591
591
|
#### Returns
|
|
592
592
|
|
|
@@ -595,7 +595,7 @@ A list of dictionaries where each item has the following keys:
|
|
|
595
595
|
- `isMain`: Whether this display is the main one
|
|
596
596
|
- `payload`: The actual PNG screenshot data encoded to base64 string
|
|
597
597
|
|
|
598
|
-
###
|
|
598
|
+
### macos: deepLink
|
|
599
599
|
|
|
600
600
|
Opens the given URL with the default or the given application.
|
|
601
601
|
Xcode must be at version 14.3+.
|
|
@@ -607,6 +607,85 @@ Name | Type | Required | Description | Example
|
|
|
607
607
|
url | string | yes | The URL to be opened. This parameter is manadatory. | https://apple.com, myscheme:yolo
|
|
608
608
|
bundleId | string | no | The bundle identifier of an application to open the given url with. If not provided then the default application for the given url scheme is going to be used. | com.myapp.yolo
|
|
609
609
|
|
|
610
|
+
### macos: startNativeScreenRecording
|
|
611
|
+
|
|
612
|
+
Initiates a new native screen recording session via XCTest.
|
|
613
|
+
If the screen recording is already running then this call results in noop.
|
|
614
|
+
A screen recording is running until a testing session is finished.
|
|
615
|
+
If a recording has never been stopped explicitly during a test session
|
|
616
|
+
then it would be stopped automatically upon the test session termination,
|
|
617
|
+
and leftover videos would be deleted as well.
|
|
618
|
+
Xcode must be at version 15+.
|
|
619
|
+
|
|
620
|
+
#### Arguments
|
|
621
|
+
|
|
622
|
+
Name | Type | Required | Description | Example
|
|
623
|
+
--- | --- | --- | --- | ---
|
|
624
|
+
fps | number | no | Frame Per Second setting for the resulting screen recording. 24 by default. Higher FPS values may significantly increase the size of the resulting video. | 60
|
|
625
|
+
codec | number | no | Possible codec value, where `0` means H264 (the default setting), `1` means HEVC | 1
|
|
626
|
+
displayId | number | no | Valid display identifier to record the video from. Main display ID is assumed by default. Use the `system_profiler -json SPDisplaysDataType` Terminal command to list IDs of connected displays or the [macos: listDisplays](#macos-listdisplays) API. | 1
|
|
627
|
+
|
|
628
|
+
#### Returns
|
|
629
|
+
|
|
630
|
+
The information about the asynchronously running video recording, which includes the following items:
|
|
631
|
+
|
|
632
|
+
Name | Type | Description | Example
|
|
633
|
+
--- | --- | --- | ---
|
|
634
|
+
fps | number | Frame Per Second value | 24
|
|
635
|
+
codec | number | Codec value, where `0` means H264 (the default setting), `1` means HEVC | 1
|
|
636
|
+
displayId | number | Display identifier used to record this video for. | 1
|
|
637
|
+
uuid | string | Unique video identifier. It is also used by XCTest to store the video on the file system. Look for `$HOME/Library/Daemon Containers/<testmanager_id>/Data/Attachments/<uuid>` to find the appropriate video file. Add the `.mp4` extension to it to make it openable by video players.
|
|
638
|
+
startedAt | number | Unix timestamp of the video startup moment | 123456789
|
|
639
|
+
|
|
640
|
+
### macos: getNativeScreenRecordingInfo
|
|
641
|
+
|
|
642
|
+
Fetches the information of the currently running native video recording.
|
|
643
|
+
Xcode must be at version 15+.
|
|
644
|
+
|
|
645
|
+
#### Returns
|
|
646
|
+
|
|
647
|
+
Either `null` if no native video recording is currently active or the same map that [macos: startNativeScreenRecording](#macos-startnativescreenrecording) returns.
|
|
648
|
+
|
|
649
|
+
### macos: stopNativeScreenRecording
|
|
650
|
+
|
|
651
|
+
Stops native screen recording previously started by
|
|
652
|
+
[macos: startNativeScreenRecording](#macos-startnativescreenrecording)
|
|
653
|
+
and returns the video payload or uploads it to a remote location,
|
|
654
|
+
depending on the provided arguments.
|
|
655
|
+
The actual video file is removed from the local file system after the video payload is
|
|
656
|
+
successfully consumed.
|
|
657
|
+
If no screen recording has been started before then this API throws an exception.
|
|
658
|
+
Xcode must be at version 15+.
|
|
659
|
+
|
|
660
|
+
#### Arguments
|
|
661
|
+
|
|
662
|
+
Name | Type | Required | Description | Example
|
|
663
|
+
--- | --- | --- | --- | ---
|
|
664
|
+
remotePath | string | no | The path to the remote location, where the resulting video should be uploaded. The following protocols are supported: http/https, ftp. Null or empty string value (the default setting) means the content of resulting file should be encoded as Base64 and passed as the endpoint response value. An exception will be thrown if the generated media file is too big to fit into the available process memory. | https://myserver.com/upload/video.mp4
|
|
665
|
+
user | string | no | The name of the user for the remote authentication. | myname
|
|
666
|
+
pass | string | no | The password for the remote authentication. | mypassword
|
|
667
|
+
method | string | no | The http multipart upload method name. The 'PUT' one is used by default. | POST
|
|
668
|
+
headers | map | no | Additional headers mapping for multipart http(s) uploads | `{"header": "value"}`
|
|
669
|
+
fileFieldName | string | no | The name of the form field, where the file content BLOB should be stored for http(s) uploads. `file` by default | payload
|
|
670
|
+
formFields | Map or `Array<Pair>` | no | Additional form fields for multipart http(s) uploads | `{"field1": "value1", "field2": "value2"}` or `[["field1", "value1"], ["field2", "value2"]]`
|
|
671
|
+
|
|
672
|
+
#### Returns
|
|
673
|
+
|
|
674
|
+
Base64-encoded content of the recorded media file if `remotePath` parameter is falsy or an empty string.
|
|
675
|
+
|
|
676
|
+
### macos: listDisplays
|
|
677
|
+
|
|
678
|
+
Fetches information about available displays.
|
|
679
|
+
|
|
680
|
+
#### Returns
|
|
681
|
+
|
|
682
|
+
A map where keys are display identifiers represented as strings and values are display infos containing the following items:
|
|
683
|
+
|
|
684
|
+
Name | Type | Description | Example
|
|
685
|
+
--- | --- | --- | ---
|
|
686
|
+
id | number | Display identifier | 12345
|
|
687
|
+
isMain | boolean | Is `true` if the display is configured as a main system display | false
|
|
688
|
+
|
|
610
689
|
|
|
611
690
|
## Application Under Test Concept
|
|
612
691
|
|
|
@@ -0,0 +1,57 @@
|
|
|
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 "AMVideoRecorder.h"
|
|
21
|
+
#import "FBScreenRecordingRequest.h"
|
|
22
|
+
#import "FBTestMacros.h"
|
|
23
|
+
#import "FBScreenRecordingContainer.h"
|
|
24
|
+
#import "FBScreenRecordingPromise.h"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@interface AMVideoRecordingTests : AMIntegrationTestCase
|
|
28
|
+
@end
|
|
29
|
+
|
|
30
|
+
@implementation AMVideoRecordingTests
|
|
31
|
+
|
|
32
|
+
- (void)setUp
|
|
33
|
+
{
|
|
34
|
+
[super setUp];
|
|
35
|
+
static dispatch_once_t onceToken;
|
|
36
|
+
dispatch_once(&onceToken, ^{
|
|
37
|
+
[self launchApplication];
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
- (void)testVideoRecording
|
|
42
|
+
{
|
|
43
|
+
AMVideoRecorder *recorder = AMVideoRecorder.sharedInstance;
|
|
44
|
+
FBScreenRecordingRequest *request = [[FBScreenRecordingRequest alloc] initWithFps:24
|
|
45
|
+
codec:0
|
|
46
|
+
displayID:nil];
|
|
47
|
+
NSError *error;
|
|
48
|
+
FBScreenRecordingPromise *promise = [recorder startScreenRecordingWithRequest:request
|
|
49
|
+
error:&error];
|
|
50
|
+
XCTAssertNotNil(promise);
|
|
51
|
+
XCTAssertNil(error);
|
|
52
|
+
FBWaitExact(5);
|
|
53
|
+
XCTAssertTrue([recorder stopScreenRecordingWithUUID:promise.identifier error:&error]);
|
|
54
|
+
XCTAssertNil(error);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@end
|
|
@@ -0,0 +1,27 @@
|
|
|
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 <Foundation/Foundation.h>
|
|
18
|
+
|
|
19
|
+
#import <WebDriverAgentLib/FBCommandHandler.h>
|
|
20
|
+
|
|
21
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
22
|
+
|
|
23
|
+
@interface AMVideoCommands : NSObject <FBCommandHandler>
|
|
24
|
+
|
|
25
|
+
@end
|
|
26
|
+
|
|
27
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,120 @@
|
|
|
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 "AMVideoCommands.h"
|
|
18
|
+
|
|
19
|
+
#import "FBRouteRequest.h"
|
|
20
|
+
#import "FBScreenRecordingContainer.h"
|
|
21
|
+
#import "FBScreenRecordingPromise.h"
|
|
22
|
+
#import "FBScreenRecordingRequest.h"
|
|
23
|
+
#import "AMScreenUtils.h"
|
|
24
|
+
#import "FBSession.h"
|
|
25
|
+
#import "AMVideoRecorder.h"
|
|
26
|
+
#import "FBErrorBuilder.h"
|
|
27
|
+
|
|
28
|
+
const NSUInteger DEFAULT_FPS = 24;
|
|
29
|
+
const NSUInteger DEFAULT_CODEC = 0;
|
|
30
|
+
|
|
31
|
+
@implementation AMVideoCommands
|
|
32
|
+
|
|
33
|
+
+ (NSArray *)routes
|
|
34
|
+
{
|
|
35
|
+
return
|
|
36
|
+
@[
|
|
37
|
+
[[FBRoute POST:@"/wda/video/start"] respondWithTarget:self action:@selector(handleStartVideoRecording:)],
|
|
38
|
+
[[FBRoute POST:@"/wda/video/stop"] respondWithTarget:self action:@selector(handleStopVideoRecording:)],
|
|
39
|
+
[[FBRoute GET:@"/wda/video"] respondWithTarget:self action:@selector(handleGetVideoRecording:)],
|
|
40
|
+
|
|
41
|
+
[[FBRoute POST:@"/wda/video/start"].withoutSession respondWithTarget:self action:@selector(handleStartVideoRecording:)],
|
|
42
|
+
[[FBRoute POST:@"/wda/video/stop"].withoutSession respondWithTarget:self action:@selector(handleStopVideoRecording:)],
|
|
43
|
+
[[FBRoute GET:@"/wda/video"].withoutSession respondWithTarget:self action:@selector(handleGetVideoRecording:)],
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
+ (id<FBResponsePayload>)handleStartVideoRecording:(FBRouteRequest *)request
|
|
48
|
+
{
|
|
49
|
+
FBScreenRecordingPromise *activeScreenRecording = FBScreenRecordingContainer.sharedInstance.screenRecordingPromise;
|
|
50
|
+
if (nil != activeScreenRecording) {
|
|
51
|
+
return FBResponseWithObject([FBScreenRecordingContainer.sharedInstance toDictionary] ?: [NSNull null]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
NSNumber *fps = (NSNumber *)request.arguments[@"fps"] ?: @(DEFAULT_FPS);
|
|
55
|
+
NSNumber *codec = (NSNumber *)request.arguments[@"codec"] ?: @(DEFAULT_CODEC);
|
|
56
|
+
NSNumber *displayID = (NSNumber *)request.arguments[@"displayId"];
|
|
57
|
+
if (nil != displayID) {
|
|
58
|
+
NSError *error;
|
|
59
|
+
if (![self verifyDisplayWithID:displayID.longLongValue error:&error]) {
|
|
60
|
+
return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:error.description
|
|
61
|
+
traceback:nil]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
FBScreenRecordingRequest *recordingRequest = [[FBScreenRecordingRequest alloc] initWithFps:fps.integerValue
|
|
65
|
+
codec:codec.longLongValue
|
|
66
|
+
displayID:displayID];
|
|
67
|
+
NSError *error;
|
|
68
|
+
FBScreenRecordingPromise* promise = [AMVideoRecorder.sharedInstance startScreenRecordingWithRequest:recordingRequest
|
|
69
|
+
error:&error];
|
|
70
|
+
if (nil == promise) {
|
|
71
|
+
[FBScreenRecordingContainer.sharedInstance reset];
|
|
72
|
+
return FBResponseWithUnknownError(error);
|
|
73
|
+
}
|
|
74
|
+
[FBScreenRecordingContainer.sharedInstance storeScreenRecordingPromise:promise
|
|
75
|
+
fps:fps.integerValue
|
|
76
|
+
codec:codec.longLongValue
|
|
77
|
+
displayID:displayID];
|
|
78
|
+
return FBResponseWithObject([FBScreenRecordingContainer.sharedInstance toDictionary]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
+ (id<FBResponsePayload>)handleStopVideoRecording:(FBRouteRequest *)request
|
|
82
|
+
{
|
|
83
|
+
FBScreenRecordingPromise *activeScreenRecording = FBScreenRecordingContainer.sharedInstance.screenRecordingPromise;
|
|
84
|
+
if (nil == activeScreenRecording) {
|
|
85
|
+
return FBResponseWithOK();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
NSUUID *recordingId = activeScreenRecording.identifier;
|
|
89
|
+
NSDictionary *response = [FBScreenRecordingContainer.sharedInstance toDictionary];
|
|
90
|
+
NSError *error;
|
|
91
|
+
if (![AMVideoRecorder.sharedInstance stopScreenRecordingWithUUID:recordingId error:&error]) {
|
|
92
|
+
[FBScreenRecordingContainer.sharedInstance reset];
|
|
93
|
+
return FBResponseWithUnknownError(error);
|
|
94
|
+
}
|
|
95
|
+
[FBScreenRecordingContainer.sharedInstance reset];
|
|
96
|
+
return FBResponseWithObject(response);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
+ (id<FBResponsePayload>)handleGetVideoRecording:(FBRouteRequest *)request
|
|
100
|
+
{
|
|
101
|
+
return FBResponseWithObject([FBScreenRecordingContainer.sharedInstance toDictionary] ?: [NSNull null]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
+ (BOOL)verifyDisplayWithID:(long long)displayID error:(NSError **)error
|
|
105
|
+
{
|
|
106
|
+
NSMutableArray* availableIds = [NSMutableArray array];
|
|
107
|
+
for (XCUIScreen *screen in XCUIScreen.screens) {
|
|
108
|
+
long long currentDisplayId = AMFetchScreenId(screen);
|
|
109
|
+
if (displayID == currentDisplayId) {
|
|
110
|
+
return YES;
|
|
111
|
+
}
|
|
112
|
+
[availableIds addObject:@(currentDisplayId)];
|
|
113
|
+
}
|
|
114
|
+
return [[[FBErrorBuilder builder]
|
|
115
|
+
withDescriptionFormat:@"The provided display identifier %lld is not known. Only the following values are allowed: %@",
|
|
116
|
+
displayID, [availableIds componentsJoinedByString:@","]]
|
|
117
|
+
buildError:error];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@end
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#import "FBDebugCommands.h"
|
|
11
11
|
|
|
12
|
+
#import "AMScreenUtils.h"
|
|
12
13
|
#import "FBRouteRequest.h"
|
|
13
14
|
#import "FBSession.h"
|
|
14
15
|
#import "XCUIApplication+AMSource.h"
|
|
@@ -23,6 +24,9 @@
|
|
|
23
24
|
@[
|
|
24
25
|
[[FBRoute GET:@"/source"] respondWithTarget:self action:@selector(handleGetSourceCommand:)],
|
|
25
26
|
[[FBRoute GET:@"/source"].withoutSession respondWithTarget:self action:@selector(handleGetSourceCommand:)],
|
|
27
|
+
|
|
28
|
+
[[FBRoute GET:@"/wda/displays/list"] respondWithTarget:self action:@selector(handleListDisplays:)],
|
|
29
|
+
[[FBRoute GET:@"/wda/displays/list"].withoutSession respondWithTarget:self action:@selector(handleListDisplays:)],
|
|
26
30
|
];
|
|
27
31
|
}
|
|
28
32
|
|
|
@@ -53,4 +57,17 @@ static NSString *const SOURCE_FORMAT_DESCRIPTION = @"description";
|
|
|
53
57
|
return FBResponseWithObject(result);
|
|
54
58
|
}
|
|
55
59
|
|
|
60
|
+
+ (id<FBResponsePayload>)handleListDisplays:(FBRouteRequest *)request
|
|
61
|
+
{
|
|
62
|
+
NSArray<AMScreenProperties *> *screenInfos = AMListScreens();
|
|
63
|
+
NSMutableDictionary <NSString *, NSDictionary<NSString *, id> *> *result = [NSMutableDictionary new];
|
|
64
|
+
for (AMScreenProperties *screenInfo in screenInfos) {
|
|
65
|
+
result[[NSString stringWithFormat:@"%lld", screenInfo.identifier]] = @{
|
|
66
|
+
@"id": @(screenInfo.identifier),
|
|
67
|
+
@"isMain": @(screenInfo.isMain),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return FBResponseWithObject(result.copy);
|
|
71
|
+
}
|
|
72
|
+
|
|
56
73
|
@end
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#import "FBScreenshotCommands.h"
|
|
11
11
|
|
|
12
|
-
#import "
|
|
12
|
+
#import "AMScreenUtils.h"
|
|
13
13
|
#import "FBRouteRequest.h"
|
|
14
14
|
|
|
15
15
|
@implementation FBScreenshotCommands
|
|
@@ -49,21 +49,21 @@
|
|
|
49
49
|
NSMutableDictionary <NSString *, NSDictionary<NSString *, id> *> *result = [NSMutableDictionary new];
|
|
50
50
|
NSMutableArray <NSNumber *> *availableDisplayIds = [NSMutableArray new];
|
|
51
51
|
for (XCUIScreen *screen in XCUIScreen.screens) {
|
|
52
|
-
|
|
53
|
-
if (nil
|
|
52
|
+
long long currentScreenId = AMFetchScreenId(screen);
|
|
53
|
+
if (nil != desiredId && desiredId.longLongValue != currentScreenId) {
|
|
54
54
|
continue;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
[availableDisplayIds addObject
|
|
58
|
-
result[
|
|
59
|
-
@"id":
|
|
60
|
-
@"isMain":
|
|
57
|
+
[availableDisplayIds addObject:@(currentScreenId)];
|
|
58
|
+
result[[NSString stringWithFormat:@"%lld", currentScreenId]] = @{
|
|
59
|
+
@"id": @(currentScreenId),
|
|
60
|
+
@"isMain": @(AMIsMainScreen(screen)),
|
|
61
61
|
@"payload": [screen.screenshot.PNGRepresentation base64EncodedStringWithOptions:0] ?: NSNull.null
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
if (nil != desiredId && 0 == [result count]) {
|
|
65
65
|
NSString *message = [NSString stringWithFormat:@"The screen identified by %@ is not available to XCTest. Only the following identifiers are available: %@",
|
|
66
|
-
desiredId, availableDisplayIds];
|
|
66
|
+
desiredId, [availableDisplayIds componentsJoinedByString:@","]];
|
|
67
67
|
return FBResponseWithStatus([FBCommandStatus unableToCaptureScreenErrorWithMessage:message
|
|
68
68
|
traceback:nil]);
|
|
69
69
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
20
|
+
|
|
21
|
+
@protocol AMXCTRunnerDaemonSession
|
|
22
|
+
|
|
23
|
+
- (void)stopScreenRecordingWithUUID:(NSUUID *)arg1
|
|
24
|
+
withReply:(void (^)(NSError *))arg2;
|
|
25
|
+
- (void)startScreenRecordingWithRequest:(id/* XCTScreenRecordingRequest */)arg1
|
|
26
|
+
withReply:(void (^)(id/* XCTAttachmentFutureMetadata */, NSError *))arg2;
|
|
27
|
+
- (_Bool)supportsScreenRecording;
|
|
28
|
+
|
|
29
|
+
@end
|
|
30
|
+
|
|
31
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,39 @@
|
|
|
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 <Foundation/Foundation.h>
|
|
18
|
+
|
|
19
|
+
@protocol AMXCTRunnerDaemonSession;
|
|
20
|
+
|
|
21
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
22
|
+
|
|
23
|
+
@interface AMXCTRunnerDaemonSessionWrapper : NSObject
|
|
24
|
+
|
|
25
|
+
+ (instancetype)sharedInstance;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
@return YES if the current Xcode SDK supports video recording
|
|
29
|
+
*/
|
|
30
|
+
- (BOOL)canRecordVideo;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
@returns The internal XCTest daemon session instance
|
|
34
|
+
*/
|
|
35
|
+
- (id<AMXCTRunnerDaemonSession>)daemonSession;
|
|
36
|
+
|
|
37
|
+
@end
|
|
38
|
+
|
|
39
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,43 @@
|
|
|
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 "AMXCTRunnerDaemonSessionWrapper.h"
|
|
18
|
+
|
|
19
|
+
#import "AMXCTRunnerDaemonSession.h"
|
|
20
|
+
|
|
21
|
+
@implementation AMXCTRunnerDaemonSessionWrapper
|
|
22
|
+
|
|
23
|
+
+ (instancetype)sharedInstance
|
|
24
|
+
{
|
|
25
|
+
static AMXCTRunnerDaemonSessionWrapper *instance;
|
|
26
|
+
static dispatch_once_t onceToken;
|
|
27
|
+
dispatch_once(&onceToken, ^{
|
|
28
|
+
instance = [[self alloc] init];
|
|
29
|
+
});
|
|
30
|
+
return instance;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
- (id<AMXCTRunnerDaemonSession>)daemonSession
|
|
34
|
+
{
|
|
35
|
+
return (id)[NSClassFromString(@"XCTRunnerDaemonSession") sharedSession];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
- (BOOL)canRecordVideo
|
|
39
|
+
{
|
|
40
|
+
return [(NSObject *)self.daemonSession respondsToSelector:@selector(startScreenRecordingWithRequest:withReply:)];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the BSD-style license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
8
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#import <Foundation/Foundation.h>
|
|
12
|
+
|
|
13
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
14
|
+
|
|
15
|
+
@class FBScreenRecordingPromise;
|
|
16
|
+
|
|
17
|
+
@interface FBScreenRecordingContainer : NSObject
|
|
18
|
+
|
|
19
|
+
/** The amount of video FPS */
|
|
20
|
+
@property (readonly, nonatomic) NSUInteger fps;
|
|
21
|
+
/** Codec to use, where 0 is h264, 1 - HEVC */
|
|
22
|
+
@property (readonly, nonatomic) long long codec;
|
|
23
|
+
@property (readonly, nonatomic, nullable) NSNumber *displayID;
|
|
24
|
+
/** Keep the currently active screen resording promise. Equals to nil if no active screen recordings are running */
|
|
25
|
+
@property (readonly, nonatomic, nullable) FBScreenRecordingPromise* screenRecordingPromise;
|
|
26
|
+
/** The timestamp of the video startup as Unix float seconds */
|
|
27
|
+
@property (readonly, nonatomic, nullable) NSNumber *startedAt;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
@return singleton instance
|
|
31
|
+
*/
|
|
32
|
+
+ (instancetype)sharedInstance;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
Keeps current screen recording promise
|
|
36
|
+
|
|
37
|
+
@param screenRecordingPromise a promise to set
|
|
38
|
+
@param fps FPS value
|
|
39
|
+
@param codec Codec value
|
|
40
|
+
*/
|
|
41
|
+
- (void)storeScreenRecordingPromise:(FBScreenRecordingPromise *)screenRecordingPromise
|
|
42
|
+
fps:(NSUInteger)fps
|
|
43
|
+
codec:(long long)codec
|
|
44
|
+
displayID:(nullable NSNumber *)displayID;
|
|
45
|
+
/**
|
|
46
|
+
Resets the current screen recording promise
|
|
47
|
+
*/
|
|
48
|
+
- (void)reset;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
Transforms the container content to a dictionary.
|
|
52
|
+
|
|
53
|
+
@return May return nil if no screen recording is currently running
|
|
54
|
+
*/
|
|
55
|
+
- (nullable NSDictionary *)toDictionary;
|
|
56
|
+
|
|
57
|
+
@end
|
|
58
|
+
|
|
59
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the BSD-style license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
8
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#import "FBScreenRecordingContainer.h"
|
|
12
|
+
|
|
13
|
+
#import "AMScreenUtils.h"
|
|
14
|
+
#import "FBScreenRecordingPromise.h"
|
|
15
|
+
|
|
16
|
+
@interface FBScreenRecordingContainer ()
|
|
17
|
+
|
|
18
|
+
@property (readwrite) NSUInteger fps;
|
|
19
|
+
@property (readwrite) long long codec;
|
|
20
|
+
@property (readwrite) NSNumber *displayID;
|
|
21
|
+
@property (readwrite) FBScreenRecordingPromise* screenRecordingPromise;
|
|
22
|
+
@property (readwrite) NSNumber *startedAt;
|
|
23
|
+
|
|
24
|
+
@end
|
|
25
|
+
|
|
26
|
+
@implementation FBScreenRecordingContainer
|
|
27
|
+
|
|
28
|
+
+ (instancetype)sharedInstance
|
|
29
|
+
{
|
|
30
|
+
static FBScreenRecordingContainer *instance;
|
|
31
|
+
static dispatch_once_t onceToken;
|
|
32
|
+
dispatch_once(&onceToken, ^{
|
|
33
|
+
instance = [[self alloc] init];
|
|
34
|
+
});
|
|
35
|
+
return instance;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
- (void)storeScreenRecordingPromise:(FBScreenRecordingPromise *)screenRecordingPromise
|
|
39
|
+
fps:(NSUInteger)fps
|
|
40
|
+
codec:(long long)codec
|
|
41
|
+
displayID:(nullable NSNumber *)displayID
|
|
42
|
+
{
|
|
43
|
+
self.fps = fps;
|
|
44
|
+
self.codec = codec;
|
|
45
|
+
self.displayID = displayID;
|
|
46
|
+
self.screenRecordingPromise = screenRecordingPromise;
|
|
47
|
+
self.startedAt = @([NSDate.date timeIntervalSince1970]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
- (void)reset;
|
|
51
|
+
{
|
|
52
|
+
self.fps = 0;
|
|
53
|
+
self.codec = 0;
|
|
54
|
+
self.displayID = nil;
|
|
55
|
+
self.screenRecordingPromise = nil;
|
|
56
|
+
self.startedAt = nil;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
- (nullable NSDictionary *)toDictionary
|
|
60
|
+
{
|
|
61
|
+
if (nil == self.screenRecordingPromise) {
|
|
62
|
+
return nil;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return @{
|
|
66
|
+
@"fps": @(self.fps),
|
|
67
|
+
@"codec": @(self.codec),
|
|
68
|
+
@"displayId": self.displayID ?: @(AMFetchScreenId(XCUIScreen.mainScreen)),
|
|
69
|
+
@"uuid": [self.screenRecordingPromise identifier].UUIDString ?: [NSNull null],
|
|
70
|
+
@"startedAt": self.startedAt ?: [NSNull null],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@end
|
|
@@ -0,0 +1,34 @@
|
|
|
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 FBScreenRecordingPromise : NSObject
|
|
15
|
+
|
|
16
|
+
/** Unique identiifier of the video recording, also used as the default file name */
|
|
17
|
+
@property (nonatomic, readonly) NSUUID *identifier;
|
|
18
|
+
/** Native screen recording promise */
|
|
19
|
+
@property (nonatomic, readonly) id nativePromise;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
Creates a wrapper object for a native screen recording promise.
|
|
23
|
+
The actual screen recording file is stored as
|
|
24
|
+
$HOME/Library/Daemon Containers/<testmanagerd_id>Data/Attachments/<recording_id>
|
|
25
|
+
although this path is not is not accessible directly from within the XCTest container,
|
|
26
|
+
and must be accessed from elsewhere.
|
|
27
|
+
|
|
28
|
+
@param promise Native promise object to be wrapped
|
|
29
|
+
*/
|
|
30
|
+
- (instancetype)initWithNativePromise:(id)promise;
|
|
31
|
+
|
|
32
|
+
@end
|
|
33
|
+
|
|
34
|
+
NS_ASSUME_NONNULL_END
|