appium-uiautomator2-driver 6.6.1 → 6.6.2
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 +6 -0
- package/build/lib/commands/screenshot.d.ts +17 -14
- package/build/lib/commands/screenshot.d.ts.map +1 -1
- package/build/lib/commands/screenshot.js +50 -36
- package/build/lib/commands/screenshot.js.map +1 -1
- package/build/test/unit/commands/general-specs.d.ts +2 -0
- package/build/test/unit/commands/general-specs.d.ts.map +1 -0
- package/build/test/unit/commands/general-specs.js +80 -0
- package/build/test/unit/commands/general-specs.js.map +1 -0
- package/build/test/unit/commands/screenshot-specs.d.ts +2 -0
- package/build/test/unit/commands/screenshot-specs.d.ts.map +1 -0
- package/build/test/unit/commands/screenshot-specs.js +124 -0
- package/build/test/unit/commands/screenshot-specs.js.map +1 -0
- package/build/test/unit/css-converter-specs.d.ts +2 -0
- package/build/test/unit/css-converter-specs.d.ts.map +1 -0
- package/build/test/unit/css-converter-specs.js +68 -0
- package/build/test/unit/css-converter-specs.js.map +1 -0
- package/build/test/unit/driver-specs.d.ts +2 -0
- package/build/test/unit/driver-specs.d.ts.map +1 -0
- package/build/test/unit/driver-specs.js +328 -0
- package/build/test/unit/driver-specs.js.map +1 -0
- package/build/test/unit/uiautomator2-specs.d.ts +2 -0
- package/build/test/unit/uiautomator2-specs.d.ts.map +1 -0
- package/build/test/unit/uiautomator2-specs.js +325 -0
- package/build/test/unit/uiautomator2-specs.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/commands/screenshot.ts +146 -0
- package/npm-shrinkwrap.json +8 -8
- package/package.json +2 -2
- package/lib/commands/screenshot.js +0 -116
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import B from 'bluebird';
|
|
3
|
+
import {imageUtil} from 'appium/support';
|
|
4
|
+
import type {AndroidUiautomator2Driver} from '../driver';
|
|
5
|
+
import type {Screenshot} from './types';
|
|
6
|
+
import type {StringRecord} from '@appium/types';
|
|
7
|
+
|
|
8
|
+
// Matches SurfaceFlinger output format:
|
|
9
|
+
// Physical: Display 4619827259835644672 (HWC display 0): port=0 pnpId=GGL displayName="EMU_display_0"
|
|
10
|
+
// Virtual: Display 11529215049243506835 (Virtual display): displayName="Emulator 2D Display" uniqueId="..."
|
|
11
|
+
const DISPLAY_PATTERN = /^Display\s+(\d+)\s+\((?:HWC\s+display\s+(\d+)|Virtual\s+display)\):.*?displayName="([^"]*)"/gm;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parses SurfaceFlinger display output to extract display information.
|
|
15
|
+
* @param displaysInfo - The raw output from `adb shell dumpsys SurfaceFlinger --display-id`
|
|
16
|
+
* @returns A record mapping display IDs to their information (without payload)
|
|
17
|
+
*/
|
|
18
|
+
export function parseSurfaceFlingerDisplays(
|
|
19
|
+
displaysInfo: string
|
|
20
|
+
): Record<string, Partial<Screenshot>> {
|
|
21
|
+
const infos: Record<string, Partial<Screenshot>> = {};
|
|
22
|
+
const lines = displaysInfo.split('\n');
|
|
23
|
+
|
|
24
|
+
for (const line of lines) {
|
|
25
|
+
let match: RegExpExecArray | null;
|
|
26
|
+
|
|
27
|
+
// Try to match display header line
|
|
28
|
+
if ((match = DISPLAY_PATTERN.exec(line))) {
|
|
29
|
+
const [, matchedDisplayId, hwcId, displayName] = match; // Skip match[0] (full match), then Display ID, HWC ID (optional), Display name
|
|
30
|
+
|
|
31
|
+
// Determine if default: HWC display 0 is default, or first physical display if no HWC info
|
|
32
|
+
const isDefault = hwcId !== undefined
|
|
33
|
+
? hwcId === '0'
|
|
34
|
+
: !line.includes('Virtual') && Object.keys(infos).length === 0;
|
|
35
|
+
|
|
36
|
+
infos[matchedDisplayId] = {
|
|
37
|
+
id: matchedDisplayId,
|
|
38
|
+
isDefault,
|
|
39
|
+
name: displayName || undefined,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Reset regex lastIndex for next iteration
|
|
43
|
+
DISPLAY_PATTERN.lastIndex = 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return infos;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Takes a screenshot of the current viewport
|
|
52
|
+
*/
|
|
53
|
+
export async function mobileViewportScreenshot(
|
|
54
|
+
this: AndroidUiautomator2Driver
|
|
55
|
+
): Promise<string> {
|
|
56
|
+
return await this.getViewportScreenshot();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gets a screenshot of the current viewport
|
|
61
|
+
*/
|
|
62
|
+
export async function getViewportScreenshot(
|
|
63
|
+
this: AndroidUiautomator2Driver
|
|
64
|
+
): Promise<string> {
|
|
65
|
+
const screenshot = await this.getScreenshot();
|
|
66
|
+
const rect = await this.getViewPortRect();
|
|
67
|
+
return await imageUtil.cropBase64Image(screenshot, rect);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Gets a screenshot of the current screen
|
|
72
|
+
*/
|
|
73
|
+
export async function getScreenshot(
|
|
74
|
+
this: AndroidUiautomator2Driver
|
|
75
|
+
): Promise<string> {
|
|
76
|
+
if (this.mjpegStream) {
|
|
77
|
+
const data = await this.mjpegStream.lastChunkPNGBase64();
|
|
78
|
+
if (data) {
|
|
79
|
+
return data;
|
|
80
|
+
}
|
|
81
|
+
this.log.warn(
|
|
82
|
+
'Tried to get screenshot from active MJPEG stream, but there ' +
|
|
83
|
+
'was no data yet. Falling back to regular screenshot methods.'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return String(
|
|
87
|
+
await this.uiautomator2.jwproxy.command('/screenshot', 'GET')
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Retrieves screenshots of each display available to Android.
|
|
93
|
+
* This functionality is only supported since Android 10.
|
|
94
|
+
* @param displayId - Android display identifier to take a screenshot for.
|
|
95
|
+
* If not provided then screenshots of all displays are going to be returned.
|
|
96
|
+
* If no matches were found then an error is thrown.
|
|
97
|
+
*/
|
|
98
|
+
export async function mobileScreenshots(
|
|
99
|
+
this: AndroidUiautomator2Driver,
|
|
100
|
+
displayId?: number | string
|
|
101
|
+
): Promise<StringRecord<Screenshot>> {
|
|
102
|
+
const displaysInfo = await this.adb.shell([
|
|
103
|
+
'dumpsys',
|
|
104
|
+
'SurfaceFlinger',
|
|
105
|
+
'--display-id',
|
|
106
|
+
]);
|
|
107
|
+
const infos = parseSurfaceFlingerDisplays(displaysInfo);
|
|
108
|
+
if (_.isEmpty(infos)) {
|
|
109
|
+
this.log.debug(displaysInfo);
|
|
110
|
+
throw new Error('Cannot determine the information about connected Android displays');
|
|
111
|
+
}
|
|
112
|
+
this.log.info(`Parsed Android display infos: ${JSON.stringify(infos)}`);
|
|
113
|
+
|
|
114
|
+
const toB64Screenshot = async (dispId: string): Promise<string> =>
|
|
115
|
+
(await this.adb.takeScreenshot(dispId)).toString('base64');
|
|
116
|
+
|
|
117
|
+
const displayIdStr: string | null =
|
|
118
|
+
_.isNil(displayId) || displayId === ''
|
|
119
|
+
? null
|
|
120
|
+
: String(displayId);
|
|
121
|
+
|
|
122
|
+
if (displayIdStr) {
|
|
123
|
+
if (!infos[displayIdStr]) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`The provided display identifier '${displayId}' is not known. ` +
|
|
126
|
+
`Only the following displays have been detected: ${JSON.stringify(infos)}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
[displayIdStr]: {
|
|
131
|
+
...infos[displayIdStr],
|
|
132
|
+
payload: await toB64Screenshot(displayIdStr),
|
|
133
|
+
} as Screenshot,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const allInfos = _.values(infos).filter((info): info is Partial<Screenshot> & {id: string} => !!info?.id);
|
|
138
|
+
const screenshots = await B.all(allInfos.map((info) => toB64Screenshot(info.id)));
|
|
139
|
+
for (const [info, payload] of _.zip(allInfos, screenshots) as Array<[Partial<Screenshot>, string]>) {
|
|
140
|
+
if (info && payload) {
|
|
141
|
+
info.payload = payload;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return infos as StringRecord<Screenshot>;
|
|
145
|
+
}
|
|
146
|
+
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-uiautomator2-driver",
|
|
3
|
-
"version": "6.6.
|
|
3
|
+
"version": "6.6.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "appium-uiautomator2-driver",
|
|
9
|
-
"version": "6.6.
|
|
9
|
+
"version": "6.6.2",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"appium-adb": "^14.0.0",
|
|
@@ -631,9 +631,9 @@
|
|
|
631
631
|
}
|
|
632
632
|
},
|
|
633
633
|
"node_modules/appium-chromedriver": {
|
|
634
|
-
"version": "8.0.
|
|
635
|
-
"resolved": "https://registry.npmjs.org/appium-chromedriver/-/appium-chromedriver-8.0.
|
|
636
|
-
"integrity": "sha512-
|
|
634
|
+
"version": "8.0.24",
|
|
635
|
+
"resolved": "https://registry.npmjs.org/appium-chromedriver/-/appium-chromedriver-8.0.24.tgz",
|
|
636
|
+
"integrity": "sha512-OL2OoICSwBzv7k08MnVEemRGTCL+cHxyOkPWxg4gWQ4nv76+U/rKGtU9b7MP0j1rC7rdpZGjGuyDcsD3U7BZqg==",
|
|
637
637
|
"license": "Apache-2.0",
|
|
638
638
|
"dependencies": {
|
|
639
639
|
"@appium/base-driver": "^10.0.0-rc.2",
|
|
@@ -656,9 +656,9 @@
|
|
|
656
656
|
}
|
|
657
657
|
},
|
|
658
658
|
"node_modules/appium-uiautomator2-server": {
|
|
659
|
-
"version": "9.
|
|
660
|
-
"resolved": "https://registry.npmjs.org/appium-uiautomator2-server/-/appium-uiautomator2-server-9.
|
|
661
|
-
"integrity": "sha512-
|
|
659
|
+
"version": "9.9.0",
|
|
660
|
+
"resolved": "https://registry.npmjs.org/appium-uiautomator2-server/-/appium-uiautomator2-server-9.9.0.tgz",
|
|
661
|
+
"integrity": "sha512-j9XN7AjP7dWk9HA+/segTQUyhV4n0LyMJdtKMLRXp60r+6oYX4UYh57Rp9NaHY5UPykw+2eFhOdc5n101IDItg==",
|
|
662
662
|
"license": "Apache-2.0",
|
|
663
663
|
"engines": {
|
|
664
664
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0",
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"automated testing",
|
|
8
8
|
"android"
|
|
9
9
|
],
|
|
10
|
-
"version": "6.6.
|
|
10
|
+
"version": "6.6.2",
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/appium/appium-uiautomator2-driver/issues"
|
|
13
13
|
},
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"rebuild": "npm run clean; npm run build",
|
|
49
49
|
"format": "prettier -w ./lib",
|
|
50
50
|
"reset": "node ./scripts/reset.js",
|
|
51
|
-
"test": "mocha --exit --timeout 1m \"./test/unit/**/*-specs.
|
|
51
|
+
"test": "mocha --exit --timeout 1m \"./test/unit/**/*-specs.ts\""
|
|
52
52
|
},
|
|
53
53
|
"prettier": {
|
|
54
54
|
"bracketSpacing": false,
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
import B from 'bluebird';
|
|
3
|
-
import {imageUtil} from 'appium/support';
|
|
4
|
-
|
|
5
|
-
// Display 4619827259835644672 (HWC display 0): port=0 pnpId=GGL displayName="EMU_display_0"
|
|
6
|
-
const DISPLAY_PATTERN = /^Display\s+(\d+)\s+\(.+display\s+(\d+)\).+displayName="([^"]*)/gm;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @this {AndroidUiautomator2Driver}
|
|
10
|
-
* @returns {Promise<string>}
|
|
11
|
-
*/
|
|
12
|
-
export async function mobileViewportScreenshot() {
|
|
13
|
-
return await this.getViewportScreenshot();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @this {AndroidUiautomator2Driver}
|
|
18
|
-
* @returns {Promise<string>}
|
|
19
|
-
*/
|
|
20
|
-
export async function getViewportScreenshot() {
|
|
21
|
-
const screenshot = await this.getScreenshot();
|
|
22
|
-
const rect = await this.getViewPortRect();
|
|
23
|
-
return await imageUtil.cropBase64Image(screenshot, rect);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @this {AndroidUiautomator2Driver}
|
|
28
|
-
* @returns {Promise<string>}
|
|
29
|
-
*/
|
|
30
|
-
export async function getScreenshot() {
|
|
31
|
-
if (this.mjpegStream) {
|
|
32
|
-
const data = await this.mjpegStream.lastChunkPNGBase64();
|
|
33
|
-
if (data) {
|
|
34
|
-
return data;
|
|
35
|
-
}
|
|
36
|
-
this.log.warn(
|
|
37
|
-
'Tried to get screenshot from active MJPEG stream, but there ' +
|
|
38
|
-
'was no data yet. Falling back to regular screenshot methods.'
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
return String(
|
|
42
|
-
await /** @type {import('../uiautomator2').UiAutomator2Server} */ (
|
|
43
|
-
this.uiautomator2
|
|
44
|
-
).jwproxy.command('/screenshot', 'GET')
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Retrieves screenshots of each display available to Android.
|
|
50
|
-
* This functionality is only supported since Android 10.
|
|
51
|
-
* @this {AndroidUiautomator2Driver}
|
|
52
|
-
* @param {number | string} [displayId] Android display identifier to take a screenshot for.
|
|
53
|
-
* If not provided then screenshots of all displays are going to be returned.
|
|
54
|
-
* If no matches were found then an error is thrown.
|
|
55
|
-
* @returns {Promise<import('@appium/types').StringRecord<import('./types').Screenshot>>}
|
|
56
|
-
*/
|
|
57
|
-
export async function mobileScreenshots(displayId) {
|
|
58
|
-
const displaysInfo = await /** @type {import('appium-adb').ADB} */ (this.adb).shell([
|
|
59
|
-
'dumpsys',
|
|
60
|
-
'SurfaceFlinger',
|
|
61
|
-
'--display-id',
|
|
62
|
-
]);
|
|
63
|
-
/** @type {import('@appium/types').StringRecord<import('./types').Screenshot>} */
|
|
64
|
-
const infos = {};
|
|
65
|
-
let match;
|
|
66
|
-
while ((match = DISPLAY_PATTERN.exec(displaysInfo))) {
|
|
67
|
-
infos[match[1]] = /** @type {any} */ ({
|
|
68
|
-
id: match[1],
|
|
69
|
-
isDefault: match[2] === '0',
|
|
70
|
-
name: match[3],
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
if (_.isEmpty(infos)) {
|
|
74
|
-
this.log.debug(displaysInfo);
|
|
75
|
-
throw new Error('Cannot determine the information about connected Android displays');
|
|
76
|
-
}
|
|
77
|
-
this.log.info(`Parsed Android display infos: ${JSON.stringify(infos)}`);
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @param {string} dispId
|
|
81
|
-
*/
|
|
82
|
-
const toB64Screenshot = async (dispId) =>
|
|
83
|
-
(await /** @type {import('appium-adb').ADB} */ (this.adb).takeScreenshot(dispId)).toString(
|
|
84
|
-
'base64'
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
// @ts-ignore isNaN works properly here
|
|
88
|
-
const displayIdStr = isNaN(displayId) ? null : `${displayId}`;
|
|
89
|
-
if (displayIdStr) {
|
|
90
|
-
if (!infos[displayIdStr]) {
|
|
91
|
-
throw new Error(
|
|
92
|
-
`The provided display identifier '${displayId}' is not known. ` +
|
|
93
|
-
`Only the following displays have been detected: ${JSON.stringify(infos)}`
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
[displayIdStr]: {
|
|
98
|
-
...infos[displayIdStr],
|
|
99
|
-
payload: await toB64Screenshot(displayIdStr),
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const allInfos = _.values(infos);
|
|
105
|
-
const screenshots = await B.all(allInfos.map(({id}) => toB64Screenshot(id)));
|
|
106
|
-
for (const [info, payload] of /** @type {[import('./types').Screenshot, string][]} */ (
|
|
107
|
-
_.zip(allInfos, screenshots)
|
|
108
|
-
)) {
|
|
109
|
-
info.payload = payload;
|
|
110
|
-
}
|
|
111
|
-
return infos;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @typedef {import('../driver').AndroidUiautomator2Driver} AndroidUiautomator2Driver
|
|
116
|
-
*/
|