ff-automationv2 2.2.35 → 2.2.36-beta.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/dist/ai/llmcalls/llmAction.js +6 -5
- package/dist/ai/llmprompts/llmPromptTypes/systemPromptTypes.d.ts +3 -0
- package/dist/ai/llmprompts/promptRegistry.js +4 -0
- package/dist/ai/llmprompts/systemPrompts/actionExtractorPrompt.js +133 -133
- package/dist/ai/llmprompts/systemPrompts/arithmeticPrompt.js +40 -40
- package/dist/ai/llmprompts/systemPrompts/fileVerifyActionExtractorPrompt.js +36 -36
- package/dist/ai/llmprompts/systemPrompts/fireflinkElementIndexExtractor_Mob.js +4 -4
- package/dist/ai/llmprompts/systemPrompts/getActionExtractorPrompt.js +51 -51
- package/dist/ai/llmprompts/systemPrompts/inputContextPrompt.js +49 -49
- package/dist/ai/llmprompts/systemPrompts/mobileKeywordExtractor.js +86 -86
- package/dist/ai/llmprompts/systemPrompts/multichannelKeywordExtractor.js +47 -47
- package/dist/ai/llmprompts/systemPrompts/otpExtractorPrompt.js +16 -16
- package/dist/ai/llmprompts/systemPrompts/sliderSwipeVisionPrompt.d.ts +2 -0
- package/dist/ai/llmprompts/systemPrompts/sliderSwipeVisionPrompt.js +54 -0
- package/dist/ai/llmprompts/systemPrompts/verifyActionExtractorPromptMob.js +43 -43
- package/dist/ai/llmprompts/userPrompts/userPrompt.js +21 -0
- package/dist/automation/actions/executor.d.ts +1 -0
- package/dist/automation/actions/executor.js +23 -0
- package/dist/automation/actions/interaction/swipe/slider_Swipe.d.ts +2 -0
- package/dist/automation/actions/interaction/swipe/slider_Swipe.js +129 -0
- package/dist/automation/actions/interface/FireFlinkResponseSchema.d.ts +6 -0
- package/dist/automation/actions/interface/FireFlinkResponseSchema.js +8 -1
- package/dist/automation/actions/interface/swipeActionInterface.d.ts +15 -0
- package/dist/core/helpers/mobileSwipeHelper.d.ts +3 -0
- package/dist/core/helpers/mobileSwipeHelper.js +78 -0
- package/dist/core/helpers/visionFallbackHandler.js +5 -3
- package/dist/core/interfaces/actionInterface.d.ts +1 -0
- package/dist/core/interfaces/mobileSwipeSliderInterface.d.ts +25 -0
- package/dist/core/interfaces/mobileSwipeSliderInterface.js +2 -0
- package/dist/core/interfaces/promptInterface.d.ts +4 -0
- package/dist/core/main/actionHandlerFactory.js +2 -2
- package/dist/core/main/runAutomationScript.js +44 -1
- package/dist/core/types/promptType.d.ts +1 -0
- package/dist/core/types/promptType.js +1 -0
- package/dist/domAnalysis/searchBest.js +0 -1
- package/dist/utils/DomExtraction/jsForAttributeInjection.js +232 -232
- package/dist/utils/helpers/xpathcreation.js +18 -2
- package/package.json +91 -91
- package/dist/tests/Framework.d.ts +0 -0
- package/dist/tests/Framework.js +0 -62
- package/dist/tests/itertaion.d.ts +0 -1
- package/dist/tests/itertaion.js +0 -43
- package/dist/tests/multiBrowser.d.ts +0 -1
- package/dist/tests/multiBrowser.js +0 -34
- package/dist/tests/poc.d.ts +0 -0
- package/dist/tests/poc.js +0 -212
- package/dist/tests/senario.d.ts +0 -0
- package/dist/tests/senario.js +0 -1120
- package/dist/tests/start_iteration_suggestion.d.ts +0 -6
- package/dist/tests/start_iteration_suggestion.js +0 -46
- package/dist/tests/test.d.ts +0 -1
- package/dist/tests/test.js +0 -26
- package/dist/tests/test1.d.ts +0 -1
- package/dist/tests/test1.js +0 -30
- package/dist/tests/test12.d.ts +0 -1
- package/dist/tests/test12.js +0 -35
- package/dist/tests/test254.d.ts +0 -0
- package/dist/tests/test254.js +0 -87
- package/dist/tests/test3.d.ts +0 -1
- package/dist/tests/test3.js +0 -27
- package/dist/tests/testkaf.d.ts +0 -0
- package/dist/tests/testkaf.js +0 -52
- package/dist/tests/tests.data.d.ts +0 -1
- package/dist/tests/tests.data.js +0 -8
- package/dist/tests/testss.d.ts +0 -1
- package/dist/tests/testss.js +0 -23
- package/dist/tests/testwe.d.ts +0 -1
- package/dist/tests/testwe.js +0 -24
|
@@ -32,6 +32,7 @@ const stepActionInspector_js_1 = require("../helpers/stepActionInspector.js");
|
|
|
32
32
|
const elementSelectorResolver_js_1 = require("../helpers/elementSelectorResolver.js");
|
|
33
33
|
const runnerPostActionHelper_js_1 = require("../helpers/runnerPostActionHelper.js");
|
|
34
34
|
const visionFallbackHandler_js_1 = require("../helpers/visionFallbackHandler.js");
|
|
35
|
+
const mobileSwipeHelper_js_1 = require("../helpers/mobileSwipeHelper.js");
|
|
35
36
|
class AutomationRunner {
|
|
36
37
|
constructor() {
|
|
37
38
|
this.KafkaPayload = {};
|
|
@@ -588,6 +589,48 @@ class AutomationRunner {
|
|
|
588
589
|
stepResult = retryActionResolution.stepResult;
|
|
589
590
|
stepAction = retryActionResolution.stepAction;
|
|
590
591
|
}
|
|
592
|
+
const swipeVisionResult = await (0, mobileSwipeHelper_js_1.checkMobileSwipeSlider)({
|
|
593
|
+
driver,
|
|
594
|
+
currentPlatform,
|
|
595
|
+
stepAction,
|
|
596
|
+
step,
|
|
597
|
+
domInfo,
|
|
598
|
+
doc,
|
|
599
|
+
stepProcessor,
|
|
600
|
+
priorAndNextSteps,
|
|
601
|
+
fallbackAttempt,
|
|
602
|
+
driverContext,
|
|
603
|
+
});
|
|
604
|
+
domInfo = swipeVisionResult.domInfo;
|
|
605
|
+
doc = swipeVisionResult.doc;
|
|
606
|
+
if (swipeVisionResult.isSlider) {
|
|
607
|
+
handler = actionHandlers["MOB_SwipeSlider"];
|
|
608
|
+
await handler({
|
|
609
|
+
parentXpath: swipeVisionResult.parentXpath,
|
|
610
|
+
targetXpath: swipeVisionResult.targetXpath,
|
|
611
|
+
xpath: swipeVisionResult.targetXpath,
|
|
612
|
+
selector: swipeVisionResult.targetXpath,
|
|
613
|
+
parentIndex: swipeVisionResult.parentIndex,
|
|
614
|
+
targetIndex: swipeVisionResult.targetIndex,
|
|
615
|
+
fireflinkIndex: swipeVisionResult.targetIndex,
|
|
616
|
+
direction: swipeVisionResult.direction,
|
|
617
|
+
currentStep: step,
|
|
618
|
+
elementName: currentElementName || result.response.elementName,
|
|
619
|
+
elementType: stepResult.response.elementType || stepResult.response.type,
|
|
620
|
+
pageDOM: domInfo.dom,
|
|
621
|
+
currentPlatform,
|
|
622
|
+
driver,
|
|
623
|
+
driverContext,
|
|
624
|
+
priorAndNextSteps,
|
|
625
|
+
stepCount,
|
|
626
|
+
stepIndex: currentStepIndex,
|
|
627
|
+
manualStep: step,
|
|
628
|
+
stepsInputData,
|
|
629
|
+
returnTypeVariableName: currentReturnTypeVariableName,
|
|
630
|
+
...(currentVariableName ? { variableName: currentVariableName } : {}),
|
|
631
|
+
});
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
591
634
|
if (stepAction === "Builtin_DragAndDropToElement") {
|
|
592
635
|
handler = actionHandlers[stepAction];
|
|
593
636
|
const fireflinkIndex1 = stepResult.response.drag;
|
|
@@ -647,7 +690,7 @@ class AutomationRunner {
|
|
|
647
690
|
fireflinkIndex,
|
|
648
691
|
pageDOM,
|
|
649
692
|
elementName: currentElementName || result.response.elementName,
|
|
650
|
-
elementType: stepResult.response.elementType,
|
|
693
|
+
elementType: stepResult.response.elementType || stepResult.response.type,
|
|
651
694
|
numOfScrolls: stepResult.response.numOfScrolls,
|
|
652
695
|
direction: stepResult.response.direction,
|
|
653
696
|
stepIndex: currentStepIndex,
|
|
@@ -5,6 +5,7 @@ export declare enum PromptType {
|
|
|
5
5
|
ERROR_DESCRIPTION = "errorDescriptionPrompt",
|
|
6
6
|
FF_INSPECTOR = "ffInspectorNumExtractor",
|
|
7
7
|
VISION_PROMPT = "visionPrompt",
|
|
8
|
+
SWIPE_VISION_PROMPT = "swipe_vision_prompt",
|
|
8
9
|
VERIFY_PROMPT = "verifyActionExtractorPrompt",
|
|
9
10
|
GET_PROMPT = "getActionExtractorPrompt",
|
|
10
11
|
WAIT_ACTION_EXTRACTOR_PROMPT = "waitActionExtractorPrompt",
|
|
@@ -9,6 +9,7 @@ var PromptType;
|
|
|
9
9
|
PromptType["ERROR_DESCRIPTION"] = "errorDescriptionPrompt";
|
|
10
10
|
PromptType["FF_INSPECTOR"] = "ffInspectorNumExtractor";
|
|
11
11
|
PromptType["VISION_PROMPT"] = "visionPrompt";
|
|
12
|
+
PromptType["SWIPE_VISION_PROMPT"] = "swipe_vision_prompt";
|
|
12
13
|
PromptType["VERIFY_PROMPT"] = "verifyActionExtractorPrompt";
|
|
13
14
|
PromptType["GET_PROMPT"] = "getActionExtractorPrompt";
|
|
14
15
|
PromptType["WAIT_ACTION_EXTRACTOR_PROMPT"] = "waitActionExtractorPrompt";
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DomSearcher = void 0;
|
|
4
4
|
const fastest_levenshtein_1 = require("fastest-levenshtein");
|
|
5
5
|
const fast_fuzzy_1 = require("fast-fuzzy");
|
|
6
|
-
// import { logger } from "../utils/logger/logData";
|
|
7
6
|
class DomSearcher {
|
|
8
7
|
search(nodes, keywords, topN = 20, threshold = 40) {
|
|
9
8
|
if (typeof keywords === "string") {
|
|
@@ -281,238 +281,238 @@ async function getReactAnnotatedDOM(browser, startIndex = 0) {
|
|
|
281
281
|
// ==========================================================
|
|
282
282
|
// ANGULAR INJECTION
|
|
283
283
|
// ==========================================================
|
|
284
|
-
exports.INJECT_AND_SERIALIZE_DOM_JS = `
|
|
285
|
-
return (function (startIndex) {
|
|
286
|
-
let nodeIndex = typeof startIndex === "number" ? startIndex : 0;
|
|
287
|
-
function getCoverageStatus(el) {
|
|
288
|
-
if (!el || !el.getBoundingClientRect) return "unknown";
|
|
289
|
-
const s = getComputedStyle(el);
|
|
290
|
-
if (s.pointerEvents === "none") return "false";
|
|
291
|
-
const rect = el.getBoundingClientRect();
|
|
292
|
-
if (rect.width === 0 || rect.height === 0) return "false";
|
|
293
|
-
// Off-screen element — check if something is blocking the viewport
|
|
294
|
-
const isOffScreen =
|
|
295
|
-
rect.bottom < 0 ||
|
|
296
|
-
rect.right < 0 ||
|
|
297
|
-
rect.top > window.innerHeight ||
|
|
298
|
-
rect.left > window.innerWidth;
|
|
299
|
-
if (isOffScreen) {
|
|
300
|
-
const centerEl = document.elementFromPoint(
|
|
301
|
-
window.innerWidth / 2,
|
|
302
|
-
window.innerHeight / 2
|
|
303
|
-
);
|
|
304
|
-
if (centerEl && centerEl !== document.body && centerEl !== document.documentElement) {
|
|
305
|
-
// Something is at center of viewport — check if el and centerEl
|
|
306
|
-
// share the same scrollable container
|
|
307
|
-
const elScrollParent = getScrollParent(el);
|
|
308
|
-
const centerScrollParent = getScrollParent(centerEl);
|
|
309
|
-
if (elScrollParent !== centerScrollParent) return "true";
|
|
310
|
-
}
|
|
311
|
-
return "false";
|
|
312
|
-
}
|
|
313
|
-
// On-screen element — use elementFromPoint
|
|
314
|
-
const testPoints = [
|
|
315
|
-
[rect.left + 5, rect.top + 5],
|
|
316
|
-
[rect.right - 5, rect.top + 5],
|
|
317
|
-
[rect.left + 5, rect.bottom - 5],
|
|
318
|
-
[rect.right - 5, rect.bottom - 5],
|
|
319
|
-
[rect.left + rect.width / 2, rect.top + rect.height / 2]
|
|
320
|
-
];
|
|
321
|
-
for (const [x, y] of testPoints) {
|
|
322
|
-
if (x < 0 || y < 0 || x > window.innerWidth || y > window.innerHeight) continue;
|
|
323
|
-
const topEl = document.elementFromPoint(x, y);
|
|
324
|
-
if (!topEl) continue;
|
|
325
|
-
if (topEl === el || el.contains(topEl)) continue;
|
|
326
|
-
if (topEl.contains(el)) continue; // topEl is ancestor, not a cover
|
|
327
|
-
return "true";
|
|
328
|
-
}
|
|
329
|
-
return "false";
|
|
330
|
-
}
|
|
331
|
-
function getScrollParent(node) {
|
|
332
|
-
if (!node || node === document.body) return document.body;
|
|
333
|
-
const s = getComputedStyle(node);
|
|
334
|
-
if (
|
|
335
|
-
s.overflow === "auto" || s.overflow === "scroll" ||
|
|
336
|
-
s.overflowY === "auto" || s.overflowY === "scroll"
|
|
337
|
-
) return node;
|
|
338
|
-
return getScrollParent(node.parentElement);
|
|
339
|
-
}
|
|
340
|
-
function isUsable(node) {
|
|
341
|
-
const s = getComputedStyle(node);
|
|
342
|
-
if (s.display === "none" || s.visibility === "hidden" || s.opacity === "0") return false;
|
|
343
|
-
const rect = node.getBoundingClientRect();
|
|
344
|
-
if (rect.width === 0 || rect.height === 0) return false;
|
|
345
|
-
return true;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function isSelectRelated(node) {
|
|
349
|
-
return (
|
|
350
|
-
node.matches?.("select, option") ||
|
|
351
|
-
node.closest?.("select, [role='listbox'], [role='combobox']")
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
function annotate(node) {
|
|
355
|
-
const s = getComputedStyle(node);
|
|
356
|
-
node.setAttribute("element-visibility", s.visibility || "");
|
|
357
|
-
node.setAttribute("element-pointer-events", s.pointerEvents || "");
|
|
358
|
-
node.setAttribute("element-zindex", s.zIndex || "");
|
|
359
|
-
if (isSelectRelated(node)) {
|
|
360
|
-
node.setAttribute("element-covered", "false");
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (!isUsable(node)) {
|
|
365
|
-
node.setAttribute("element-covered", "true");
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
try {
|
|
369
|
-
node.setAttribute("element-covered", getCoverageStatus(node));
|
|
370
|
-
|
|
371
|
-
} catch {
|
|
372
|
-
node.setAttribute("element-covered", "unknown");
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
function getXPath(node) {
|
|
376
|
-
if (!node || node.nodeType !== 1) return "";
|
|
377
|
-
let segments = [];
|
|
378
|
-
let current = node;
|
|
379
|
-
while (current && current.nodeType === 1) {
|
|
380
|
-
const parent = current.parentNode;
|
|
381
|
-
if (parent instanceof ShadowRoot) {
|
|
382
|
-
const host = parent.host;
|
|
383
|
-
const siblings = Array.from(parent.children).filter(n => n.tagName === current.tagName);
|
|
384
|
-
const index = siblings.indexOf(current) + 1;
|
|
385
|
-
segments.unshift("shadow/" + current.tagName.toLowerCase() + "[" + index + "]");
|
|
386
|
-
current = host;
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
let index = 1;
|
|
390
|
-
if (parent && parent.children) {
|
|
391
|
-
const siblings = Array.from(parent.children).filter(n => n.tagName === current.tagName);
|
|
392
|
-
index = siblings.indexOf(current) + 1;
|
|
393
|
-
}
|
|
394
|
-
segments.unshift(current.tagName.toLowerCase() + "[" + index + "]");
|
|
395
|
-
if (!parent || parent.nodeType !== 1) break;
|
|
396
|
-
current = parent;
|
|
397
|
-
}
|
|
398
|
-
let path = "/" + segments.join("/");
|
|
399
|
-
if (node.ownerDocument !== document) {
|
|
400
|
-
const frameEl = node.ownerDocument.defaultView && node.ownerDocument.defaultView.frameElement;
|
|
401
|
-
if (frameEl) {
|
|
402
|
-
let framePath = frameEl.getAttribute("ff-xpath");
|
|
403
|
-
if (!framePath) framePath = getXPath(frameEl);
|
|
404
|
-
path = framePath + path;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
path = path.replace(/\/div\[0\]\/div\[0\]/g, "/div[1]").replace(/\/div\[0\]/g, "/div[1]");
|
|
408
|
-
return path;
|
|
409
|
-
}
|
|
410
|
-
function walk(root) {
|
|
411
|
-
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
|
|
412
|
-
let node = walker.currentNode;
|
|
413
|
-
while (node) {
|
|
414
|
-
if (node.nodeType === 1
|
|
415
|
-
&& typeof node.setAttribute === 'function'
|
|
416
|
-
&& !["META", "STYLE", "SCRIPT"].includes(node.tagName)) {
|
|
417
|
-
node.setAttribute("ff-inspect", "Fire-Flink-" + nodeIndex++);
|
|
418
|
-
node.setAttribute("ff-xpath", getXPath(node));
|
|
419
|
-
annotate(node);
|
|
420
|
-
}
|
|
421
|
-
if (node.shadowRoot) walk(node.shadowRoot);
|
|
422
|
-
if (node.tagName === "IFRAME") {
|
|
423
|
-
try {
|
|
424
|
-
const doc = node.contentDocument;
|
|
425
|
-
if (doc && doc.documentElement) {
|
|
426
|
-
walk(doc.documentElement);
|
|
427
|
-
} else {
|
|
428
|
-
node.setAttribute("ff-cross-origin", "true");
|
|
429
|
-
}
|
|
430
|
-
} catch {
|
|
431
|
-
node.setAttribute("ff-cross-origin", "true");
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
node = walker.nextNode();
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
function cleanup(root) {
|
|
438
|
-
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
|
|
439
|
-
let node = walker.currentNode;
|
|
440
|
-
while (node) {
|
|
441
|
-
if (node.nodeType === 1 && typeof node.removeAttribute === 'function') {
|
|
442
|
-
node.removeAttribute("ff-inspect");
|
|
443
|
-
node.removeAttribute("ff-xpath");
|
|
444
|
-
node.removeAttribute("element-visibility");
|
|
445
|
-
node.removeAttribute("element-pointer-events");
|
|
446
|
-
node.removeAttribute("element-zindex");
|
|
447
|
-
node.removeAttribute("element-covered");
|
|
448
|
-
node.removeAttribute("ff-cross-origin");
|
|
449
|
-
}
|
|
450
|
-
if (node.shadowRoot) cleanup(node.shadowRoot);
|
|
451
|
-
node = walker.nextNode();
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
cleanup(document.documentElement);
|
|
455
|
-
// Force browser to settle DOM after cleanup
|
|
456
|
-
void document.documentElement.offsetHeight;
|
|
457
|
-
walk(document.documentElement);
|
|
458
|
-
function serializeNode(node) {
|
|
459
|
-
if (node.nodeType === Node.TEXT_NODE) return node.textContent;
|
|
460
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return "";
|
|
461
|
-
const tag = node.tagName.toLowerCase();
|
|
462
|
-
let html = "<" + tag;
|
|
463
|
-
for (const attr of node.attributes) {
|
|
464
|
-
if (attr.name === "srcdoc") continue;
|
|
465
|
-
html += " " + attr.name + "=\"" + attr.value.replace(/"/g, """) + "\"";
|
|
466
|
-
}
|
|
467
|
-
html += ">";
|
|
468
|
-
if (node.shadowRoot) {
|
|
469
|
-
html += "<!--#shadow-root-->";
|
|
470
|
-
node.shadowRoot.childNodes.forEach(child => { html += serializeNode(child); });
|
|
471
|
-
html += "<!--#/shadow-root-->";
|
|
472
|
-
}
|
|
473
|
-
if (tag === "iframe" && node.getAttribute("ff-cross-origin") !== "true") {
|
|
474
|
-
try {
|
|
475
|
-
const doc = node.contentDocument;
|
|
476
|
-
if (doc) html += serializeNode(doc.documentElement);
|
|
477
|
-
} catch {}
|
|
478
|
-
}
|
|
479
|
-
node.childNodes.forEach(child => { html += serializeNode(child); });
|
|
480
|
-
html += "</" + tag + ">";
|
|
481
|
-
return html;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const crossOriginIframes =
|
|
485
|
-
Array.from(
|
|
486
|
-
document.querySelectorAll("iframe")
|
|
487
|
-
)
|
|
488
|
-
.filter(
|
|
489
|
-
iframe =>
|
|
490
|
-
iframe.getAttribute(
|
|
491
|
-
"ff-cross-origin"
|
|
492
|
-
) === "true"
|
|
493
|
-
)
|
|
494
|
-
.map(iframe => ({
|
|
495
|
-
xpath:
|
|
496
|
-
iframe.getAttribute(
|
|
497
|
-
"ff-xpath"
|
|
498
|
-
),
|
|
499
|
-
|
|
500
|
-
src:
|
|
501
|
-
iframe.getAttribute("src"),
|
|
502
|
-
|
|
503
|
-
title:
|
|
504
|
-
iframe.getAttribute("title"),
|
|
505
|
-
|
|
506
|
-
name:
|
|
507
|
-
iframe.getAttribute("name")
|
|
508
|
-
}));
|
|
509
|
-
|
|
510
|
-
return {
|
|
511
|
-
dom: "<!DOCTYPE html>\n" + serializeNode(document.documentElement),
|
|
512
|
-
crossOriginIframes: crossOriginIframes,
|
|
513
|
-
nextNodeIndex: nodeIndex
|
|
514
|
-
};
|
|
515
|
-
})(arguments[0]);
|
|
284
|
+
exports.INJECT_AND_SERIALIZE_DOM_JS = `
|
|
285
|
+
return (function (startIndex) {
|
|
286
|
+
let nodeIndex = typeof startIndex === "number" ? startIndex : 0;
|
|
287
|
+
function getCoverageStatus(el) {
|
|
288
|
+
if (!el || !el.getBoundingClientRect) return "unknown";
|
|
289
|
+
const s = getComputedStyle(el);
|
|
290
|
+
if (s.pointerEvents === "none") return "false";
|
|
291
|
+
const rect = el.getBoundingClientRect();
|
|
292
|
+
if (rect.width === 0 || rect.height === 0) return "false";
|
|
293
|
+
// Off-screen element — check if something is blocking the viewport
|
|
294
|
+
const isOffScreen =
|
|
295
|
+
rect.bottom < 0 ||
|
|
296
|
+
rect.right < 0 ||
|
|
297
|
+
rect.top > window.innerHeight ||
|
|
298
|
+
rect.left > window.innerWidth;
|
|
299
|
+
if (isOffScreen) {
|
|
300
|
+
const centerEl = document.elementFromPoint(
|
|
301
|
+
window.innerWidth / 2,
|
|
302
|
+
window.innerHeight / 2
|
|
303
|
+
);
|
|
304
|
+
if (centerEl && centerEl !== document.body && centerEl !== document.documentElement) {
|
|
305
|
+
// Something is at center of viewport — check if el and centerEl
|
|
306
|
+
// share the same scrollable container
|
|
307
|
+
const elScrollParent = getScrollParent(el);
|
|
308
|
+
const centerScrollParent = getScrollParent(centerEl);
|
|
309
|
+
if (elScrollParent !== centerScrollParent) return "true";
|
|
310
|
+
}
|
|
311
|
+
return "false";
|
|
312
|
+
}
|
|
313
|
+
// On-screen element — use elementFromPoint
|
|
314
|
+
const testPoints = [
|
|
315
|
+
[rect.left + 5, rect.top + 5],
|
|
316
|
+
[rect.right - 5, rect.top + 5],
|
|
317
|
+
[rect.left + 5, rect.bottom - 5],
|
|
318
|
+
[rect.right - 5, rect.bottom - 5],
|
|
319
|
+
[rect.left + rect.width / 2, rect.top + rect.height / 2]
|
|
320
|
+
];
|
|
321
|
+
for (const [x, y] of testPoints) {
|
|
322
|
+
if (x < 0 || y < 0 || x > window.innerWidth || y > window.innerHeight) continue;
|
|
323
|
+
const topEl = document.elementFromPoint(x, y);
|
|
324
|
+
if (!topEl) continue;
|
|
325
|
+
if (topEl === el || el.contains(topEl)) continue;
|
|
326
|
+
if (topEl.contains(el)) continue; // topEl is ancestor, not a cover
|
|
327
|
+
return "true";
|
|
328
|
+
}
|
|
329
|
+
return "false";
|
|
330
|
+
}
|
|
331
|
+
function getScrollParent(node) {
|
|
332
|
+
if (!node || node === document.body) return document.body;
|
|
333
|
+
const s = getComputedStyle(node);
|
|
334
|
+
if (
|
|
335
|
+
s.overflow === "auto" || s.overflow === "scroll" ||
|
|
336
|
+
s.overflowY === "auto" || s.overflowY === "scroll"
|
|
337
|
+
) return node;
|
|
338
|
+
return getScrollParent(node.parentElement);
|
|
339
|
+
}
|
|
340
|
+
function isUsable(node) {
|
|
341
|
+
const s = getComputedStyle(node);
|
|
342
|
+
if (s.display === "none" || s.visibility === "hidden" || s.opacity === "0") return false;
|
|
343
|
+
const rect = node.getBoundingClientRect();
|
|
344
|
+
if (rect.width === 0 || rect.height === 0) return false;
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function isSelectRelated(node) {
|
|
349
|
+
return (
|
|
350
|
+
node.matches?.("select, option") ||
|
|
351
|
+
node.closest?.("select, [role='listbox'], [role='combobox']")
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
function annotate(node) {
|
|
355
|
+
const s = getComputedStyle(node);
|
|
356
|
+
node.setAttribute("element-visibility", s.visibility || "");
|
|
357
|
+
node.setAttribute("element-pointer-events", s.pointerEvents || "");
|
|
358
|
+
node.setAttribute("element-zindex", s.zIndex || "");
|
|
359
|
+
if (isSelectRelated(node)) {
|
|
360
|
+
node.setAttribute("element-covered", "false");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (!isUsable(node)) {
|
|
365
|
+
node.setAttribute("element-covered", "true");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
node.setAttribute("element-covered", getCoverageStatus(node));
|
|
370
|
+
|
|
371
|
+
} catch {
|
|
372
|
+
node.setAttribute("element-covered", "unknown");
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function getXPath(node) {
|
|
376
|
+
if (!node || node.nodeType !== 1) return "";
|
|
377
|
+
let segments = [];
|
|
378
|
+
let current = node;
|
|
379
|
+
while (current && current.nodeType === 1) {
|
|
380
|
+
const parent = current.parentNode;
|
|
381
|
+
if (parent instanceof ShadowRoot) {
|
|
382
|
+
const host = parent.host;
|
|
383
|
+
const siblings = Array.from(parent.children).filter(n => n.tagName === current.tagName);
|
|
384
|
+
const index = siblings.indexOf(current) + 1;
|
|
385
|
+
segments.unshift("shadow/" + current.tagName.toLowerCase() + "[" + index + "]");
|
|
386
|
+
current = host;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
let index = 1;
|
|
390
|
+
if (parent && parent.children) {
|
|
391
|
+
const siblings = Array.from(parent.children).filter(n => n.tagName === current.tagName);
|
|
392
|
+
index = siblings.indexOf(current) + 1;
|
|
393
|
+
}
|
|
394
|
+
segments.unshift(current.tagName.toLowerCase() + "[" + index + "]");
|
|
395
|
+
if (!parent || parent.nodeType !== 1) break;
|
|
396
|
+
current = parent;
|
|
397
|
+
}
|
|
398
|
+
let path = "/" + segments.join("/");
|
|
399
|
+
if (node.ownerDocument !== document) {
|
|
400
|
+
const frameEl = node.ownerDocument.defaultView && node.ownerDocument.defaultView.frameElement;
|
|
401
|
+
if (frameEl) {
|
|
402
|
+
let framePath = frameEl.getAttribute("ff-xpath");
|
|
403
|
+
if (!framePath) framePath = getXPath(frameEl);
|
|
404
|
+
path = framePath + path;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
path = path.replace(/\/div\[0\]\/div\[0\]/g, "/div[1]").replace(/\/div\[0\]/g, "/div[1]");
|
|
408
|
+
return path;
|
|
409
|
+
}
|
|
410
|
+
function walk(root) {
|
|
411
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
|
|
412
|
+
let node = walker.currentNode;
|
|
413
|
+
while (node) {
|
|
414
|
+
if (node.nodeType === 1
|
|
415
|
+
&& typeof node.setAttribute === 'function'
|
|
416
|
+
&& !["META", "STYLE", "SCRIPT"].includes(node.tagName)) {
|
|
417
|
+
node.setAttribute("ff-inspect", "Fire-Flink-" + nodeIndex++);
|
|
418
|
+
node.setAttribute("ff-xpath", getXPath(node));
|
|
419
|
+
annotate(node);
|
|
420
|
+
}
|
|
421
|
+
if (node.shadowRoot) walk(node.shadowRoot);
|
|
422
|
+
if (node.tagName === "IFRAME") {
|
|
423
|
+
try {
|
|
424
|
+
const doc = node.contentDocument;
|
|
425
|
+
if (doc && doc.documentElement) {
|
|
426
|
+
walk(doc.documentElement);
|
|
427
|
+
} else {
|
|
428
|
+
node.setAttribute("ff-cross-origin", "true");
|
|
429
|
+
}
|
|
430
|
+
} catch {
|
|
431
|
+
node.setAttribute("ff-cross-origin", "true");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
node = walker.nextNode();
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function cleanup(root) {
|
|
438
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
|
|
439
|
+
let node = walker.currentNode;
|
|
440
|
+
while (node) {
|
|
441
|
+
if (node.nodeType === 1 && typeof node.removeAttribute === 'function') {
|
|
442
|
+
node.removeAttribute("ff-inspect");
|
|
443
|
+
node.removeAttribute("ff-xpath");
|
|
444
|
+
node.removeAttribute("element-visibility");
|
|
445
|
+
node.removeAttribute("element-pointer-events");
|
|
446
|
+
node.removeAttribute("element-zindex");
|
|
447
|
+
node.removeAttribute("element-covered");
|
|
448
|
+
node.removeAttribute("ff-cross-origin");
|
|
449
|
+
}
|
|
450
|
+
if (node.shadowRoot) cleanup(node.shadowRoot);
|
|
451
|
+
node = walker.nextNode();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
cleanup(document.documentElement);
|
|
455
|
+
// Force browser to settle DOM after cleanup
|
|
456
|
+
void document.documentElement.offsetHeight;
|
|
457
|
+
walk(document.documentElement);
|
|
458
|
+
function serializeNode(node) {
|
|
459
|
+
if (node.nodeType === Node.TEXT_NODE) return node.textContent;
|
|
460
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return "";
|
|
461
|
+
const tag = node.tagName.toLowerCase();
|
|
462
|
+
let html = "<" + tag;
|
|
463
|
+
for (const attr of node.attributes) {
|
|
464
|
+
if (attr.name === "srcdoc") continue;
|
|
465
|
+
html += " " + attr.name + "=\"" + attr.value.replace(/"/g, """) + "\"";
|
|
466
|
+
}
|
|
467
|
+
html += ">";
|
|
468
|
+
if (node.shadowRoot) {
|
|
469
|
+
html += "<!--#shadow-root-->";
|
|
470
|
+
node.shadowRoot.childNodes.forEach(child => { html += serializeNode(child); });
|
|
471
|
+
html += "<!--#/shadow-root-->";
|
|
472
|
+
}
|
|
473
|
+
if (tag === "iframe" && node.getAttribute("ff-cross-origin") !== "true") {
|
|
474
|
+
try {
|
|
475
|
+
const doc = node.contentDocument;
|
|
476
|
+
if (doc) html += serializeNode(doc.documentElement);
|
|
477
|
+
} catch {}
|
|
478
|
+
}
|
|
479
|
+
node.childNodes.forEach(child => { html += serializeNode(child); });
|
|
480
|
+
html += "</" + tag + ">";
|
|
481
|
+
return html;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const crossOriginIframes =
|
|
485
|
+
Array.from(
|
|
486
|
+
document.querySelectorAll("iframe")
|
|
487
|
+
)
|
|
488
|
+
.filter(
|
|
489
|
+
iframe =>
|
|
490
|
+
iframe.getAttribute(
|
|
491
|
+
"ff-cross-origin"
|
|
492
|
+
) === "true"
|
|
493
|
+
)
|
|
494
|
+
.map(iframe => ({
|
|
495
|
+
xpath:
|
|
496
|
+
iframe.getAttribute(
|
|
497
|
+
"ff-xpath"
|
|
498
|
+
),
|
|
499
|
+
|
|
500
|
+
src:
|
|
501
|
+
iframe.getAttribute("src"),
|
|
502
|
+
|
|
503
|
+
title:
|
|
504
|
+
iframe.getAttribute("title"),
|
|
505
|
+
|
|
506
|
+
name:
|
|
507
|
+
iframe.getAttribute("name")
|
|
508
|
+
}));
|
|
509
|
+
|
|
510
|
+
return {
|
|
511
|
+
dom: "<!DOCTYPE html>\n" + serializeNode(document.documentElement),
|
|
512
|
+
crossOriginIframes: crossOriginIframes,
|
|
513
|
+
nextNodeIndex: nodeIndex
|
|
514
|
+
};
|
|
515
|
+
})(arguments[0]);
|
|
516
516
|
`;
|
|
517
517
|
async function injectAngular(browser, startIndex = 0) {
|
|
518
518
|
const fn = new Function("startIndex", exports.INJECT_AND_SERIALIZE_DOM_JS);
|
|
@@ -61,8 +61,24 @@ function getFFIndexAndXPath(annotatedDom, fireflinkIndex, platform) {
|
|
|
61
61
|
else {
|
|
62
62
|
xpathQuery = `//*[@FF-inspecter='${fireflinkIndex}']`;
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
let nodes = xpath_1.default.select(xpathQuery, xmlDoc);
|
|
65
|
+
let element = nodes.length ? nodes[0] : null;
|
|
66
|
+
if (!element && isBounds) {
|
|
67
|
+
if (platform === "android") {
|
|
68
|
+
xpathQuery = `//*[@bounds='${fireflinkIndex}']`;
|
|
69
|
+
nodes = xpath_1.default.select(xpathQuery, xmlDoc);
|
|
70
|
+
element = nodes.length ? nodes[0] : null;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const match = fireflinkIndex.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
|
|
74
|
+
if (match) {
|
|
75
|
+
const [, x1, y1, x2, y2] = match.map(Number);
|
|
76
|
+
xpathQuery = `//*[@x='${x1}' and @y='${y1}' and @width='${x2 - x1}' and @height='${y2 - y1}']`;
|
|
77
|
+
nodes = xpath_1.default.select(xpathQuery, xmlDoc);
|
|
78
|
+
element = nodes.length ? nodes[0] : null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
66
82
|
if (!element) {
|
|
67
83
|
return { xpathPath: "" };
|
|
68
84
|
}
|