appium-remote-debugger 15.2.12 → 15.2.14
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 +16 -10
- package/build/lib/atoms.d.ts.map +1 -1
- package/build/lib/atoms.js +27 -16
- package/build/lib/atoms.js.map +1 -1
- package/build/lib/logger.d.ts +1 -2
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +2 -2
- package/build/lib/logger.js.map +1 -1
- package/build/lib/protocol/index.d.ts +13 -8
- package/build/lib/protocol/index.d.ts.map +1 -1
- package/build/lib/protocol/index.js +17 -12
- package/build/lib/protocol/index.js.map +1 -1
- package/build/lib/remote-debugger.js +2 -2
- package/build/lib/remote-debugger.js.map +1 -1
- package/build/lib/rpc/index.d.ts +2 -3
- package/build/lib/rpc/index.d.ts.map +1 -1
- package/build/lib/rpc/index.js +2 -2
- package/build/lib/rpc/index.js.map +1 -1
- package/build/lib/rpc/remote-messages.d.ts +62 -41
- package/build/lib/rpc/remote-messages.d.ts.map +1 -1
- package/build/lib/rpc/remote-messages.js +53 -38
- package/build/lib/rpc/remote-messages.js.map +1 -1
- package/build/lib/rpc/rpc-client-real-device.js +2 -5
- package/build/lib/rpc/rpc-client-real-device.js.map +1 -1
- package/build/lib/rpc/rpc-client-simulator.js +10 -10
- package/build/lib/rpc/rpc-client-simulator.js.map +1 -1
- package/build/lib/rpc/rpc-client.js +50 -50
- package/build/lib/rpc/rpc-client.js.map +1 -1
- package/build/lib/rpc/rpc-message-handler.d.ts +32 -39
- package/build/lib/rpc/rpc-message-handler.d.ts.map +1 -1
- package/build/lib/rpc/rpc-message-handler.js +45 -59
- package/build/lib/rpc/rpc-message-handler.js.map +1 -1
- package/build/lib/types.d.ts +3 -0
- package/build/lib/types.d.ts.map +1 -1
- package/build/lib/utils.d.ts +54 -34
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +58 -39
- package/build/lib/utils.js.map +1 -1
- package/build/test/functional/safari-e2e-specs.js +5 -1
- package/build/test/functional/safari-e2e-specs.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/atoms.ts +98 -0
- package/lib/logger.ts +3 -0
- package/lib/protocol/{index.js → index.ts} +29 -17
- package/lib/remote-debugger.ts +1 -1
- package/lib/rpc/index.ts +2 -0
- package/lib/rpc/{remote-messages.js → remote-messages.ts} +70 -56
- package/lib/rpc/rpc-client-real-device.js +1 -1
- package/lib/rpc/rpc-client-simulator.js +1 -1
- package/lib/rpc/rpc-client.js +1 -1
- package/lib/rpc/{rpc-message-handler.js → rpc-message-handler.ts} +74 -65
- package/lib/types.ts +4 -0
- package/lib/{utils.js → utils.ts} +72 -51
- package/package.json +1 -1
- package/lib/atoms.js +0 -84
- package/lib/logger.js +0 -6
- package/lib/rpc/index.js +0 -4
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { log } from '../logger';
|
|
2
3
|
import _ from 'lodash';
|
|
3
4
|
import { util } from '@appium/support';
|
|
4
|
-
import
|
|
5
|
+
import type { StringRecord } from '@appium/types';
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Represents a data message from the Web Inspector.
|
|
9
|
+
*/
|
|
10
|
+
interface DataMessage {
|
|
11
|
+
id?: string;
|
|
12
|
+
method: string;
|
|
13
|
+
params: StringRecord;
|
|
14
|
+
result: any;
|
|
15
|
+
error?: string | DataErrorMessage;
|
|
16
|
+
}
|
|
6
17
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Represents an error message structure in a data message.
|
|
20
|
+
*/
|
|
21
|
+
interface DataErrorMessage {
|
|
22
|
+
message: string;
|
|
23
|
+
code: number;
|
|
24
|
+
data: any;
|
|
25
|
+
}
|
|
11
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Handles messages from the Web Inspector and dispatches them as events.
|
|
29
|
+
* Extends EventEmitter to provide event-based message handling.
|
|
30
|
+
*/
|
|
31
|
+
export default class RpcMessageHandler extends EventEmitter {
|
|
12
32
|
/**
|
|
13
|
-
*
|
|
33
|
+
* Handles a message from the Web Inspector by parsing the selector
|
|
34
|
+
* and emitting appropriate events.
|
|
14
35
|
*
|
|
15
|
-
* @param
|
|
16
|
-
*
|
|
36
|
+
* @param plist - The plist message from the Web Inspector containing
|
|
37
|
+
* __selector and __argument properties.
|
|
17
38
|
*/
|
|
18
|
-
async handleMessage
|
|
39
|
+
async handleMessage(plist: StringRecord): Promise<void> {
|
|
19
40
|
const selector = plist.__selector;
|
|
20
41
|
if (!selector) {
|
|
21
42
|
log.debug('Got an invalid plist');
|
|
@@ -70,35 +91,41 @@ export default class RpcMessageHandler extends EventEmitters {
|
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
/**
|
|
73
|
-
*
|
|
94
|
+
* Parses the data key from a plist message.
|
|
95
|
+
* The data key is a JSON string that needs to be parsed.
|
|
74
96
|
*
|
|
75
|
-
* @param
|
|
76
|
-
* @returns
|
|
77
|
-
* @throws
|
|
97
|
+
* @param plist - The plist message containing the data key.
|
|
98
|
+
* @returns The parsed DataMessage object.
|
|
99
|
+
* @throws Error if the data key cannot be parsed.
|
|
78
100
|
*/
|
|
79
|
-
parseDataKey
|
|
101
|
+
private parseDataKey(plist: StringRecord): DataMessage {
|
|
80
102
|
try {
|
|
81
103
|
return JSON.parse(plist.__argument.WIRMessageDataKey.toString('utf8'));
|
|
82
|
-
} catch (err) {
|
|
104
|
+
} catch (err: any) {
|
|
83
105
|
log.error(`Unparseable message data: ${_.truncate(JSON.stringify(plist), {length: 100})}`);
|
|
84
106
|
throw new Error(`Unable to parse message data: ${err.message}`);
|
|
85
107
|
}
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
/**
|
|
89
|
-
*
|
|
111
|
+
* Dispatches a data message by emitting events.
|
|
112
|
+
* If msgId is provided, emits a message-specific event.
|
|
113
|
+
* Otherwise, emits method-based events with appropriate argument mapping.
|
|
90
114
|
*
|
|
91
|
-
* @param
|
|
92
|
-
* - <
|
|
93
|
-
*
|
|
94
|
-
* -
|
|
95
|
-
* @param
|
|
96
|
-
* @param
|
|
97
|
-
* @param {any} result
|
|
98
|
-
* @param {Error | undefined} error
|
|
99
|
-
* @returns {Promise<void>}
|
|
115
|
+
* @param msgId - If not empty, emits an event with this ID: <msgId, error, result>.
|
|
116
|
+
* If empty, emits method-based events: <name, error, ...args>.
|
|
117
|
+
* @param method - The method name from the data message.
|
|
118
|
+
* @param params - The parameters from the data message.
|
|
119
|
+
* @param result - The result from the data message.
|
|
120
|
+
* @param error - Any error that occurred during message processing.
|
|
100
121
|
*/
|
|
101
|
-
async dispatchDataMessage
|
|
122
|
+
private async dispatchDataMessage(
|
|
123
|
+
msgId: string,
|
|
124
|
+
method: string | undefined,
|
|
125
|
+
params: StringRecord | undefined,
|
|
126
|
+
result: any,
|
|
127
|
+
error: Error | undefined
|
|
128
|
+
): Promise<void> {
|
|
102
129
|
if (msgId) {
|
|
103
130
|
if (this.listenerCount(msgId)) {
|
|
104
131
|
if (_.has(result?.result, 'value')) {
|
|
@@ -114,22 +141,20 @@ export default class RpcMessageHandler extends EventEmitters {
|
|
|
114
141
|
return;
|
|
115
142
|
}
|
|
116
143
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
/** @type {any[]} */
|
|
120
|
-
let args = [params];
|
|
144
|
+
const eventNames: string[] = method ? [method] : [];
|
|
145
|
+
let args: any[] = [params];
|
|
121
146
|
|
|
122
147
|
// some events have different names, or the arguments are mapped from the
|
|
123
148
|
// parameters received
|
|
124
149
|
switch (method) {
|
|
125
150
|
case 'Page.frameStoppedLoading':
|
|
126
151
|
eventNames.push('Page.frameNavigated');
|
|
127
|
-
|
|
152
|
+
// eslint-disable-next-line no-fallthrough
|
|
153
|
+
case 'Page.frameNavigated':
|
|
128
154
|
args = [`'${method}' event`];
|
|
129
155
|
break;
|
|
130
156
|
case 'Timeline.eventRecorded':
|
|
131
|
-
|
|
132
|
-
args = [params || params.record];
|
|
157
|
+
args = [params || (params as any)?.record];
|
|
133
158
|
break;
|
|
134
159
|
case 'Console.messageAdded':
|
|
135
160
|
args = [params?.message];
|
|
@@ -142,13 +167,13 @@ export default class RpcMessageHandler extends EventEmitters {
|
|
|
142
167
|
break;
|
|
143
168
|
}
|
|
144
169
|
|
|
145
|
-
if (_.startsWith(method, 'Network.')) {
|
|
170
|
+
if (method && _.startsWith(method, 'Network.')) {
|
|
146
171
|
// aggregate Network events, and add original method name to the arguments
|
|
147
172
|
eventNames.push('NetworkEvent');
|
|
148
173
|
args.push(method);
|
|
149
174
|
}
|
|
150
|
-
if (_.startsWith(method, 'Console.')) {
|
|
151
|
-
// aggregate
|
|
175
|
+
if (method && _.startsWith(method, 'Console.')) {
|
|
176
|
+
// aggregate Console events, and add original method name to the arguments
|
|
152
177
|
eventNames.push('ConsoleEvent');
|
|
153
178
|
args.push(method);
|
|
154
179
|
}
|
|
@@ -159,12 +184,12 @@ export default class RpcMessageHandler extends EventEmitters {
|
|
|
159
184
|
}
|
|
160
185
|
|
|
161
186
|
/**
|
|
162
|
-
*
|
|
187
|
+
* Handles a data message from the Web Inspector by parsing it and
|
|
188
|
+
* dispatching appropriate events based on the message type.
|
|
163
189
|
*
|
|
164
|
-
* @param
|
|
165
|
-
* @returns {Promise<void>}
|
|
190
|
+
* @param plist - The plist message from the Web Inspector.
|
|
166
191
|
*/
|
|
167
|
-
async handleDataMessage
|
|
192
|
+
private async handleDataMessage(plist: StringRecord): Promise<void> {
|
|
168
193
|
const dataKey = this.parseDataKey(plist);
|
|
169
194
|
let msgId = (dataKey.id || '').toString();
|
|
170
195
|
let result = dataKey.result;
|
|
@@ -172,7 +197,7 @@ export default class RpcMessageHandler extends EventEmitters {
|
|
|
172
197
|
let method = dataKey.method;
|
|
173
198
|
let params = dataKey.params;
|
|
174
199
|
|
|
175
|
-
const parseError = () => {
|
|
200
|
+
const parseError = (): Error | undefined => {
|
|
176
201
|
const defaultMessage = 'Error occurred in handling data message';
|
|
177
202
|
if (result?.wasThrown) {
|
|
178
203
|
const message = (result?.result?.value || result?.result?.description)
|
|
@@ -182,10 +207,10 @@ export default class RpcMessageHandler extends EventEmitters {
|
|
|
182
207
|
}
|
|
183
208
|
if (dataKey.error) {
|
|
184
209
|
if (_.isPlainObject(dataKey.error)) {
|
|
185
|
-
const dataKeyError =
|
|
186
|
-
|
|
210
|
+
const dataKeyError = dataKey.error as DataErrorMessage;
|
|
211
|
+
const error = new Error(defaultMessage);
|
|
187
212
|
for (const key of Object.keys(dataKeyError)) {
|
|
188
|
-
error[key] = dataKeyError[key];
|
|
213
|
+
(error as any)[key] = dataKeyError[key as keyof DataErrorMessage];
|
|
189
214
|
}
|
|
190
215
|
return error;
|
|
191
216
|
}
|
|
@@ -213,9 +238,9 @@ export default class RpcMessageHandler extends EventEmitters {
|
|
|
213
238
|
method = message.method;
|
|
214
239
|
result = message.result || message;
|
|
215
240
|
params = result.params;
|
|
216
|
-
} catch (err) {
|
|
241
|
+
} catch (err: any) {
|
|
217
242
|
// if this happens then some aspect of the protocol is missing to us
|
|
218
|
-
// so print the entire message to get
|
|
243
|
+
// so print the entire message to get visibility into what is going on
|
|
219
244
|
log.error(`Unexpected message format from Web Inspector: ${util.jsonStringify(plist, null)}`);
|
|
220
245
|
throw err;
|
|
221
246
|
}
|
|
@@ -227,22 +252,6 @@ export default class RpcMessageHandler extends EventEmitters {
|
|
|
227
252
|
default: {
|
|
228
253
|
await this.dispatchDataMessage(msgId, method, params, result, parseError());
|
|
229
254
|
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
232
257
|
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* @typedef {Object} DataMessage
|
|
236
|
-
* @property {string} [id]
|
|
237
|
-
* @property {string} method
|
|
238
|
-
* @property {import('@appium/types').StringRecord} params
|
|
239
|
-
* @property {any} result
|
|
240
|
-
* @property {string | DataErrorMessage} [error]
|
|
241
|
-
*/
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* @typedef {Object} DataErrorMessage
|
|
245
|
-
* @property {string} message
|
|
246
|
-
* @property {number} code
|
|
247
|
-
* @property {any} data
|
|
248
|
-
*/
|
package/lib/types.ts
CHANGED
|
@@ -83,6 +83,10 @@ export type AppIdKey = string | number;
|
|
|
83
83
|
export type PageIdKey = string | number;
|
|
84
84
|
export type TargetId = string;
|
|
85
85
|
|
|
86
|
+
export interface RemoteCommandId {
|
|
87
|
+
id: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
86
90
|
export interface RemoteCommandOpts {
|
|
87
91
|
appIdKey?: AppIdKey;
|
|
88
92
|
pageIdKey?: PageIdKey;
|
|
@@ -3,6 +3,8 @@ import { errorFromMJSONWPStatusCode } from '@appium/base-driver';
|
|
|
3
3
|
import { util, node } from '@appium/support';
|
|
4
4
|
import nodeFs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import type { StringRecord } from '@appium/types';
|
|
7
|
+
import type { AppInfo, AppDict, Page } from './types';
|
|
6
8
|
|
|
7
9
|
const MODULE_NAME = 'appium-remote-debugger';
|
|
8
10
|
export const WEB_CONTENT_BUNDLE_ID = 'com.apple.WebKit.WebContent';
|
|
@@ -16,13 +18,13 @@ const ACCEPTED_PAGE_TYPES = [
|
|
|
16
18
|
export const RESPONSE_LOG_LENGTH = 100;
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
|
-
* Takes a dictionary from the remote debugger and
|
|
20
|
-
*
|
|
21
|
+
* Takes a dictionary from the remote debugger and converts it into a more
|
|
22
|
+
* manageable AppInfo object with understandable keys.
|
|
21
23
|
*
|
|
22
|
-
* @param
|
|
23
|
-
* @returns
|
|
24
|
+
* @param dict - Dictionary from the remote debugger containing application information.
|
|
25
|
+
* @returns A tuple containing the application ID and the AppInfo object.
|
|
24
26
|
*/
|
|
25
|
-
export function appInfoFromDict
|
|
27
|
+
export function appInfoFromDict(dict: Record<string, any>): [string, AppInfo] {
|
|
26
28
|
const id = dict.WIRApplicationIdentifierKey;
|
|
27
29
|
const isProxy = _.isString(dict.WIRIsApplicationProxyKey)
|
|
28
30
|
? dict.WIRIsApplicationProxyKey.toLowerCase() === 'true'
|
|
@@ -30,8 +32,7 @@ export function appInfoFromDict (dict) {
|
|
|
30
32
|
// automation enabled can be either from the keys
|
|
31
33
|
// - WIRRemoteAutomationEnabledKey (boolean)
|
|
32
34
|
// - WIRAutomationAvailabilityKey (string or boolean)
|
|
33
|
-
|
|
34
|
-
let isAutomationEnabled = !!dict.WIRRemoteAutomationEnabledKey;
|
|
35
|
+
let isAutomationEnabled: boolean | string = !!dict.WIRRemoteAutomationEnabledKey;
|
|
35
36
|
if (_.has(dict, 'WIRAutomationAvailabilityKey')) {
|
|
36
37
|
if (_.isString(dict.WIRAutomationAvailabilityKey)) {
|
|
37
38
|
isAutomationEnabled = dict.WIRAutomationAvailabilityKey === 'WIRAutomationAvailabilityUnknown'
|
|
@@ -41,8 +42,7 @@ export function appInfoFromDict (dict) {
|
|
|
41
42
|
isAutomationEnabled = !!dict.WIRAutomationAvailabilityKey;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
-
const entry = {
|
|
45
|
+
const entry: AppInfo = {
|
|
46
46
|
id,
|
|
47
47
|
isProxy,
|
|
48
48
|
name: dict.WIRApplicationNameKey,
|
|
@@ -56,13 +56,13 @@ export function appInfoFromDict (dict) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
59
|
+
* Takes a dictionary from the remote debugger and converts it into an array
|
|
60
|
+
* of Page objects with understandable keys. Filters out non-web pages.
|
|
61
61
|
*
|
|
62
|
-
* @param
|
|
63
|
-
* @returns
|
|
62
|
+
* @param pageDict - Dictionary from the remote debugger containing page information.
|
|
63
|
+
* @returns An array of Page objects representing the available pages.
|
|
64
64
|
*/
|
|
65
|
-
export function pageArrayFromDict
|
|
65
|
+
export function pageArrayFromDict(pageDict: StringRecord): Page[] {
|
|
66
66
|
return _.values(pageDict)
|
|
67
67
|
// count only WIRTypeWeb pages and ignore all others (WIRTypeJavaScript etc)
|
|
68
68
|
.filter((dict) => _.isUndefined(dict.WIRTypeKey) || ACCEPTED_PAGE_TYPES.includes(dict.WIRTypeKey))
|
|
@@ -75,14 +75,16 @@ export function pageArrayFromDict (pageDict) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
+
* Finds all application identifier keys that match the given bundle ID.
|
|
79
|
+
* If no matches are found and the bundle ID is not WEB_CONTENT_BUNDLE_ID,
|
|
80
|
+
* falls back to searching for WEB_CONTENT_BUNDLE_ID.
|
|
78
81
|
*
|
|
79
|
-
* @param
|
|
80
|
-
* @param
|
|
81
|
-
* @returns
|
|
82
|
+
* @param bundleId - The bundle identifier to search for.
|
|
83
|
+
* @param appDict - The application dictionary to search in.
|
|
84
|
+
* @returns An array of unique application identifier keys matching the bundle ID.
|
|
82
85
|
*/
|
|
83
|
-
export function appIdsForBundle
|
|
84
|
-
|
|
85
|
-
const appIds = _.toPairs(appDict)
|
|
86
|
+
export function appIdsForBundle(bundleId: string, appDict: AppDict): string[] {
|
|
87
|
+
const appIds: string[] = _.toPairs(appDict)
|
|
86
88
|
.filter(([, data]) => data.bundleId === bundleId)
|
|
87
89
|
.map(([key]) => key);
|
|
88
90
|
|
|
@@ -95,11 +97,15 @@ export function appIdsForBundle (bundleId, appDict) {
|
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
100
|
+
* Validates that all parameters in the provided object have non-nil values.
|
|
101
|
+
* Throws an error if any parameters are missing (null or undefined).
|
|
102
|
+
*
|
|
103
|
+
* @template T - The type of the parameters object.
|
|
104
|
+
* @param params - An object containing parameters to validate.
|
|
105
|
+
* @returns The same parameters object if all values are valid.
|
|
106
|
+
* @throws Error if any parameters are missing, listing all missing parameter names.
|
|
101
107
|
*/
|
|
102
|
-
export function checkParams (params) {
|
|
108
|
+
export function checkParams<T extends StringRecord>(params: T): T {
|
|
103
109
|
// check if all parameters have a value
|
|
104
110
|
const errors = _.toPairs(params)
|
|
105
111
|
.filter(([, value]) => _.isNil(value))
|
|
@@ -111,30 +117,33 @@ export function checkParams (params) {
|
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
120
|
+
* Converts a value to a JSON string, removing noisy function properties
|
|
121
|
+
* that can muddy the logs.
|
|
122
|
+
*
|
|
123
|
+
* @param value - The value to stringify.
|
|
124
|
+
* @param multiline - If true, formats the JSON with indentation. Defaults to false.
|
|
125
|
+
* @returns A JSON string representation of the value with noisy properties removed.
|
|
117
126
|
*/
|
|
118
|
-
export function simpleStringify
|
|
127
|
+
export function simpleStringify(value: any, multiline: boolean = false): string {
|
|
119
128
|
if (!value) {
|
|
120
129
|
return JSON.stringify(value);
|
|
121
130
|
}
|
|
122
131
|
|
|
123
|
-
|
|
124
|
-
// which muddy the logs
|
|
125
|
-
let cleanValue = _.clone(value);
|
|
126
|
-
for (const property of ['ceil', 'clone', 'floor', 'round', 'scale', 'toString']) {
|
|
127
|
-
delete cleanValue[property];
|
|
128
|
-
}
|
|
132
|
+
const cleanValue = removeNoisyProperties(_.clone(value));
|
|
129
133
|
return multiline ? JSON.stringify(cleanValue, null, 2) : JSON.stringify(cleanValue);
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
/**
|
|
137
|
+
* Converts the result from a JavaScript evaluation in the remote debugger
|
|
138
|
+
* into a usable format. Handles errors, serialization, and cleans up noisy
|
|
139
|
+
* function properties.
|
|
133
140
|
*
|
|
134
|
-
* @param
|
|
135
|
-
* @returns
|
|
141
|
+
* @param res - The raw result from the remote debugger's JavaScript evaluation.
|
|
142
|
+
* @returns The cleaned and converted result value.
|
|
143
|
+
* @throws Error if the result is undefined, has an unexpected type, or contains
|
|
144
|
+
* an error status code.
|
|
136
145
|
*/
|
|
137
|
-
export function convertJavascriptEvaluationResult
|
|
146
|
+
export function convertJavascriptEvaluationResult(res: any): any {
|
|
138
147
|
if (_.isUndefined(res)) {
|
|
139
148
|
throw new Error(`Did not get OK result from remote debugger. Result was: ${_.truncate(simpleStringify(res), {length: RESPONSE_LOG_LENGTH})}`);
|
|
140
149
|
} else if (_.isString(res)) {
|
|
@@ -156,23 +165,17 @@ export function convertJavascriptEvaluationResult (res) {
|
|
|
156
165
|
// with either have an object with a `value` property (even if `null`),
|
|
157
166
|
// or a plain object
|
|
158
167
|
const value = _.has(res, 'value') ? res.value : res;
|
|
159
|
-
|
|
160
|
-
// get rid of noisy functions on objects
|
|
161
|
-
if (_.isObject(value)) {
|
|
162
|
-
for (const property of ['ceil', 'clone', 'floor', 'round', 'scale', 'toString']) {
|
|
163
|
-
delete value[property];
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return value;
|
|
168
|
+
return removeNoisyProperties(value);
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
/**
|
|
170
|
-
* Calculates the path to the current module's root folder
|
|
172
|
+
* Calculates the path to the current module's root folder.
|
|
173
|
+
* The result is memoized for performance.
|
|
171
174
|
*
|
|
172
|
-
* @returns
|
|
173
|
-
* @throws
|
|
175
|
+
* @returns The full path to the module root directory.
|
|
176
|
+
* @throws Error if the module root folder cannot be determined.
|
|
174
177
|
*/
|
|
175
|
-
export const getModuleRoot = _.memoize(function getModuleRoot
|
|
178
|
+
export const getModuleRoot = _.memoize(function getModuleRoot(): string {
|
|
176
179
|
const root = node.getModuleRootSync(MODULE_NAME, __filename);
|
|
177
180
|
if (!root) {
|
|
178
181
|
throw new Error(`Cannot find the root folder of the ${MODULE_NAME} Node.js module`);
|
|
@@ -181,9 +184,27 @@ export const getModuleRoot = _.memoize(function getModuleRoot () {
|
|
|
181
184
|
});
|
|
182
185
|
|
|
183
186
|
/**
|
|
184
|
-
*
|
|
187
|
+
* Reads and parses the package.json file from the module root.
|
|
188
|
+
*
|
|
189
|
+
* @returns The parsed package.json contents as a StringRecord.
|
|
185
190
|
*/
|
|
186
|
-
export function getModuleProperties() {
|
|
191
|
+
export function getModuleProperties(): StringRecord {
|
|
187
192
|
const fullPath = path.resolve(getModuleRoot(), 'package.json');
|
|
188
193
|
return JSON.parse(nodeFs.readFileSync(fullPath, 'utf8'));
|
|
189
194
|
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Removes noisy function properties from an object that can muddy the logs.
|
|
198
|
+
* These properties are often added by JavaScript number objects and similar.
|
|
199
|
+
*
|
|
200
|
+
* @param obj - The object to clean.
|
|
201
|
+
* @returns The cleaned object.
|
|
202
|
+
*/
|
|
203
|
+
function removeNoisyProperties<T>(obj: T): T {
|
|
204
|
+
if (_.isObject(obj)) {
|
|
205
|
+
for (const property of ['ceil', 'clone', 'floor', 'round', 'scale', 'toString']) {
|
|
206
|
+
delete obj[property];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return obj;
|
|
210
|
+
}
|
package/package.json
CHANGED
package/lib/atoms.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { fs } from '@appium/support';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import _ from 'lodash';
|
|
4
|
-
import log from './logger';
|
|
5
|
-
import { getModuleRoot } from './utils';
|
|
6
|
-
|
|
7
|
-
const ATOMS_CACHE = {};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {any} obj
|
|
11
|
-
* @returns {string}
|
|
12
|
-
*/
|
|
13
|
-
function atomsStringify(obj) {
|
|
14
|
-
if (typeof obj === 'undefined') {
|
|
15
|
-
return 'undefined';
|
|
16
|
-
}
|
|
17
|
-
return JSON.stringify(obj);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
22
|
-
* @param {string} atomName
|
|
23
|
-
* @returns {Promise<Buffer>}
|
|
24
|
-
*/
|
|
25
|
-
async function getAtom (atomName) {
|
|
26
|
-
// check if we have already loaded and cached this atom
|
|
27
|
-
if (!_.has(ATOMS_CACHE, atomName)) {
|
|
28
|
-
const atomFileName = path.resolve(getModuleRoot(), 'atoms', `${atomName}.js`);
|
|
29
|
-
try {
|
|
30
|
-
ATOMS_CACHE[atomName] = await fs.readFile(atomFileName);
|
|
31
|
-
} catch {
|
|
32
|
-
throw new Error(`Unable to load Atom '${atomName}' from file '${atomFileName}'`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return ATOMS_CACHE[atomName];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @param {string} script
|
|
41
|
-
* @param {string} frame
|
|
42
|
-
* @returns {Promise<string>}
|
|
43
|
-
*/
|
|
44
|
-
async function wrapScriptForFrame (script, frame) {
|
|
45
|
-
log.debug(`Wrapping script for frame '${frame}'`);
|
|
46
|
-
const elFromCache = await getAtom('get_element_from_cache');
|
|
47
|
-
return `(function (window) { var document = window.document; ` +
|
|
48
|
-
`return (${script}); })((${elFromCache.toString('utf8')})(${atomsStringify(frame)}))`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
*
|
|
53
|
-
* @param {string} atom
|
|
54
|
-
* @param {any[]} [args]
|
|
55
|
-
* @param {string[]} [frames]
|
|
56
|
-
* @param {string?} [asyncCallBack]
|
|
57
|
-
* @returns {Promise<string>}
|
|
58
|
-
*/
|
|
59
|
-
async function getScriptForAtom (atom, args = [], frames = [], asyncCallBack = null) {
|
|
60
|
-
const atomSrc = (await getAtom(atom)).toString('utf8');
|
|
61
|
-
let script;
|
|
62
|
-
if (frames.length > 0) {
|
|
63
|
-
script = atomSrc;
|
|
64
|
-
for (const frame of frames) {
|
|
65
|
-
script = await wrapScriptForFrame(script, frame);
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
log.debug(`Executing '${atom}' atom in default context`);
|
|
69
|
-
script = `(${atomSrc})`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// add the arguments, as strings
|
|
73
|
-
args = args.map(atomsStringify);
|
|
74
|
-
if (asyncCallBack) {
|
|
75
|
-
script += `(${args.join(',')}, ${asyncCallBack}, true)`;
|
|
76
|
-
} else {
|
|
77
|
-
script += `(${args.join(',')})`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return script;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export { getAtom, getScriptForAtom };
|
|
84
|
-
export default getAtom;
|
package/lib/logger.js
DELETED
package/lib/rpc/index.js
DELETED