Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 45 additions & 114 deletions server/src/workflow-management/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,59 +302,38 @@ export const getElementInformation = async (
);
return elementInfo;
} else {
page.on('console', msg => {
console.log(`Browser console: ${msg.text()}`);
});
Comment on lines +305 to +307
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid adding a new page.on('console') listener on every call

page.on('console', …) is executed every time getElementInformation enters this
branch. After just a few invocations the page will have dozens of identical
listeners, causing:

  • memory‑leak‐like growth,
  • duplicated logs,
  • slower teardown.
-      page.on('console', msg => {
-        console.log(`Browser console: ${msg.text()}`);
-      });
+      if (page.listenerCount('console') === 0) {
+        page.on('console', msg => console.log(`Browser console: ${msg.text()}`));
+      }

(You could also register the listener once at module‑initialisation time instead.)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
page.on('console', msg => {
console.log(`Browser console: ${msg.text()}`);
});
if (page.listenerCount('console') === 0) {
page.on('console', msg => console.log(`Browser console: ${msg.text()}`));
}

const elementInfo = await page.evaluate(
async ({ x, y }) => {
const getDeepestElementFromPoint = (x: number, y: number): HTMLElement | null => {
let elements = document.elementsFromPoint(x, y) as HTMLElement[];
if (!elements.length) return null;

const findDeepestElement = (elements: HTMLElement[]): HTMLElement | null => {
const findContainerElement = (elements: HTMLElement[]): HTMLElement | null => {
if (!elements.length) return null;
if (elements.length === 1) return elements[0];

let deepestElement = elements[0];
let maxDepth = 0;

for (const element of elements) {
let depth = 0;
let current = element;
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
const rect = element.getBoundingClientRect();

while (current) {
depth++;
if (current.parentElement) {
current = current.parentElement;
} else {
break;
}
}

if (depth > maxDepth) {
maxDepth = depth;
deepestElement = element;
if (rect.width >= 30 && rect.height >= 30) {
const hasChildrenInList = elements.some((otherElement, j) =>
i !== j && element.contains(otherElement)
);

if (hasChildrenInList) {
return element;
}
}
}

return deepestElement;
return elements[0];
};

// Logic to get list container element
let targetElement = null;

for (const element of elements) {
const deepestEl = findDeepestElement(elements);

if (deepestEl && element !== deepestEl) {
if (element.contains(deepestEl) &&
element !== deepestEl.parentElement &&
element.tagName !== 'HTML' &&
element.tagName !== 'BODY') {
targetElement = element;
break;
}
}
}

let deepestElement = targetElement || findDeepestElement(elements);
let deepestElement = findContainerElement(elements);
if (!deepestElement) return null;
Comment on lines +314 to 337
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Duplicate “container element” heuristic – consider extracting to a helper

The identical findContainerElement implementation now appears three times
(getElementInformation, getRect, getNonUniqueSelectors). Besides the
maintenance burden, any future tweak (e.g. size threshold, containment rule)
must be triplicated and risks drifting out of sync.

Suggestion:

  1. Define the helper once in TypeScript:

    function findContainerElement(elements: HTMLElement[]): HTMLElement {
      // …body…
    }
  2. Pass it into each page.evaluate via
    page.addInitScript(findContainerElement.toString()) or by serialising the
    function in the evaluate call (evaluate(findContainerElement, …)).

This keeps the heuristic single‑sourced and testable.

Also applies to: 824-847, 1999-2022


const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
Expand Down Expand Up @@ -842,53 +821,29 @@ export const getRect = async (page: Page, coordinates: Coordinates, listSelector
let elements = document.elementsFromPoint(x, y) as HTMLElement[];
if (!elements.length) return null;

const findDeepestElement = (elements: HTMLElement[]): HTMLElement | null => {
const findContainerElement = (elements: HTMLElement[]): HTMLElement | null => {
if (!elements.length) return null;
if (elements.length === 1) return elements[0];

let deepestElement = elements[0];
let maxDepth = 0;

for (const element of elements) {
let depth = 0;
let current = element;

while (current) {
depth++;
if (current.parentElement) {
current = current.parentElement;
} else {
break;
}
}
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
const rect = element.getBoundingClientRect();

if (depth > maxDepth) {
maxDepth = depth;
deepestElement = element;
if (rect.width >= 30 && rect.height >= 30) {
const hasChildrenInList = elements.some((otherElement, j) =>
i !== j && element.contains(otherElement)
);

if (hasChildrenInList) {
return element;
}
}
}

return deepestElement;
return elements[0];
};

// Logic to get list container element
let targetElement = null;

for (const element of elements) {
const deepestEl = findDeepestElement(elements);

if (deepestEl && element !== deepestEl) {
if (element.contains(deepestEl) &&
element !== deepestEl.parentElement &&
element.tagName !== 'HTML' &&
element.tagName !== 'BODY') {
targetElement = element;
break;
}
}
}

let deepestElement = targetElement || findDeepestElement(elements);
let deepestElement = findContainerElement(elements);
if (!deepestElement) return null;

const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
Expand Down Expand Up @@ -2041,53 +1996,29 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
let elements = document.elementsFromPoint(x, y) as HTMLElement[];
if (!elements.length) return null;

const findDeepestElement = (elements: HTMLElement[]): HTMLElement | null => {
const findContainerElement = (elements: HTMLElement[]): HTMLElement | null => {
if (!elements.length) return null;
if (elements.length === 1) return elements[0];

let deepestElement = elements[0];
let maxDepth = 0;

for (const element of elements) {
let depth = 0;
let current = element;

while (current) {
depth++;
if (current.parentElement) {
current = current.parentElement;
} else {
break;
}
}
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
const rect = element.getBoundingClientRect();

if (depth > maxDepth) {
maxDepth = depth;
deepestElement = element;
if (rect.width >= 30 && rect.height >= 30) {
const hasChildrenInList = elements.some((otherElement, j) =>
i !== j && element.contains(otherElement)
);

if (hasChildrenInList) {
return element;
}
}
}

return deepestElement;
return elements[0];
};

// Logic to get list container element
let targetElement = null;

for (const element of elements) {
const deepestEl = findDeepestElement(elements);

if (deepestEl && element !== deepestEl) {
if (element.contains(deepestEl) &&
element !== deepestEl.parentElement &&
element.tagName !== 'HTML' &&
element.tagName !== 'BODY') {
targetElement = element;
break;
}
}
}

let deepestElement = targetElement || findDeepestElement(elements);
let deepestElement = findContainerElement(elements);
if (!deepestElement) return null;

const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
Expand Down