appium-remote-debugger 15.7.2 → 15.8.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 +12 -0
- package/build/lib/atoms.d.ts.map +1 -1
- package/build/lib/atoms.js +27 -28
- package/build/lib/atoms.js.map +1 -1
- package/build/lib/index.d.ts.map +1 -1
- package/build/lib/index.js +7 -0
- package/build/lib/index.js.map +1 -1
- package/build/lib/mixins/connect.d.ts.map +1 -1
- package/build/lib/mixins/connect.js +21 -25
- package/build/lib/mixins/connect.js.map +1 -1
- package/build/lib/mixins/execute.d.ts.map +1 -1
- package/build/lib/mixins/execute.js +2 -8
- package/build/lib/mixins/execute.js.map +1 -1
- package/build/lib/mixins/message-handlers.d.ts.map +1 -1
- package/build/lib/mixins/message-handlers.js +8 -12
- package/build/lib/mixins/message-handlers.js.map +1 -1
- package/build/lib/mixins/misc.d.ts.map +1 -1
- package/build/lib/mixins/misc.js +5 -39
- package/build/lib/mixins/misc.js.map +1 -1
- package/build/lib/mixins/navigate.d.ts.map +1 -1
- package/build/lib/mixins/navigate.js +20 -55
- package/build/lib/mixins/navigate.js.map +1 -1
- package/build/lib/mixins/property-accessors.d.ts +24 -0
- package/build/lib/mixins/property-accessors.d.ts.map +1 -1
- package/build/lib/mixins/property-accessors.js +24 -0
- package/build/lib/mixins/property-accessors.js.map +1 -1
- package/build/lib/remote-debugger.d.ts +38 -38
- package/build/lib/remote-debugger.d.ts.map +1 -1
- package/build/lib/remote-debugger.js +64 -69
- package/build/lib/remote-debugger.js.map +1 -1
- package/build/lib/rpc/remote-messages.d.ts.map +1 -1
- package/build/lib/rpc/remote-messages.js +7 -8
- package/build/lib/rpc/remote-messages.js.map +1 -1
- package/build/lib/rpc/rpc-client-real-device-shim.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client-real-device-shim.js +3 -6
- package/build/lib/rpc/rpc-client-real-device-shim.js.map +1 -1
- package/build/lib/rpc/rpc-client-simulator.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client-simulator.js +3 -5
- package/build/lib/rpc/rpc-client-simulator.js.map +1 -1
- package/build/lib/rpc/rpc-client.d.ts +27 -27
- package/build/lib/rpc/rpc-client.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client.js +226 -224
- package/build/lib/rpc/rpc-client.js.map +1 -1
- package/build/lib/rpc/rpc-message-handler.js +7 -10
- package/build/lib/rpc/rpc-message-handler.js.map +1 -1
- package/build/lib/types.d.ts +19 -19
- package/build/lib/types.d.ts.map +1 -1
- package/build/lib/utils.d.ts +70 -4
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +171 -23
- package/build/lib/utils.js.map +1 -1
- package/lib/atoms.ts +31 -32
- package/lib/index.ts +7 -0
- package/lib/mixins/connect.ts +22 -23
- package/lib/mixins/execute.ts +3 -5
- package/lib/mixins/message-handlers.ts +9 -10
- package/lib/mixins/misc.ts +8 -7
- package/lib/mixins/navigate.ts +58 -63
- package/lib/mixins/property-accessors.ts +24 -0
- package/lib/remote-debugger.ts +74 -76
- package/lib/rpc/remote-messages.ts +10 -5
- package/lib/rpc/rpc-client-real-device-shim.ts +3 -3
- package/lib/rpc/rpc-client-simulator.ts +3 -5
- package/lib/rpc/rpc-client.ts +259 -247
- package/lib/rpc/rpc-message-handler.ts +7 -7
- package/lib/types.ts +24 -24
- package/lib/utils.ts +181 -23
- package/package.json +4 -8
- package/scripts/common.mjs +42 -37
- package/scripts/web_inspector_proxy.mjs +3 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {EventEmitter} from 'node:events';
|
|
2
2
|
import {log} from '../logger';
|
|
3
|
-
import
|
|
3
|
+
import {isPlainObject, truncateString} from '../utils';
|
|
4
4
|
import {util} from '@appium/support';
|
|
5
5
|
import type {StringRecord} from '@appium/types';
|
|
6
6
|
|
|
@@ -106,7 +106,7 @@ export default class RpcMessageHandler extends EventEmitter {
|
|
|
106
106
|
try {
|
|
107
107
|
return JSON.parse(plist.__argument.WIRMessageDataKey.toString('utf8'));
|
|
108
108
|
} catch (err: any) {
|
|
109
|
-
log.error(`Unparseable message data: ${
|
|
109
|
+
log.error(`Unparseable message data: ${truncateString(JSON.stringify(plist), 100)}`);
|
|
110
110
|
throw new Error(`Unable to parse message data: ${err.message}`);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -132,7 +132,7 @@ export default class RpcMessageHandler extends EventEmitter {
|
|
|
132
132
|
): Promise<void> {
|
|
133
133
|
if (msgId) {
|
|
134
134
|
if (this.listenerCount(msgId)) {
|
|
135
|
-
if (
|
|
135
|
+
if (Object.hasOwn(result?.result ?? {}, 'value')) {
|
|
136
136
|
result = result.result.value;
|
|
137
137
|
}
|
|
138
138
|
this.emit(msgId, error, result);
|
|
@@ -173,12 +173,12 @@ export default class RpcMessageHandler extends EventEmitter {
|
|
|
173
173
|
break;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
if (method
|
|
176
|
+
if (method?.startsWith('Network.')) {
|
|
177
177
|
// aggregate Network events, and add original method name to the arguments
|
|
178
178
|
eventNames.push('NetworkEvent');
|
|
179
179
|
args.push(method);
|
|
180
180
|
}
|
|
181
|
-
if (method
|
|
181
|
+
if (method?.startsWith('Console.')) {
|
|
182
182
|
// aggregate Console events, and add original method name to the arguments
|
|
183
183
|
eventNames.push('ConsoleEvent');
|
|
184
184
|
args.push(method);
|
|
@@ -213,7 +213,7 @@ export default class RpcMessageHandler extends EventEmitter {
|
|
|
213
213
|
return new Error(message);
|
|
214
214
|
}
|
|
215
215
|
if (dataKey.error) {
|
|
216
|
-
if (
|
|
216
|
+
if (isPlainObject(dataKey.error)) {
|
|
217
217
|
const dataKeyError = dataKey.error as DataErrorMessage;
|
|
218
218
|
const error = new Error(defaultMessage);
|
|
219
219
|
for (const key of Object.keys(dataKeyError)) {
|
|
@@ -242,7 +242,7 @@ export default class RpcMessageHandler extends EventEmitter {
|
|
|
242
242
|
if (!dataKey.error) {
|
|
243
243
|
try {
|
|
244
244
|
const message = JSON.parse(dataKey.params.message);
|
|
245
|
-
msgId =
|
|
245
|
+
msgId = message.id === undefined ? '' : String(message.id);
|
|
246
246
|
method = message.method;
|
|
247
247
|
result = message.result || message;
|
|
248
248
|
params = result.params;
|
package/lib/types.ts
CHANGED
|
@@ -75,10 +75,6 @@ export interface RemoteDebuggerOptions {
|
|
|
75
75
|
log?: AppiumLogger;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
interface RemoteDebuggerRealDeviceSpecificOptions {
|
|
79
|
-
udid: string;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
78
|
export type RemoteDebuggerRealDeviceOptions = RemoteDebuggerRealDeviceSpecificOptions &
|
|
83
79
|
RemoteDebuggerOptions;
|
|
84
80
|
|
|
@@ -110,9 +106,9 @@ export interface RpcClientSimulatorOptions {
|
|
|
110
106
|
}
|
|
111
107
|
|
|
112
108
|
export type AppIdKey = string | number;
|
|
109
|
+
|
|
113
110
|
export type PageIdKey = string | number;
|
|
114
111
|
export type TargetId = string;
|
|
115
|
-
|
|
116
112
|
export interface RemoteCommandId {
|
|
117
113
|
id: string;
|
|
118
114
|
}
|
|
@@ -135,26 +131,8 @@ export interface ProtocolCommandOpts {
|
|
|
135
131
|
params: StringRecord;
|
|
136
132
|
}
|
|
137
133
|
|
|
138
|
-
type SocketDataKey = Buffer | StringRecord;
|
|
139
|
-
|
|
140
|
-
interface RemoteCommandArgument<T extends SocketDataKey> {
|
|
141
|
-
WIRSocketDataKey?: T;
|
|
142
|
-
WIRConnectionIdentifierKey?: string;
|
|
143
|
-
WIRSenderKey?: string;
|
|
144
|
-
WIRApplicationIdentifierKey?: AppIdKey;
|
|
145
|
-
WIRPageIdentifierKey?: PageIdKey;
|
|
146
|
-
WIRMessageDataTypeKey?: string;
|
|
147
|
-
WIRDestinationKey?: string;
|
|
148
|
-
WIRMessageDataKey?: string;
|
|
149
|
-
[key: string]: any;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
interface RemoteCommandTemplated<T extends SocketDataKey> {
|
|
153
|
-
__argument: RemoteCommandArgument<T>;
|
|
154
|
-
__selector: string;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
134
|
export type RawRemoteCommand = RemoteCommandTemplated<StringRecord>;
|
|
135
|
+
|
|
158
136
|
export type RemoteCommand = RemoteCommandTemplated<Buffer>;
|
|
159
137
|
|
|
160
138
|
/**
|
|
@@ -175,3 +153,25 @@ export interface ProvisionalTargetInfo {
|
|
|
175
153
|
oldTargetId: string;
|
|
176
154
|
newTargetId: string;
|
|
177
155
|
}
|
|
156
|
+
interface RemoteDebuggerRealDeviceSpecificOptions {
|
|
157
|
+
udid: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type SocketDataKey = Buffer | StringRecord;
|
|
161
|
+
|
|
162
|
+
interface RemoteCommandArgument<T extends SocketDataKey> {
|
|
163
|
+
WIRSocketDataKey?: T;
|
|
164
|
+
WIRConnectionIdentifierKey?: string;
|
|
165
|
+
WIRSenderKey?: string;
|
|
166
|
+
WIRApplicationIdentifierKey?: AppIdKey;
|
|
167
|
+
WIRPageIdentifierKey?: PageIdKey;
|
|
168
|
+
WIRMessageDataTypeKey?: string;
|
|
169
|
+
WIRDestinationKey?: string;
|
|
170
|
+
WIRMessageDataKey?: string;
|
|
171
|
+
[key: string]: any;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
interface RemoteCommandTemplated<T extends SocketDataKey> {
|
|
175
|
+
__argument: RemoteCommandArgument<T>;
|
|
176
|
+
__selector: string;
|
|
177
|
+
}
|
package/lib/utils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {errorFromMJSONWPStatusCode} from '@appium/base-driver';
|
|
3
2
|
import {util, node} from '@appium/support';
|
|
3
|
+
import {isDeepStrictEqual} from 'node:util';
|
|
4
4
|
import nodeFs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import type {StringRecord} from '@appium/types';
|
|
@@ -17,6 +17,149 @@ const ACCEPTED_PAGE_TYPES = [
|
|
|
17
17
|
];
|
|
18
18
|
export const RESPONSE_LOG_LENGTH = 100;
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Error thrown when an async operation exceeds the configured timeout.
|
|
22
|
+
*/
|
|
23
|
+
export class TimeoutError extends Error {
|
|
24
|
+
constructor(message: string = 'Operation timed out') {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'TimeoutError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Truncates a string to the requested length and appends ellipsis when needed.
|
|
32
|
+
*
|
|
33
|
+
* @param value - The input string.
|
|
34
|
+
* @param length - Maximum output length.
|
|
35
|
+
* @returns The original string when short enough, otherwise a truncated variant.
|
|
36
|
+
*/
|
|
37
|
+
export function truncateString(value: string, length: number): string {
|
|
38
|
+
if (value.length <= length) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
return `${value.slice(0, Math.max(0, length - 1))}…`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Creates a shallow object where undefined keys from `target` are filled
|
|
46
|
+
* from `defaultsObj`.
|
|
47
|
+
*
|
|
48
|
+
* @param target - The object with priority values.
|
|
49
|
+
* @param defaultsObj - The object providing fallback values.
|
|
50
|
+
* @returns A new object containing merged defaulted values.
|
|
51
|
+
*/
|
|
52
|
+
export function defaults<T extends Record<string, any>, U extends Record<string, any>>(
|
|
53
|
+
target: T,
|
|
54
|
+
defaultsObj: U,
|
|
55
|
+
): T & U {
|
|
56
|
+
const result = {...target} as T & U;
|
|
57
|
+
for (const [key, value] of Object.entries(defaultsObj)) {
|
|
58
|
+
if (result[key as keyof (T & U)] === undefined) {
|
|
59
|
+
(result as any)[key] = value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Determines whether a value is a plain object.
|
|
67
|
+
*
|
|
68
|
+
* @param value - The value to check.
|
|
69
|
+
* @returns True when the value is a non-null non-array object.
|
|
70
|
+
*/
|
|
71
|
+
export function isPlainObject(value: unknown): value is Record<string, any> {
|
|
72
|
+
if (value == null || typeof value !== 'object' || Array.isArray(value)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const prototype = Object.getPrototypeOf(value);
|
|
76
|
+
return prototype === Object.prototype || prototype === null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Checks whether a value should be treated as empty.
|
|
81
|
+
*
|
|
82
|
+
* @param value - The value to evaluate.
|
|
83
|
+
* @returns True for nullish values, empty arrays/strings/maps/sets, or empty objects.
|
|
84
|
+
*/
|
|
85
|
+
export function isEmpty(value: unknown): boolean {
|
|
86
|
+
if (value == null) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (Array.isArray(value) || typeof value === 'string') {
|
|
90
|
+
return value.length === 0;
|
|
91
|
+
}
|
|
92
|
+
if (value instanceof Map || value instanceof Set) {
|
|
93
|
+
return value.size === 0;
|
|
94
|
+
}
|
|
95
|
+
if (isPlainObject(value)) {
|
|
96
|
+
return Object.keys(value).length === 0;
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Deduplicates array entries while preserving order.
|
|
103
|
+
*
|
|
104
|
+
* @param items - Items to deduplicate.
|
|
105
|
+
* @returns The input items without duplicates.
|
|
106
|
+
*/
|
|
107
|
+
export function uniq<T>(items: T[]): T[] {
|
|
108
|
+
return [...new Set(items)];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Performs deep strict equality comparison.
|
|
113
|
+
*
|
|
114
|
+
* @param a - First value.
|
|
115
|
+
* @param b - Second value.
|
|
116
|
+
* @returns True when both values are deeply equal.
|
|
117
|
+
*/
|
|
118
|
+
export function deepEqual(a: unknown, b: unknown): boolean {
|
|
119
|
+
return isDeepStrictEqual(a, b);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Returns a promise that resolves after the specified delay.
|
|
124
|
+
*
|
|
125
|
+
* @param ms - Delay in milliseconds.
|
|
126
|
+
* @returns A promise that resolves when the delay expires.
|
|
127
|
+
*/
|
|
128
|
+
export function delay(ms: number): Promise<void> {
|
|
129
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Wraps a promise with a timeout.
|
|
134
|
+
*
|
|
135
|
+
* @param promise - The promise to resolve.
|
|
136
|
+
* @param timeoutMs - Maximum time to wait in milliseconds.
|
|
137
|
+
* @param message - Optional timeout message.
|
|
138
|
+
* @returns A promise that resolves/rejects with the original promise result, or rejects on timeout.
|
|
139
|
+
*/
|
|
140
|
+
export async function withTimeout<T>(
|
|
141
|
+
promise: Promise<T>,
|
|
142
|
+
timeoutMs: number,
|
|
143
|
+
message?: string,
|
|
144
|
+
): Promise<T> {
|
|
145
|
+
let timeoutId: NodeJS.Timeout | undefined;
|
|
146
|
+
try {
|
|
147
|
+
return await Promise.race([
|
|
148
|
+
promise,
|
|
149
|
+
new Promise<T>((_resolve, reject) => {
|
|
150
|
+
timeoutId = setTimeout(
|
|
151
|
+
() => reject(new TimeoutError(message ?? `Operation timed out after ${timeoutMs}ms`)),
|
|
152
|
+
timeoutMs,
|
|
153
|
+
);
|
|
154
|
+
}),
|
|
155
|
+
]);
|
|
156
|
+
} finally {
|
|
157
|
+
if (timeoutId) {
|
|
158
|
+
clearTimeout(timeoutId);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
20
163
|
/**
|
|
21
164
|
* Takes a dictionary from the remote debugger and converts it into a more
|
|
22
165
|
* manageable AppInfo object with understandable keys.
|
|
@@ -26,15 +169,16 @@ export const RESPONSE_LOG_LENGTH = 100;
|
|
|
26
169
|
*/
|
|
27
170
|
export function appInfoFromDict(dict: Record<string, any>): [string, AppInfo] {
|
|
28
171
|
const id = dict.WIRApplicationIdentifierKey;
|
|
29
|
-
const isProxy =
|
|
30
|
-
|
|
31
|
-
|
|
172
|
+
const isProxy =
|
|
173
|
+
typeof dict.WIRIsApplicationProxyKey === 'string'
|
|
174
|
+
? dict.WIRIsApplicationProxyKey.toLowerCase() === 'true'
|
|
175
|
+
: dict.WIRIsApplicationProxyKey;
|
|
32
176
|
// automation enabled can be either from the keys
|
|
33
177
|
// - WIRRemoteAutomationEnabledKey (boolean)
|
|
34
178
|
// - WIRAutomationAvailabilityKey (string or boolean)
|
|
35
179
|
let isAutomationEnabled: boolean | string = !!dict.WIRRemoteAutomationEnabledKey;
|
|
36
|
-
if (
|
|
37
|
-
if (
|
|
180
|
+
if (Object.hasOwn(dict, 'WIRAutomationAvailabilityKey')) {
|
|
181
|
+
if (typeof dict.WIRAutomationAvailabilityKey === 'string') {
|
|
38
182
|
isAutomationEnabled =
|
|
39
183
|
dict.WIRAutomationAvailabilityKey === 'WIRAutomationAvailabilityUnknown'
|
|
40
184
|
? 'Unknown'
|
|
@@ -65,16 +209,16 @@ export function appInfoFromDict(dict: Record<string, any>): [string, AppInfo] {
|
|
|
65
209
|
*/
|
|
66
210
|
export function pageArrayFromDict(pageDict: StringRecord): Page[] {
|
|
67
211
|
return (
|
|
68
|
-
|
|
212
|
+
Object.values(pageDict)
|
|
69
213
|
// count only WIRTypeWeb pages and ignore all others (WIRTypeJavaScript etc)
|
|
70
214
|
.filter(
|
|
71
|
-
(dict) =>
|
|
215
|
+
(dict) => dict.WIRTypeKey === undefined || ACCEPTED_PAGE_TYPES.includes(dict.WIRTypeKey),
|
|
72
216
|
)
|
|
73
217
|
.map((dict) => ({
|
|
74
218
|
id: dict.WIRPageIdentifierKey,
|
|
75
219
|
title: dict.WIRTitleKey,
|
|
76
220
|
url: dict.WIRURLKey,
|
|
77
|
-
isKey:
|
|
221
|
+
isKey: dict.WIRConnectionIdentifierKey !== undefined,
|
|
78
222
|
}))
|
|
79
223
|
);
|
|
80
224
|
}
|
|
@@ -89,7 +233,7 @@ export function pageArrayFromDict(pageDict: StringRecord): Page[] {
|
|
|
89
233
|
* @returns An array of unique application identifier keys matching the bundle ID.
|
|
90
234
|
*/
|
|
91
235
|
export function appIdsForBundle(bundleId: string, appDict: AppDict): string[] {
|
|
92
|
-
const appIds: string[] =
|
|
236
|
+
const appIds: string[] = Object.entries(appDict)
|
|
93
237
|
.filter(([, data]) => data.bundleId === bundleId)
|
|
94
238
|
.map(([key]) => key);
|
|
95
239
|
|
|
@@ -98,7 +242,7 @@ export function appIdsForBundle(bundleId: string, appDict: AppDict): string[] {
|
|
|
98
242
|
return appIdsForBundle(WEB_CONTENT_BUNDLE_ID, appDict);
|
|
99
243
|
}
|
|
100
244
|
|
|
101
|
-
return
|
|
245
|
+
return uniq(appIds);
|
|
102
246
|
}
|
|
103
247
|
|
|
104
248
|
/**
|
|
@@ -112,8 +256,8 @@ export function appIdsForBundle(bundleId: string, appDict: AppDict): string[] {
|
|
|
112
256
|
*/
|
|
113
257
|
export function checkParams<T extends StringRecord>(params: T): T {
|
|
114
258
|
// check if all parameters have a value
|
|
115
|
-
const errors =
|
|
116
|
-
.filter(([, value]) =>
|
|
259
|
+
const errors = Object.entries(params)
|
|
260
|
+
.filter(([, value]) => value == null)
|
|
117
261
|
.map(([param]) => param);
|
|
118
262
|
if (errors.length) {
|
|
119
263
|
throw new Error(`Missing ${util.pluralize('parameter', errors.length)}: ${errors.join(', ')}`);
|
|
@@ -134,7 +278,10 @@ export function simpleStringify(value: any, multiline: boolean = false): string
|
|
|
134
278
|
return JSON.stringify(value);
|
|
135
279
|
}
|
|
136
280
|
|
|
137
|
-
const cleanValue =
|
|
281
|
+
const cleanValue =
|
|
282
|
+
value && (typeof value === 'object' || typeof value === 'function')
|
|
283
|
+
? removeNoisyProperties(structuredClone(value))
|
|
284
|
+
: value;
|
|
138
285
|
return multiline ? JSON.stringify(cleanValue, null, 2) : JSON.stringify(cleanValue);
|
|
139
286
|
}
|
|
140
287
|
|
|
@@ -149,29 +296,29 @@ export function simpleStringify(value: any, multiline: boolean = false): string
|
|
|
149
296
|
* an error status code.
|
|
150
297
|
*/
|
|
151
298
|
export function convertJavascriptEvaluationResult(res: any): any {
|
|
152
|
-
if (
|
|
299
|
+
if (res === undefined) {
|
|
153
300
|
throw new Error(
|
|
154
|
-
`Did not get OK result from remote debugger. Result was: ${
|
|
301
|
+
`Did not get OK result from remote debugger. Result was: ${truncateString(simpleStringify(res), RESPONSE_LOG_LENGTH)}`,
|
|
155
302
|
);
|
|
156
|
-
} else if (
|
|
303
|
+
} else if (typeof res === 'string') {
|
|
157
304
|
try {
|
|
158
305
|
res = JSON.parse(res);
|
|
159
306
|
} catch {
|
|
160
307
|
// we might get a serialized object, but we might not
|
|
161
308
|
// if we get here, it is just a value
|
|
162
309
|
}
|
|
163
|
-
} else if (
|
|
310
|
+
} else if ((typeof res !== 'object' && typeof res !== 'function') || res === null) {
|
|
164
311
|
throw new Error(`Result has unexpected type: (${typeof res}).`);
|
|
165
312
|
}
|
|
166
313
|
|
|
167
|
-
if (res
|
|
314
|
+
if (Object.hasOwn(res, 'status') && res.status !== 0) {
|
|
168
315
|
// we got some form of error.
|
|
169
316
|
throw errorFromMJSONWPStatusCode(res.status, res.value.message || res.value);
|
|
170
317
|
}
|
|
171
318
|
|
|
172
319
|
// with either have an object with a `value` property (even if `null`),
|
|
173
320
|
// or a plain object
|
|
174
|
-
const value =
|
|
321
|
+
const value = Object.hasOwn(res, 'value') ? res.value : res;
|
|
175
322
|
return removeNoisyProperties(value);
|
|
176
323
|
}
|
|
177
324
|
|
|
@@ -182,13 +329,24 @@ export function convertJavascriptEvaluationResult(res: any): any {
|
|
|
182
329
|
* @returns The full path to the module root directory.
|
|
183
330
|
* @throws Error if the module root folder cannot be determined.
|
|
184
331
|
*/
|
|
185
|
-
|
|
332
|
+
let cachedModuleRoot: string | undefined;
|
|
333
|
+
/**
|
|
334
|
+
* Calculates and memoizes the path to the current module root.
|
|
335
|
+
*
|
|
336
|
+
* @returns The full path to the module root directory.
|
|
337
|
+
* @throws Error if the module root folder cannot be determined.
|
|
338
|
+
*/
|
|
339
|
+
export function getModuleRoot(): string {
|
|
340
|
+
if (cachedModuleRoot) {
|
|
341
|
+
return cachedModuleRoot;
|
|
342
|
+
}
|
|
186
343
|
const root = node.getModuleRootSync(MODULE_NAME, __filename);
|
|
187
344
|
if (!root) {
|
|
188
345
|
throw new Error(`Cannot find the root folder of the ${MODULE_NAME} Node.js module`);
|
|
189
346
|
}
|
|
347
|
+
cachedModuleRoot = root;
|
|
190
348
|
return root;
|
|
191
|
-
}
|
|
349
|
+
}
|
|
192
350
|
|
|
193
351
|
/**
|
|
194
352
|
* Reads and parses the package.json file from the module root.
|
|
@@ -217,7 +375,7 @@ export function canUseWebInspectorShim(platformVersion: string): boolean {
|
|
|
217
375
|
* @returns The cleaned object.
|
|
218
376
|
*/
|
|
219
377
|
function removeNoisyProperties<T>(obj: T): T {
|
|
220
|
-
if (
|
|
378
|
+
if (obj && typeof obj === 'object') {
|
|
221
379
|
for (const property of ['ceil', 'clone', 'floor', 'round', 'scale', 'toString']) {
|
|
222
380
|
delete obj[property];
|
|
223
381
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"keywords": [
|
|
5
5
|
"appium"
|
|
6
6
|
],
|
|
7
|
-
"version": "15.
|
|
7
|
+
"version": "15.8.0",
|
|
8
8
|
"author": "Appium Contributors",
|
|
9
9
|
"license": "Apache-2.0",
|
|
10
10
|
"repository": {
|
|
@@ -35,15 +35,13 @@
|
|
|
35
35
|
"@appium/base-driver": "^10.0.0-rc.1",
|
|
36
36
|
"@appium/support": "^7.0.0-rc.1",
|
|
37
37
|
"appium-ios-device": "^3.0.0",
|
|
38
|
-
"asyncbox": "^6.1.0",
|
|
39
38
|
"async-lock": "^1.4.1",
|
|
40
|
-
"
|
|
39
|
+
"asyncbox": "^6.1.0",
|
|
41
40
|
"glob": "^13.0.0",
|
|
42
|
-
"lodash": "^4.17.11",
|
|
43
41
|
"teen_process": "^4.0.4"
|
|
44
42
|
},
|
|
45
43
|
"optionalDependencies": {
|
|
46
|
-
"appium-ios-remotexpc": "^0.
|
|
44
|
+
"appium-ios-remotexpc": "^1.0.0"
|
|
47
45
|
},
|
|
48
46
|
"scripts": {
|
|
49
47
|
"build": "tsc -b",
|
|
@@ -74,8 +72,6 @@
|
|
|
74
72
|
"@appium/types": "^1.0.0-rc.1",
|
|
75
73
|
"@semantic-release/changelog": "^6.0.1",
|
|
76
74
|
"@semantic-release/git": "^10.0.1",
|
|
77
|
-
"@types/bluebird": "^3.5.38",
|
|
78
|
-
"@types/lodash": "^4.14.196",
|
|
79
75
|
"@types/mocha": "^10.0.1",
|
|
80
76
|
"@types/node": "^25.0.0",
|
|
81
77
|
"appium-ios-simulator": "^8.0.0",
|
|
@@ -87,8 +83,8 @@
|
|
|
87
83
|
"mocha-multi-reporters": "^1.5.1",
|
|
88
84
|
"node-simctl": "^8.0.0",
|
|
89
85
|
"prettier": "^3.0.0",
|
|
90
|
-
"serve-static": "^2.2.0",
|
|
91
86
|
"semantic-release": "^25.0.2",
|
|
87
|
+
"serve-static": "^2.2.0",
|
|
92
88
|
"sinon": "^21.0.0",
|
|
93
89
|
"ts-node": "^10.9.1",
|
|
94
90
|
"typescript": "^6.0.3"
|
package/scripts/common.mjs
CHANGED
|
@@ -40,6 +40,48 @@ const ATOMS_DIRECTORY = path.resolve(WORKING_ROOT_DIR, 'atoms');
|
|
|
40
40
|
const LAST_UPDATE_FILE = path.resolve(ATOMS_DIRECTORY, 'lastupdate');
|
|
41
41
|
let bazelCommand;
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Clone the target selenium repository and branch into the temporary directory.
|
|
45
|
+
*/
|
|
46
|
+
export async function seleniumClone () {
|
|
47
|
+
await seleniumMkdir();
|
|
48
|
+
await seleniumClean();
|
|
49
|
+
const cloneArgs = (branch) => ([
|
|
50
|
+
'clone',
|
|
51
|
+
`--branch=${branch}`,
|
|
52
|
+
'--depth=1',
|
|
53
|
+
SELENIUM_GITHUB,
|
|
54
|
+
SELENIUM_DIRECTORY,
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
log.info(`Cloning branch '${SELENIUM_BRANCH}' from '${SELENIUM_GITHUB}'`);
|
|
58
|
+
await exec('git', cloneArgs(SELENIUM_BRANCH));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Builds Selenium atoms and imports them into this repository.
|
|
63
|
+
*
|
|
64
|
+
* @param {boolean} shouldClean - Whether to run `bazel clean` before building.
|
|
65
|
+
*/
|
|
66
|
+
export async function importAtoms(shouldClean) {
|
|
67
|
+
await checkBazel();
|
|
68
|
+
await atomsCleanDir();
|
|
69
|
+
if (shouldClean) {
|
|
70
|
+
await atomsClean();
|
|
71
|
+
}
|
|
72
|
+
await atomsMkdir();
|
|
73
|
+
await atomsBuild();
|
|
74
|
+
const bazelOutDir = await getBazelOutDir();
|
|
75
|
+
const atomsDir = path.resolve(bazelOutDir, BAZEL_WD_ATOMS_DIR);
|
|
76
|
+
const atomsInjectDir = path.resolve(bazelOutDir, BAZEL_WD_ATOMS_INJECT_DIR);
|
|
77
|
+
const fragmentsDir = path.resolve(bazelOutDir, BAZEL_FRAGMENTS_DIR);
|
|
78
|
+
await atomsCopyAtoms(fragmentsDir);
|
|
79
|
+
// copy fragments first and atoms later so atoms overwrite fragments
|
|
80
|
+
await atomsCopyAtoms(atomsDir);
|
|
81
|
+
await atomsCopyAtoms(atomsInjectDir);
|
|
82
|
+
await atomsTimestamp();
|
|
83
|
+
}
|
|
84
|
+
|
|
43
85
|
function getBazelEnv() {
|
|
44
86
|
// Selenium atoms build does not require Android SDK. If these env vars are set locally,
|
|
45
87
|
// Bazel may try to auto-configure Android toolchains and fail on host-specific SDK issues.
|
|
@@ -66,24 +108,6 @@ async function seleniumClean () {
|
|
|
66
108
|
await fs.rimraf(SELENIUM_DIRECTORY);
|
|
67
109
|
}
|
|
68
110
|
|
|
69
|
-
/**
|
|
70
|
-
* Clone the target selenium repository and branch into the temporary directory.
|
|
71
|
-
*/
|
|
72
|
-
export async function seleniumClone () {
|
|
73
|
-
await seleniumMkdir();
|
|
74
|
-
await seleniumClean();
|
|
75
|
-
const cloneArgs = (branch) => ([
|
|
76
|
-
'clone',
|
|
77
|
-
`--branch=${branch}`,
|
|
78
|
-
'--depth=1',
|
|
79
|
-
SELENIUM_GITHUB,
|
|
80
|
-
SELENIUM_DIRECTORY,
|
|
81
|
-
]);
|
|
82
|
-
|
|
83
|
-
log.info(`Cloning branch '${SELENIUM_BRANCH}' from '${SELENIUM_GITHUB}'`);
|
|
84
|
-
await exec('git', cloneArgs(SELENIUM_BRANCH));
|
|
85
|
-
};
|
|
86
|
-
|
|
87
111
|
/**
|
|
88
112
|
* Check bazel version if current available bazel version on the host machine
|
|
89
113
|
* meets Selenium's minimum from `.bazelversion` (newer Bazel is allowed).
|
|
@@ -228,23 +252,4 @@ async function atomsTimestamp () {
|
|
|
228
252
|
log.info(`Recording Selenium revision in atoms dir`);
|
|
229
253
|
const {stdout} = await exec('git', ['log', '-n', '1', '--decorate=full'], {cwd: SELENIUM_DIRECTORY});
|
|
230
254
|
await fs.writeFile(LAST_UPDATE_FILE, Buffer.from(stdout.trimEnd() + '\n'));
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export async function importAtoms(shouldClean) {
|
|
234
|
-
await checkBazel();
|
|
235
|
-
await atomsCleanDir();
|
|
236
|
-
if (shouldClean) {
|
|
237
|
-
await atomsClean();
|
|
238
|
-
}
|
|
239
|
-
await atomsMkdir();
|
|
240
|
-
await atomsBuild();
|
|
241
|
-
const bazelOutDir = await getBazelOutDir();
|
|
242
|
-
const atomsDir = path.resolve(bazelOutDir, BAZEL_WD_ATOMS_DIR);
|
|
243
|
-
const atomsInjectDir = path.resolve(bazelOutDir, BAZEL_WD_ATOMS_INJECT_DIR);
|
|
244
|
-
const fragmentsDir = path.resolve(bazelOutDir, BAZEL_FRAGMENTS_DIR);
|
|
245
|
-
await atomsCopyAtoms(fragmentsDir);
|
|
246
|
-
// copy fragments first and atoms later so atoms overwrite fragments
|
|
247
|
-
await atomsCopyAtoms(atomsDir);
|
|
248
|
-
await atomsCopyAtoms(atomsInjectDir);
|
|
249
|
-
await atomsTimestamp();
|
|
250
255
|
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import { SubProcess } from 'teen_process';
|
|
3
3
|
import { plist, util } from '@appium/support';
|
|
4
|
-
import B from 'bluebird';
|
|
5
4
|
import { getSimulator } from 'appium-ios-simulator';
|
|
6
|
-
import _ from 'lodash';
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
async function getSocket (udid) {
|
|
@@ -83,8 +81,8 @@ async function startSoCat (socket) {
|
|
|
83
81
|
}
|
|
84
82
|
});
|
|
85
83
|
|
|
86
|
-
const prom = new
|
|
87
|
-
proc.on('exit',
|
|
84
|
+
const prom = new Promise((resolve) => {
|
|
85
|
+
proc.on('exit', () => {
|
|
88
86
|
resolve('done');
|
|
89
87
|
});
|
|
90
88
|
});
|
|
@@ -95,7 +93,7 @@ async function startSoCat (socket) {
|
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
async function main () {
|
|
98
|
-
const udid =
|
|
96
|
+
const udid = process.argv.at(-1);
|
|
99
97
|
const s = await getSocket(udid);
|
|
100
98
|
console.log('Simulator web inspector socket:', s);
|
|
101
99
|
await startSoCat(s);
|