devicely 2.2.13 → 2.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/bin/devicely.js +1 -1
- package/lib/advanced-logger.js +1 -1
- package/lib/androidDeviceDetection.js +1 -1
- package/lib/appMappings.js +1 -1
- package/lib/commanderService.js +1 -1
- package/lib/deviceDetection.js +1 -1
- package/lib/devices.js +1 -1
- package/lib/doctor.js +1 -1
- package/lib/encryption.js +1 -1
- package/lib/executor.js +1 -1
- package/lib/hybridAI.js +1 -1
- package/lib/intelligentLocatorService.js +1 -1
- package/lib/lightweightAI.js +1 -1
- package/lib/localBuiltInAI.js +1 -1
- package/lib/localBuiltInAI_backup.js +1 -1
- package/lib/localBuiltInAI_simple.js +1 -1
- package/lib/locatorStrategy.js +1 -1
- package/lib/logger-demo.js +1 -1
- package/lib/logger.js +1 -1
- package/lib/quick-start-logger.js +1 -1
- package/lib/scriptLoader.js +1 -1
- package/lib/server.js +1 -1
- package/lib/tensorflowAI.js +1 -1
- package/lib/tinyAI.js +1 -1
- package/lib/universalSessionManager.js +1 -1
- package/package.json +1 -1
- package/lib/.logging-backup/aiProviders.js.backup +0 -654
- package/lib/.logging-backup/appMappings.js.backup +0 -337
- package/lib/.logging-backup/commanderService.js.backup +0 -4427
- package/lib/.logging-backup/devices.js.backup +0 -54
- package/lib/.logging-backup/doctor.js.backup +0 -94
- package/lib/.logging-backup/encryption.js.backup +0 -61
- package/lib/.logging-backup/executor.js.backup +0 -104
- package/lib/.logging-backup/hybridAI.js.backup +0 -154
- package/lib/.logging-backup/intelligentLocatorService.js.backup +0 -1541
- package/lib/.logging-backup/locatorStrategy.js.backup +0 -342
- package/lib/.logging-backup/scriptLoader.js.backup +0 -13
- package/lib/.logging-backup/server.js.backup +0 -6298
- package/lib/.logging-backup/tensorflowAI.js.backup +0 -714
- package/lib/.logging-backup/universalSessionManager.js.backup +0 -370
- package/lib/.logging-enhanced-backup/server.js.enhanced-backup +0 -6298
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
const Logger = require('./logger');
|
|
2
|
-
const { execSync } = require('child_process');
|
|
3
|
-
|
|
4
|
-
class LocatorStrategy {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.logger = new Logger('LocatorStrategy');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Capture element information at given coordinates
|
|
11
|
-
* Returns all possible locators for the element
|
|
12
|
-
*/
|
|
13
|
-
async captureElementLocators(device, x, y) {
|
|
14
|
-
const locators = {
|
|
15
|
-
coordinates: { x, y },
|
|
16
|
-
timestamp: Date.now()
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
if (device.platform === 'Android') {
|
|
21
|
-
return await this.captureAndroidElement(device, x, y);
|
|
22
|
-
} else if (device.platform === 'iOS') {
|
|
23
|
-
return await this.captureIOSElement(device, x, y);
|
|
24
|
-
}
|
|
25
|
-
} catch (error) {
|
|
26
|
-
this.logger.error(`Failed to capture element locators: ${error.message}`);
|
|
27
|
-
return locators;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return locators;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Capture Android element using UIAutomator2
|
|
35
|
-
*/
|
|
36
|
-
async captureAndroidElement(device, x, y) {
|
|
37
|
-
try {
|
|
38
|
-
// Use UIAutomator2 to get element at coordinates
|
|
39
|
-
// This requires active UIAutomator2 session
|
|
40
|
-
const dumpCommand = `adb -s ${device.udid || device.name} shell uiautomator dump /sdcard/window_dump.xml && adb -s ${device.udid || device.name} shell cat /sdcard/window_dump.xml`;
|
|
41
|
-
|
|
42
|
-
const xmlDump = execSync(dumpCommand, {
|
|
43
|
-
encoding: 'utf8',
|
|
44
|
-
timeout: 10000
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const element = this.findElementAtCoordinates(xmlDump, x, y);
|
|
48
|
-
|
|
49
|
-
if (element) {
|
|
50
|
-
return {
|
|
51
|
-
xpath: this.buildXPath(element),
|
|
52
|
-
resourceId: element.resourceId || null,
|
|
53
|
-
contentDesc: element.contentDesc || null,
|
|
54
|
-
text: element.text || null,
|
|
55
|
-
className: element.className || null,
|
|
56
|
-
index: element.index || 0,
|
|
57
|
-
bounds: element.bounds || null,
|
|
58
|
-
coordinates: { x, y },
|
|
59
|
-
platform: 'Android'
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
} catch (error) {
|
|
63
|
-
this.logger.warn(`Android element capture failed: ${error.message}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return { coordinates: { x, y }, platform: 'Android' };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Capture iOS element using WDA
|
|
71
|
-
*/
|
|
72
|
-
async captureIOSElement(device, x, y) {
|
|
73
|
-
try {
|
|
74
|
-
// Use WDA to get element at coordinates
|
|
75
|
-
// Requires active WDA session
|
|
76
|
-
const sessionPort = device.wdaPort || 8100;
|
|
77
|
-
const axios = require('axios');
|
|
78
|
-
|
|
79
|
-
const response = await axios.get(`http://localhost:${sessionPort}/source`, {
|
|
80
|
-
params: { format: 'json' },
|
|
81
|
-
timeout: 5000
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const pageSource = response.data;
|
|
85
|
-
const element = this.findIOSElementAtCoordinates(pageSource, x, y);
|
|
86
|
-
|
|
87
|
-
if (element) {
|
|
88
|
-
return {
|
|
89
|
-
xpath: this.buildIOSXPath(element),
|
|
90
|
-
accessibilityId: element.name || element.label || null,
|
|
91
|
-
accessibilityLabel: element.label || null,
|
|
92
|
-
text: element.value || element.label || null,
|
|
93
|
-
type: element.type || null,
|
|
94
|
-
index: element.index || 0,
|
|
95
|
-
bounds: element.rect || null,
|
|
96
|
-
coordinates: { x, y },
|
|
97
|
-
platform: 'iOS'
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
} catch (error) {
|
|
101
|
-
this.logger.warn(`iOS element capture failed: ${error.message}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return { coordinates: { x, y }, platform: 'iOS' };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Find element at coordinates from XML dump
|
|
109
|
-
*/
|
|
110
|
-
findElementAtCoordinates(xmlDump, x, y) {
|
|
111
|
-
// Parse XML and find element whose bounds contain (x, y)
|
|
112
|
-
// This is a simplified implementation
|
|
113
|
-
const boundsRegex = /bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
|
|
114
|
-
const resourceIdRegex = /resource-id="([^"]*)"/;
|
|
115
|
-
const textRegex = /text="([^"]*)"/;
|
|
116
|
-
const contentDescRegex = /content-desc="([^"]*)"/;
|
|
117
|
-
const classRegex = /class="([^"]*)"/;
|
|
118
|
-
|
|
119
|
-
const lines = xmlDump.split('\n');
|
|
120
|
-
|
|
121
|
-
for (const line of lines) {
|
|
122
|
-
const boundsMatch = boundsRegex.exec(line);
|
|
123
|
-
if (boundsMatch) {
|
|
124
|
-
const [, left, top, right, bottom] = boundsMatch.map(Number);
|
|
125
|
-
|
|
126
|
-
if (x >= left && x <= right && y >= top && y <= bottom) {
|
|
127
|
-
return {
|
|
128
|
-
resourceId: (resourceIdRegex.exec(line) || [])[1] || null,
|
|
129
|
-
text: (textRegex.exec(line) || [])[1] || null,
|
|
130
|
-
contentDesc: (contentDescRegex.exec(line) || [])[1] || null,
|
|
131
|
-
className: (classRegex.exec(line) || [])[1] || null,
|
|
132
|
-
bounds: { left, top, right, bottom }
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Find iOS element at coordinates from page source
|
|
143
|
-
*/
|
|
144
|
-
findIOSElementAtCoordinates(pageSource, x, y) {
|
|
145
|
-
// Recursively search through WDA page source tree
|
|
146
|
-
const findElement = (node) => {
|
|
147
|
-
if (node.rect) {
|
|
148
|
-
const { x: ex, y: ey, width, height } = node.rect;
|
|
149
|
-
if (x >= ex && x <= (ex + width) && y >= ey && y <= (ey + height)) {
|
|
150
|
-
return node;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (node.children) {
|
|
155
|
-
for (const child of node.children) {
|
|
156
|
-
const found = findElement(child);
|
|
157
|
-
if (found) return found;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return null;
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
return findElement(pageSource);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Build XPath for Android element
|
|
169
|
-
*/
|
|
170
|
-
buildXPath(element) {
|
|
171
|
-
const parts = [];
|
|
172
|
-
|
|
173
|
-
if (element.resourceId) {
|
|
174
|
-
return `//*[@resource-id="${element.resourceId}"]`;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (element.contentDesc) {
|
|
178
|
-
return `//*[@content-desc="${element.contentDesc}"]`;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (element.text) {
|
|
182
|
-
return `//${element.className || '*'}[@text="${element.text}"]`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (element.className) {
|
|
186
|
-
return `//${element.className}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return `//*`;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Build XPath for iOS element
|
|
194
|
-
*/
|
|
195
|
-
buildIOSXPath(element) {
|
|
196
|
-
if (element.name) {
|
|
197
|
-
return `//*[@name="${element.name}"]`;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (element.label) {
|
|
201
|
-
return `//${element.type || '*'}[@label="${element.label}"]`;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (element.type) {
|
|
205
|
-
return `//${element.type}`;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return `//*`;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Execute action using specific locator strategy
|
|
213
|
-
*/
|
|
214
|
-
async executeWithLocator(device, action, locator, input = null) {
|
|
215
|
-
if (device.platform === 'Android') {
|
|
216
|
-
return await this.executeAndroidAction(device, action, locator, input);
|
|
217
|
-
} else if (device.platform === 'iOS') {
|
|
218
|
-
return await this.executeIOSAction(device, action, locator, input);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
throw new Error(`Unsupported platform: ${device.platform}`);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Execute action on Android device
|
|
226
|
-
*/
|
|
227
|
-
async executeAndroidAction(device, action, locator, input) {
|
|
228
|
-
const deviceId = device.udid || device.name;
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
switch (action) {
|
|
232
|
-
case 'tap':
|
|
233
|
-
return await this.androidTap(deviceId, locator);
|
|
234
|
-
case 'swipe':
|
|
235
|
-
return await this.androidSwipe(deviceId, locator);
|
|
236
|
-
case 'type':
|
|
237
|
-
return await this.androidType(deviceId, locator, input);
|
|
238
|
-
case 'clear':
|
|
239
|
-
return await this.androidClear(deviceId, locator);
|
|
240
|
-
default:
|
|
241
|
-
throw new Error(`Unsupported action: ${action}`);
|
|
242
|
-
}
|
|
243
|
-
} catch (error) {
|
|
244
|
-
this.logger.error(`Android action failed: ${error.message}`);
|
|
245
|
-
throw error;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Execute action on iOS device
|
|
251
|
-
*/
|
|
252
|
-
async executeIOSAction(device, action, locator, input) {
|
|
253
|
-
const sessionPort = device.wdaPort || 8100;
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
switch (action) {
|
|
257
|
-
case 'tap':
|
|
258
|
-
return await this.iOSTap(sessionPort, locator);
|
|
259
|
-
case 'swipe':
|
|
260
|
-
return await this.iOSSwipe(sessionPort, locator);
|
|
261
|
-
case 'type':
|
|
262
|
-
return await this.iOSType(sessionPort, locator, input);
|
|
263
|
-
case 'clear':
|
|
264
|
-
return await this.iOSClear(sessionPort, locator);
|
|
265
|
-
default:
|
|
266
|
-
throw new Error(`Unsupported action: ${action}`);
|
|
267
|
-
}
|
|
268
|
-
} catch (error) {
|
|
269
|
-
this.logger.error(`iOS action failed: ${error.message}`);
|
|
270
|
-
throw error;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Android action implementations
|
|
275
|
-
async androidTap(deviceId, locator) {
|
|
276
|
-
if (locator.type === 'coordinates') {
|
|
277
|
-
const { x, y } = locator.value;
|
|
278
|
-
execSync(`adb -s ${deviceId} shell input tap ${x} ${y}`);
|
|
279
|
-
} else if (locator.type === 'resource_id') {
|
|
280
|
-
// Use UIAutomator2 to find and tap element
|
|
281
|
-
execSync(`adb -s ${deviceId} shell input tap $(uiautomator dump | grep "${locator.value}")`);
|
|
282
|
-
}
|
|
283
|
-
return { success: true };
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
async androidSwipe(deviceId, locator) {
|
|
287
|
-
const { x1, y1, x2, y2, duration = 300 } = locator.value;
|
|
288
|
-
execSync(`adb -s ${deviceId} shell input swipe ${x1} ${y1} ${x2} ${y2} ${duration}`);
|
|
289
|
-
return { success: true };
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
async androidType(deviceId, locator, text) {
|
|
293
|
-
// First tap on element, then input text
|
|
294
|
-
await this.androidTap(deviceId, locator);
|
|
295
|
-
const escapedText = text.replace(/\s/g, '%s');
|
|
296
|
-
execSync(`adb -s ${deviceId} shell input text "${escapedText}"`);
|
|
297
|
-
return { success: true };
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
async androidClear(deviceId, locator) {
|
|
301
|
-
// Select all and delete
|
|
302
|
-
await this.androidTap(deviceId, locator);
|
|
303
|
-
execSync(`adb -s ${deviceId} shell input keyevent KEYCODE_MOVE_END`);
|
|
304
|
-
execSync(`adb -s ${deviceId} shell input keyevent --longpress KEYCODE_DEL`);
|
|
305
|
-
return { success: true };
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// iOS action implementations (using WDA HTTP API)
|
|
309
|
-
async iOSTap(sessionPort, locator) {
|
|
310
|
-
const axios = require('axios');
|
|
311
|
-
if (locator.type === 'coordinates') {
|
|
312
|
-
const { x, y } = locator.value;
|
|
313
|
-
await axios.post(`http://localhost:${sessionPort}/wda/tap/0`, { x, y });
|
|
314
|
-
}
|
|
315
|
-
return { success: true };
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async iOSSwipe(sessionPort, locator) {
|
|
319
|
-
const axios = require('axios');
|
|
320
|
-
const { x1, y1, x2, y2 } = locator.value;
|
|
321
|
-
await axios.post(`http://localhost:${sessionPort}/wda/dragfromtoforduration`, {
|
|
322
|
-
fromX: x1, fromY: y1, toX: x2, toY: y2, duration: 0.3
|
|
323
|
-
});
|
|
324
|
-
return { success: true };
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
async iOSType(sessionPort, locator, text) {
|
|
328
|
-
const axios = require('axios');
|
|
329
|
-
await this.iOSTap(sessionPort, locator);
|
|
330
|
-
await axios.post(`http://localhost:${sessionPort}/wda/keys`, { value: [text] });
|
|
331
|
-
return { success: true };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
async iOSClear(sessionPort, locator) {
|
|
335
|
-
const axios = require('axios');
|
|
336
|
-
await this.iOSTap(sessionPort, locator);
|
|
337
|
-
await axios.post(`http://localhost:${sessionPort}/wda/keys`, { value: [''] });
|
|
338
|
-
return { success: true };
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
module.exports = LocatorStrategy;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const { decrypt } = require('./encryption');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Decrypt encrypted shell script
|
|
6
|
-
* Uses centralized encryption config for consistency
|
|
7
|
-
*/
|
|
8
|
-
function decryptScript(encryptedFilePath) {
|
|
9
|
-
const encryptedData = fs.readFileSync(encryptedFilePath, 'utf8');
|
|
10
|
-
return decrypt(encryptedData);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
module.exports = { decryptScript };
|