appium-mac2-driver 3.2.15 → 3.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/applescript.d.ts.map +1 -1
- package/build/lib/commands/applescript.js.map +1 -1
- package/build/lib/commands/bidi/models.d.ts.map +1 -1
- package/build/lib/commands/bidi/models.js.map +1 -1
- package/build/lib/commands/clipboard.d.ts.map +1 -1
- package/build/lib/commands/clipboard.js +2 -2
- package/build/lib/commands/clipboard.js.map +1 -1
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +3 -1
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/gestures.d.ts.map +1 -1
- package/build/lib/commands/gestures.js.map +1 -1
- package/build/lib/commands/helpers.d.ts.map +1 -1
- package/build/lib/commands/helpers.js.map +1 -1
- package/build/lib/commands/native-record-screen.d.ts.map +1 -1
- package/build/lib/commands/native-record-screen.js +7 -7
- package/build/lib/commands/native-record-screen.js.map +1 -1
- package/build/lib/commands/navigation.d.ts.map +1 -1
- package/build/lib/commands/navigation.js.map +1 -1
- package/build/lib/commands/record-screen.d.ts.map +1 -1
- package/build/lib/commands/record-screen.js +2 -3
- package/build/lib/commands/record-screen.js.map +1 -1
- package/build/lib/commands/screenshots.d.ts.map +1 -1
- package/build/lib/commands/screenshots.js.map +1 -1
- package/build/lib/commands/source.d.ts.map +1 -1
- package/build/lib/commands/source.js.map +1 -1
- package/build/lib/doctor/optional-checks.d.ts.map +1 -1
- package/build/lib/doctor/optional-checks.js.map +1 -1
- package/build/lib/doctor/required-checks.d.ts.map +1 -1
- package/build/lib/doctor/required-checks.js.map +1 -1
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +1 -1
- package/build/lib/driver.js.map +1 -1
- package/build/lib/execute-method-map.d.ts.map +1 -1
- package/build/lib/execute-method-map.js +31 -140
- package/build/lib/execute-method-map.js.map +1 -1
- package/build/lib/index.d.ts +4 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/{index.js → lib/index.js} +1 -1
- package/build/lib/index.js.map +1 -0
- package/build/lib/logger.js.map +1 -1
- package/build/lib/types.d.ts.map +1 -1
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +2 -1
- package/build/lib/utils.js.map +1 -1
- package/build/lib/wda-mac.d.ts.map +1 -1
- package/build/lib/wda-mac.js +24 -25
- package/build/lib/wda-mac.js.map +1 -1
- package/lib/commands/app-management.ts +9 -10
- package/lib/commands/applescript.ts +5 -6
- package/lib/commands/bidi/models.ts +2 -2
- package/lib/commands/clipboard.ts +9 -8
- package/lib/commands/execute.ts +6 -5
- package/lib/commands/find.ts +3 -4
- package/lib/commands/gestures.ts +25 -26
- package/lib/commands/helpers.ts +8 -6
- package/lib/commands/native-record-screen.ts +61 -52
- package/lib/commands/navigation.ts +3 -4
- package/lib/commands/record-screen.ts +35 -32
- package/lib/commands/screenshots.ts +4 -5
- package/lib/commands/source.ts +6 -7
- package/lib/constraints.ts +0 -1
- package/lib/doctor/optional-checks.ts +3 -3
- package/lib/doctor/required-checks.ts +12 -9
- package/lib/doctor/utils.ts +0 -1
- package/lib/driver.ts +20 -18
- package/lib/execute-method-map.ts +32 -141
- package/lib/index.ts +4 -0
- package/lib/logger.ts +1 -2
- package/lib/method-map.ts +0 -1
- package/lib/types.ts +1 -1
- package/lib/utils.ts +7 -6
- package/lib/wda-mac.ts +113 -86
- package/npm-shrinkwrap.json +402 -1850
- package/package.json +7 -6
- package/build/index.d.ts +0 -4
- package/build/index.d.ts.map +0 -1
- package/build/index.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {fs, tempDir, util} from 'appium/support';
|
|
2
|
+
import {exec} from 'teen_process';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import type {
|
|
4
|
+
import type {Mac2Driver} from '../driver';
|
|
5
5
|
|
|
6
6
|
const OSASCRIPT = 'osascript';
|
|
7
7
|
const APPLE_SCRIPT_FEATURE = 'apple_script';
|
|
@@ -36,7 +36,7 @@ export async function macosExecAppleScript(
|
|
|
36
36
|
language?: string,
|
|
37
37
|
command?: string,
|
|
38
38
|
cwd?: string,
|
|
39
|
-
timeout?: number
|
|
39
|
+
timeout?: number,
|
|
40
40
|
): Promise<string> {
|
|
41
41
|
this.assertFeatureEnabled(APPLE_SCRIPT_FEATURE);
|
|
42
42
|
|
|
@@ -71,7 +71,7 @@ export async function macosExecAppleScript(
|
|
|
71
71
|
}
|
|
72
72
|
this.log.info(`Running ${OSASCRIPT} with arguments: ${util.quote(args)}`);
|
|
73
73
|
try {
|
|
74
|
-
const {
|
|
74
|
+
const {stdout} = await exec(OSASCRIPT, args, {cwd, timeout});
|
|
75
75
|
return stdout;
|
|
76
76
|
} catch (e: any) {
|
|
77
77
|
throw new Error(e.stderr || e.message);
|
|
@@ -82,4 +82,3 @@ export async function macosExecAppleScript(
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type {NativeVideoChunkAddedEvent} from './types';
|
|
2
|
+
import {NATIVE_VIDEO_CHUNK_ADDED_EVENT} from './constants';
|
|
3
3
|
|
|
4
4
|
export function toNativeVideoChunkAddedEvent(
|
|
5
5
|
uuid: string,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {Mac2Driver} from '../driver';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Sets the content of the clipboard.
|
|
@@ -10,7 +10,7 @@ import type { Mac2Driver } from '../driver';
|
|
|
10
10
|
export async function macosSetClipboard(
|
|
11
11
|
this: Mac2Driver,
|
|
12
12
|
content: string,
|
|
13
|
-
contentType: string = 'plaintext'
|
|
13
|
+
contentType: string = 'plaintext',
|
|
14
14
|
): Promise<void> {
|
|
15
15
|
await this.proxyCommand('/wda/setPasteboard', 'POST', {
|
|
16
16
|
content,
|
|
@@ -26,10 +26,11 @@ export async function macosSetClipboard(
|
|
|
26
26
|
* @returns {Promise<string>} The actual clipboard content encoded into base64 string.
|
|
27
27
|
* An empty string is returned if the clipboard contains no data for the given content type.
|
|
28
28
|
*/
|
|
29
|
-
export async function macosGetClipboard(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
export async function macosGetClipboard(
|
|
30
|
+
this: Mac2Driver,
|
|
31
|
+
contentType: string = 'plaintext',
|
|
32
|
+
): Promise<string> {
|
|
33
|
+
return /** @type {string} */ await this.proxyCommand('/wda/getPasteboard', 'POST', {
|
|
34
|
+
contentType,
|
|
35
|
+
});
|
|
35
36
|
}
|
package/lib/commands/execute.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
2
|
+
import type {Mac2Driver} from '../driver';
|
|
3
|
+
import type {StringRecord} from '@appium/types';
|
|
4
4
|
|
|
5
5
|
const EXECUTE_SCRIPT_PREFIX = 'macos:';
|
|
6
6
|
|
|
@@ -12,10 +12,12 @@ const EXECUTE_SCRIPT_PREFIX = 'macos:';
|
|
|
12
12
|
export async function execute(
|
|
13
13
|
this: Mac2Driver,
|
|
14
14
|
script: string,
|
|
15
|
-
args?: readonly any[] | StringRecord
|
|
15
|
+
args?: readonly any[] | StringRecord,
|
|
16
16
|
): Promise<any> {
|
|
17
17
|
this.log.info(`Executing extension command '${script}'`);
|
|
18
|
-
const formattedScript = String(script)
|
|
18
|
+
const formattedScript = String(script)
|
|
19
|
+
.trim()
|
|
20
|
+
.replace(/^macos:\s*/, `${EXECUTE_SCRIPT_PREFIX} `);
|
|
19
21
|
const preprocessedArgs = preprocessExecuteMethodArgs(args);
|
|
20
22
|
return await this.executeMethod(formattedScript, [preprocessedArgs]);
|
|
21
23
|
}
|
|
@@ -29,4 +31,3 @@ export async function execute(
|
|
|
29
31
|
function preprocessExecuteMethodArgs(args?: readonly any[] | StringRecord): StringRecord {
|
|
30
32
|
return (_.isArray(args) ? _.first(args) : args) ?? {};
|
|
31
33
|
}
|
|
32
|
-
|
package/lib/commands/find.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import {util} from 'appium/support';
|
|
2
|
+
import type {Mac2Driver} from '../driver';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* This is needed to make lookup by image working
|
|
@@ -14,7 +14,7 @@ export async function findElOrEls(
|
|
|
14
14
|
strategy: string,
|
|
15
15
|
selector: string,
|
|
16
16
|
mult: boolean,
|
|
17
|
-
context?: string
|
|
17
|
+
context?: string,
|
|
18
18
|
): Promise<any> {
|
|
19
19
|
const contextId = context ? util.unwrapElement(context) : context;
|
|
20
20
|
const endpoint = `/element${contextId ? `/${contextId}/element` : ''}${mult ? 's' : ''}`;
|
|
@@ -31,4 +31,3 @@ export async function findElOrEls(
|
|
|
31
31
|
value: selector,
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
-
|
package/lib/commands/gestures.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
2
|
+
import {util} from 'appium/support';
|
|
3
|
+
import {errors} from 'appium/driver';
|
|
4
|
+
import type {Mac2Driver} from '../driver';
|
|
5
|
+
import type {KeyOptions} from '../types';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Set value to the given element.
|
|
@@ -23,7 +23,7 @@ export async function macosSetValue(
|
|
|
23
23
|
elementId: string,
|
|
24
24
|
value?: any,
|
|
25
25
|
text?: string,
|
|
26
|
-
keyModifierFlags?: number
|
|
26
|
+
keyModifierFlags?: number,
|
|
27
27
|
): Promise<unknown> {
|
|
28
28
|
return await this.wda.proxy.command(`/element/${elementId}/value`, 'POST', {
|
|
29
29
|
value,
|
|
@@ -51,7 +51,7 @@ export async function macosClick(
|
|
|
51
51
|
elementId?: string,
|
|
52
52
|
x?: number,
|
|
53
53
|
y?: number,
|
|
54
|
-
keyModifierFlags?: number
|
|
54
|
+
keyModifierFlags?: number,
|
|
55
55
|
): Promise<unknown> {
|
|
56
56
|
requireElementIdOrXY(elementId, x, y);
|
|
57
57
|
const url = elementId ? `/element/${elementId}/click` : '/wda/click';
|
|
@@ -85,7 +85,7 @@ export async function macosScroll(
|
|
|
85
85
|
elementId?: string,
|
|
86
86
|
x?: number,
|
|
87
87
|
y?: number,
|
|
88
|
-
keyModifierFlags?: number
|
|
88
|
+
keyModifierFlags?: number,
|
|
89
89
|
): Promise<unknown> {
|
|
90
90
|
requireElementIdOrXY(elementId, x, y);
|
|
91
91
|
const url = elementId ? `/wda/element/${elementId}/scroll` : '/wda/scroll';
|
|
@@ -125,7 +125,7 @@ export async function macosSwipe(
|
|
|
125
125
|
x?: number,
|
|
126
126
|
y?: number,
|
|
127
127
|
velocity?: number,
|
|
128
|
-
keyModifierFlags?: number
|
|
128
|
+
keyModifierFlags?: number,
|
|
129
129
|
): Promise<unknown> {
|
|
130
130
|
requireElementIdOrXY(elementId, x, y);
|
|
131
131
|
const url = elementId ? `/wda/element/${elementId}/swipe` : `/wda/swipe`;
|
|
@@ -157,7 +157,7 @@ export async function macosRightClick(
|
|
|
157
157
|
elementId?: string,
|
|
158
158
|
x?: number,
|
|
159
159
|
y?: number,
|
|
160
|
-
keyModifierFlags?: number
|
|
160
|
+
keyModifierFlags?: number,
|
|
161
161
|
): Promise<unknown> {
|
|
162
162
|
requireElementIdOrXY(elementId, x, y);
|
|
163
163
|
const url = elementId ? `/wda/element/${elementId}/rightClick` : '/wda/rightClick';
|
|
@@ -187,7 +187,7 @@ export async function macosHover(
|
|
|
187
187
|
elementId?: string,
|
|
188
188
|
x?: number,
|
|
189
189
|
y?: number,
|
|
190
|
-
keyModifierFlags?: number
|
|
190
|
+
keyModifierFlags?: number,
|
|
191
191
|
): Promise<unknown> {
|
|
192
192
|
requireElementIdOrXY(elementId, x, y);
|
|
193
193
|
const url = elementId ? `/wda/element/${elementId}/hover` : '/wda/hover';
|
|
@@ -217,7 +217,7 @@ export async function macosDoubleClick(
|
|
|
217
217
|
elementId?: string,
|
|
218
218
|
x?: number,
|
|
219
219
|
y?: number,
|
|
220
|
-
keyModifierFlags?: number
|
|
220
|
+
keyModifierFlags?: number,
|
|
221
221
|
): Promise<unknown> {
|
|
222
222
|
requireElementIdOrXY(elementId, x, y);
|
|
223
223
|
const url = elementId ? `/wda/element/${elementId}/doubleClick` : '/wda/doubleClick';
|
|
@@ -256,7 +256,7 @@ export async function macosClickAndDrag(
|
|
|
256
256
|
startY?: number,
|
|
257
257
|
endX?: number,
|
|
258
258
|
endY?: number,
|
|
259
|
-
keyModifierFlags?: number
|
|
259
|
+
keyModifierFlags?: number,
|
|
260
260
|
): Promise<unknown> {
|
|
261
261
|
requireSourceDestWithElementsOrCoordinates(
|
|
262
262
|
sourceElementId,
|
|
@@ -264,7 +264,7 @@ export async function macosClickAndDrag(
|
|
|
264
264
|
startX,
|
|
265
265
|
startY,
|
|
266
266
|
endX,
|
|
267
|
-
endY
|
|
267
|
+
endY,
|
|
268
268
|
);
|
|
269
269
|
const url =
|
|
270
270
|
sourceElementId && destinationElementId
|
|
@@ -317,7 +317,7 @@ export async function macosClickAndDragAndHold(
|
|
|
317
317
|
endX?: number,
|
|
318
318
|
endY?: number,
|
|
319
319
|
velocity?: number,
|
|
320
|
-
keyModifierFlags?: number
|
|
320
|
+
keyModifierFlags?: number,
|
|
321
321
|
): Promise<unknown> {
|
|
322
322
|
requireSourceDestWithElementsOrCoordinates(
|
|
323
323
|
sourceElementId,
|
|
@@ -325,7 +325,7 @@ export async function macosClickAndDragAndHold(
|
|
|
325
325
|
startX,
|
|
326
326
|
startY,
|
|
327
327
|
endX,
|
|
328
|
-
endY
|
|
328
|
+
endY,
|
|
329
329
|
);
|
|
330
330
|
const url =
|
|
331
331
|
sourceElementId && destinationElementId
|
|
@@ -360,10 +360,10 @@ export async function macosClickAndDragAndHold(
|
|
|
360
360
|
export async function macosKeys(
|
|
361
361
|
this: Mac2Driver,
|
|
362
362
|
keys: (KeyOptions | string)[],
|
|
363
|
-
elementId?: string
|
|
363
|
+
elementId?: string,
|
|
364
364
|
): Promise<unknown> {
|
|
365
365
|
const url = elementId ? `/wda/element/${elementId}/keys` : '/wda/keys';
|
|
366
|
-
return await this.wda.proxy.command(url, 'POST', {
|
|
366
|
+
return await this.wda.proxy.command(url, 'POST', {keys});
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
/**
|
|
@@ -385,7 +385,7 @@ export async function macosTap(
|
|
|
385
385
|
elementId?: string,
|
|
386
386
|
x?: number,
|
|
387
387
|
y?: number,
|
|
388
|
-
keyModifierFlags?: number
|
|
388
|
+
keyModifierFlags?: number,
|
|
389
389
|
): Promise<unknown> {
|
|
390
390
|
requireElementIdOrXY(elementId, x, y);
|
|
391
391
|
const url = elementId ? `/wda/element/${elementId}/tap` : '/wda/tap';
|
|
@@ -415,7 +415,7 @@ export async function macosDoubleTap(
|
|
|
415
415
|
elementId?: string,
|
|
416
416
|
x?: number,
|
|
417
417
|
y?: number,
|
|
418
|
-
keyModifierFlags?: number
|
|
418
|
+
keyModifierFlags?: number,
|
|
419
419
|
): Promise<unknown> {
|
|
420
420
|
requireElementIdOrXY(elementId, x, y);
|
|
421
421
|
const url = elementId ? `/wda/element/${elementId}/doubleTap` : '/wda/doubleTap';
|
|
@@ -447,7 +447,7 @@ export async function macosPressAndHold(
|
|
|
447
447
|
elementId?: string,
|
|
448
448
|
x?: number,
|
|
449
449
|
y?: number,
|
|
450
|
-
keyModifierFlags?: number
|
|
450
|
+
keyModifierFlags?: number,
|
|
451
451
|
): Promise<unknown> {
|
|
452
452
|
const url = elementId ? `/wda/element/${elementId}/press` : '/wda/press';
|
|
453
453
|
return await this.wda.proxy.command(url, 'POST', {
|
|
@@ -486,7 +486,7 @@ export async function macosPressAndDrag(
|
|
|
486
486
|
startY?: number,
|
|
487
487
|
endX?: number,
|
|
488
488
|
endY?: number,
|
|
489
|
-
keyModifierFlags?: number
|
|
489
|
+
keyModifierFlags?: number,
|
|
490
490
|
): Promise<unknown> {
|
|
491
491
|
// requireSourceDestWithElementsOrCoordinates(
|
|
492
492
|
// sourceElementId, destinationElementId,
|
|
@@ -543,7 +543,7 @@ export async function macosPressAndDragAndHold(
|
|
|
543
543
|
endX?: number,
|
|
544
544
|
endY?: number,
|
|
545
545
|
velocity?: number,
|
|
546
|
-
keyModifierFlags?: number
|
|
546
|
+
keyModifierFlags?: number,
|
|
547
547
|
): Promise<unknown> {
|
|
548
548
|
requireSourceDestWithElementsOrCoordinates(
|
|
549
549
|
sourceElementId,
|
|
@@ -551,7 +551,7 @@ export async function macosPressAndDragAndHold(
|
|
|
551
551
|
startX,
|
|
552
552
|
startY,
|
|
553
553
|
endX,
|
|
554
|
-
endY
|
|
554
|
+
endY,
|
|
555
555
|
);
|
|
556
556
|
const url =
|
|
557
557
|
sourceElementId && destinationElementId
|
|
@@ -599,7 +599,7 @@ function requireSourceDestWithElementsOrCoordinates(
|
|
|
599
599
|
startX?: number,
|
|
600
600
|
startY?: number,
|
|
601
601
|
endX?: number,
|
|
602
|
-
endY?: number
|
|
602
|
+
endY?: number,
|
|
603
603
|
): void {
|
|
604
604
|
if (
|
|
605
605
|
!(_.isString(sourceElementId) && _.isString(destinationElementId)) &&
|
|
@@ -607,8 +607,7 @@ function requireSourceDestWithElementsOrCoordinates(
|
|
|
607
607
|
) {
|
|
608
608
|
throw new errors.InvalidArgumentError(
|
|
609
609
|
`'sourceElementId' and 'destinationElementId' ` +
|
|
610
|
-
`or 'startX', 'startY', 'endX' and 'endY' are required
|
|
610
|
+
`or 'startX', 'startY', 'endX' and 'endY' are required.`,
|
|
611
611
|
);
|
|
612
612
|
}
|
|
613
613
|
}
|
|
614
|
-
|
package/lib/commands/helpers.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
2
|
+
import {util, fs, net} from 'appium/support';
|
|
3
|
+
import type {Mac2Driver} from '../driver';
|
|
4
|
+
import type {StringRecord} from '@appium/types';
|
|
5
5
|
|
|
6
|
-
export async function uploadRecordedMedia
|
|
6
|
+
export async function uploadRecordedMedia(
|
|
7
7
|
this: Mac2Driver,
|
|
8
8
|
localFile: string,
|
|
9
9
|
remotePath: string | null,
|
|
10
|
-
uploadOptions: StringRecord = {}
|
|
10
|
+
uploadOptions: StringRecord = {},
|
|
11
11
|
): Promise<string> {
|
|
12
12
|
if (_.isEmpty(remotePath) || _.isNil(remotePath)) {
|
|
13
13
|
const {size} = await fs.stat(localFile);
|
|
14
|
-
this.log.debug(
|
|
14
|
+
this.log.debug(
|
|
15
|
+
`The size of the resulting screen recording is ${util.toReadableSizeString(size)}`,
|
|
16
|
+
);
|
|
15
17
|
return (await util.toInMemoryBase64(localFile)).toString();
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import B, {TimeoutError} from 'bluebird';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
6
|
-
import {
|
|
7
|
-
import type {
|
|
4
|
+
import {fs, util} from 'appium/support';
|
|
5
|
+
import type {Mac2Driver} from '../driver';
|
|
6
|
+
import {uploadRecordedMedia} from './helpers';
|
|
7
|
+
import type {AppiumLogger, StringRecord} from '@appium/types';
|
|
8
8
|
import type EventEmitter from 'node:events';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import {waitForCondition} from 'asyncbox';
|
|
10
|
+
import {exec} from 'teen_process';
|
|
11
|
+
import {BIDI_EVENT_NAME} from './bidi/constants';
|
|
12
|
+
import {toNativeVideoChunkAddedEvent} from './bidi/models';
|
|
13
13
|
|
|
14
14
|
const RECORDING_STARTUP_TIMEOUT_MS = 5000;
|
|
15
|
-
const BUFFER_SIZE =
|
|
15
|
+
const BUFFER_SIZE = 0xffff;
|
|
16
16
|
const MONITORING_INTERVAL_DURATION_MS = 1000;
|
|
17
17
|
const MAX_MONITORING_DURATION_MS = 24 * 60 * 60 * 1000; // 1 day
|
|
18
18
|
|
|
@@ -22,7 +22,7 @@ export class NativeVideoChunksBroadcaster {
|
|
|
22
22
|
private _publishers: Map<string, Promise<void>>;
|
|
23
23
|
private _terminated: boolean;
|
|
24
24
|
|
|
25
|
-
constructor
|
|
25
|
+
constructor(ee: EventEmitter, log: AppiumLogger) {
|
|
26
26
|
this._ee = ee;
|
|
27
27
|
this._log = log;
|
|
28
28
|
this._publishers = new Map();
|
|
@@ -62,22 +62,25 @@ export class NativeVideoChunksBroadcaster {
|
|
|
62
62
|
let fullPath = '';
|
|
63
63
|
let bytesRead = 0n;
|
|
64
64
|
try {
|
|
65
|
-
await waitForCondition(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
await waitForCondition(
|
|
66
|
+
async () => {
|
|
67
|
+
const paths = await listAttachments();
|
|
68
|
+
const result = paths.find((name) => name.endsWith(uuid));
|
|
69
|
+
if (result) {
|
|
70
|
+
fullPath = result;
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
waitMs: RECORDING_STARTUP_TIMEOUT_MS,
|
|
77
|
+
intervalMs: 300,
|
|
78
|
+
},
|
|
79
|
+
);
|
|
77
80
|
} catch {
|
|
78
81
|
throw new Error(
|
|
79
82
|
`The video recording identified by ${uuid} did not ` +
|
|
80
|
-
|
|
83
|
+
`start within ${RECORDING_STARTUP_TIMEOUT_MS}ms timeout`,
|
|
81
84
|
);
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -85,12 +88,14 @@ export class NativeVideoChunksBroadcaster {
|
|
|
85
88
|
while (!this._terminated && performance.now() - startedMs < MAX_MONITORING_DURATION_MS) {
|
|
86
89
|
const isCompleted = !(await isFileUsed(fullPath, 'testman'));
|
|
87
90
|
|
|
88
|
-
const {
|
|
91
|
+
const {size} = await fs.stat(fullPath, {bigint: true});
|
|
89
92
|
if (bytesRead < size) {
|
|
90
93
|
const handle = await fs.open(fullPath, 'r');
|
|
91
94
|
try {
|
|
92
95
|
while (bytesRead < size) {
|
|
93
|
-
const bufferSize = Number(
|
|
96
|
+
const bufferSize = Number(
|
|
97
|
+
size - bytesRead > BUFFER_SIZE ? BUFFER_SIZE : size - bytesRead,
|
|
98
|
+
);
|
|
94
99
|
const buf = Buffer.alloc(bufferSize);
|
|
95
100
|
await fs.read(handle, buf as any, 0, bufferSize, bytesRead as any);
|
|
96
101
|
this._ee.emit(BIDI_EVENT_NAME, toNativeVideoChunkAddedEvent(uuid, buf));
|
|
@@ -103,7 +108,7 @@ export class NativeVideoChunksBroadcaster {
|
|
|
103
108
|
|
|
104
109
|
if (isCompleted) {
|
|
105
110
|
this._log.debug(
|
|
106
|
-
`The native video recording identified by ${uuid} has been detected as completed
|
|
111
|
+
`The native video recording identified by ${uuid} has been detected as completed`,
|
|
107
112
|
);
|
|
108
113
|
return;
|
|
109
114
|
}
|
|
@@ -113,7 +118,7 @@ export class NativeVideoChunksBroadcaster {
|
|
|
113
118
|
|
|
114
119
|
this._log.warn(
|
|
115
120
|
`Stopped monitoring of the native video recording identified by ${uuid} ` +
|
|
116
|
-
|
|
121
|
+
`because of the timeout`,
|
|
117
122
|
);
|
|
118
123
|
}
|
|
119
124
|
|
|
@@ -151,7 +156,7 @@ export class NativeVideoChunksBroadcaster {
|
|
|
151
156
|
}
|
|
152
157
|
const tasks: Promise<any>[] = attachments
|
|
153
158
|
.map((attachmentPath) => [path.basename(attachmentPath), attachmentPath])
|
|
154
|
-
.filter(([name
|
|
159
|
+
.filter(([name]) => this._publishers.has(name))
|
|
155
160
|
.map(([, attachmentPath]) => fs.rimraf(attachmentPath));
|
|
156
161
|
if (_.isEmpty(tasks)) {
|
|
157
162
|
return;
|
|
@@ -159,7 +164,7 @@ export class NativeVideoChunksBroadcaster {
|
|
|
159
164
|
try {
|
|
160
165
|
await Promise.all(tasks);
|
|
161
166
|
this._log.debug(
|
|
162
|
-
`Successfully deleted ${util.pluralize('leftover video recording', tasks.length, true)}
|
|
167
|
+
`Successfully deleted ${util.pluralize('leftover video recording', tasks.length, true)}`,
|
|
163
168
|
);
|
|
164
169
|
} catch (e) {
|
|
165
170
|
this._log.warn(`Could not cleanup some leftover video recordings: ${e.message}`);
|
|
@@ -188,11 +193,11 @@ export async function macosStartNativeScreenRecording(
|
|
|
188
193
|
codec?: number,
|
|
189
194
|
displayId?: number,
|
|
190
195
|
): Promise<ActiveVideoInfo> {
|
|
191
|
-
const result = await this.wda.proxy.command('/wda/video/start', 'POST', {
|
|
196
|
+
const result = (await this.wda.proxy.command('/wda/video/start', 'POST', {
|
|
192
197
|
fps,
|
|
193
198
|
codec,
|
|
194
199
|
displayId,
|
|
195
|
-
}) as ActiveVideoInfo;
|
|
200
|
+
})) as ActiveVideoInfo;
|
|
196
201
|
this._videoChunksBroadcaster.schedule(result.uuid);
|
|
197
202
|
return result;
|
|
198
203
|
}
|
|
@@ -203,9 +208,9 @@ export async function macosStartNativeScreenRecording(
|
|
|
203
208
|
* null if no native video recording has been started.
|
|
204
209
|
*/
|
|
205
210
|
export async function macosGetNativeScreenRecordingInfo(
|
|
206
|
-
this: Mac2Driver
|
|
211
|
+
this: Mac2Driver,
|
|
207
212
|
): Promise<ActiveVideoInfo | null> {
|
|
208
|
-
return await this.wda.proxy.command('/wda/video', 'GET') as ActiveVideoInfo | null;
|
|
213
|
+
return (await this.wda.proxy.command('/wda/video', 'GET')) as ActiveVideoInfo | null;
|
|
209
214
|
}
|
|
210
215
|
|
|
211
216
|
/**
|
|
@@ -246,23 +251,25 @@ export async function macosStopNativeScreenRecording(
|
|
|
246
251
|
formFields?: StringRecord | [string, string][],
|
|
247
252
|
ignorePayload?: boolean,
|
|
248
253
|
): Promise<string> {
|
|
249
|
-
const response: ActiveVideoInfo | null = (
|
|
250
|
-
|
|
251
|
-
|
|
254
|
+
const response: ActiveVideoInfo | null = (await this.wda.proxy.command(
|
|
255
|
+
'/wda/video/stop',
|
|
256
|
+
'POST',
|
|
257
|
+
{},
|
|
258
|
+
)) as ActiveVideoInfo | null;
|
|
252
259
|
if (!response || !_.isPlainObject(response)) {
|
|
253
260
|
throw new Error(
|
|
254
|
-
'There is no active screen recording, thus nothing to stop. Did you start it before?'
|
|
261
|
+
'There is no active screen recording, thus nothing to stop. Did you start it before?',
|
|
255
262
|
);
|
|
256
263
|
}
|
|
257
264
|
|
|
258
|
-
const {
|
|
265
|
+
const {uuid} = response;
|
|
259
266
|
try {
|
|
260
267
|
await B.resolve(this._videoChunksBroadcaster.waitFor(uuid)).timeout(5000);
|
|
261
268
|
} catch (e) {
|
|
262
269
|
if (e instanceof TimeoutError) {
|
|
263
270
|
this.log.debug(
|
|
264
271
|
`The BiDi chunks broadcaster for the native screen recording identified ` +
|
|
265
|
-
|
|
272
|
+
`by ${uuid} cannot complete within 5000ms timeout`,
|
|
266
273
|
);
|
|
267
274
|
} else {
|
|
268
275
|
this.log.debug(e.stack);
|
|
@@ -273,17 +280,15 @@ export async function macosStopNativeScreenRecording(
|
|
|
273
280
|
return '';
|
|
274
281
|
}
|
|
275
282
|
|
|
276
|
-
const matchedVideoPath = _.first(
|
|
277
|
-
(await listAttachments()).filter((name) => name.endsWith(uuid))
|
|
278
|
-
);
|
|
283
|
+
const matchedVideoPath = _.first((await listAttachments()).filter((name) => name.endsWith(uuid)));
|
|
279
284
|
if (!matchedVideoPath) {
|
|
280
285
|
throw new Error(
|
|
281
286
|
`The screen recording identified by ${uuid} cannot be retrieved. ` +
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
+
`Make sure the Appium Server process or its parent process (e.g. Terminal) ` +
|
|
288
|
+
`has Full Disk Access permission enabled in 'System Preferences' -> 'Privacy & Security' tab. ` +
|
|
289
|
+
`You may verify the presence of the recorded video manually by running the ` +
|
|
290
|
+
`'find "$HOME/Library/Daemon Containers/" -type f -name "${uuid}"' command from Terminal ` +
|
|
291
|
+
`if the latter has been granted the above access permission.`,
|
|
287
292
|
);
|
|
288
293
|
}
|
|
289
294
|
const options = {
|
|
@@ -292,7 +297,7 @@ export async function macosStopNativeScreenRecording(
|
|
|
292
297
|
method,
|
|
293
298
|
headers,
|
|
294
299
|
fileFieldName,
|
|
295
|
-
formFields
|
|
300
|
+
formFields,
|
|
296
301
|
};
|
|
297
302
|
return await uploadRecordedMedia.bind(this)(matchedVideoPath, remotePath, options);
|
|
298
303
|
}
|
|
@@ -303,7 +308,7 @@ export async function macosStopNativeScreenRecording(
|
|
|
303
308
|
* @returns A map where keys are display identifiers and values are display infos
|
|
304
309
|
*/
|
|
305
310
|
export async function macosListDisplays(this: Mac2Driver): Promise<StringRecord<DisplayInfo>> {
|
|
306
|
-
return await this.wda.proxy.command('/wda/displays/list', 'GET') as StringRecord<DisplayInfo>;
|
|
311
|
+
return (await this.wda.proxy.command('/wda/displays/list', 'GET')) as StringRecord<DisplayInfo>;
|
|
307
312
|
}
|
|
308
313
|
|
|
309
314
|
// #region Private functions
|
|
@@ -311,7 +316,11 @@ export async function macosListDisplays(this: Mac2Driver): Promise<StringRecord<
|
|
|
311
316
|
async function listAttachments(): Promise<string[]> {
|
|
312
317
|
// The expected path looks like
|
|
313
318
|
// $HOME/Library/Daemon Containers/EFDD24BF-F856-411F-8954-CD5F0D6E6F3E/Data/Attachments/CAE7E5E2-5AC9-4D33-A47B-C491D644DE06
|
|
314
|
-
const deamonContainersRoot = path.resolve(
|
|
319
|
+
const deamonContainersRoot = path.resolve(
|
|
320
|
+
process.env.HOME as string,
|
|
321
|
+
'Library',
|
|
322
|
+
'Daemon Containers',
|
|
323
|
+
);
|
|
315
324
|
return await fs.glob(`*/Data/Attachments/*`, {
|
|
316
325
|
cwd: deamonContainersRoot,
|
|
317
326
|
absolute: true,
|
|
@@ -319,7 +328,7 @@ async function listAttachments(): Promise<string[]> {
|
|
|
319
328
|
}
|
|
320
329
|
|
|
321
330
|
async function isFileUsed(fpath: string, userProcessName: string): Promise<boolean> {
|
|
322
|
-
const {
|
|
331
|
+
const {stdout} = await exec('lsof', [fpath]);
|
|
323
332
|
return stdout.includes(userProcessName);
|
|
324
333
|
}
|
|
325
334
|
|
|
@@ -336,4 +345,4 @@ interface DisplayInfo {
|
|
|
336
345
|
isMain: boolean;
|
|
337
346
|
}
|
|
338
347
|
|
|
339
|
-
// #endregion
|
|
348
|
+
// #endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {Mac2Driver} from '../driver';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Opens the given URL with the default or the given application.
|
|
@@ -12,8 +12,7 @@ import type { Mac2Driver } from '../driver';
|
|
|
12
12
|
export async function macosDeepLink(
|
|
13
13
|
this: Mac2Driver,
|
|
14
14
|
url: string,
|
|
15
|
-
bundleId?: string
|
|
15
|
+
bundleId?: string,
|
|
16
16
|
): Promise<unknown> {
|
|
17
|
-
return await this.wda.proxy.command('/url', 'POST', {
|
|
17
|
+
return await this.wda.proxy.command('/url', 'POST', {url, bundleId});
|
|
18
18
|
}
|
|
19
|
-
|