feat: condtionally handle getRect & getelementInfo

This commit is contained in:
amhsirak
2024-12-11 02:01:55 +05:30
parent c994072ef7
commit 81bbba473f

View File

@@ -22,100 +22,144 @@ type Workflow = WorkflowFile["workflow"];
*/ */
export const getElementInformation = async ( export const getElementInformation = async (
page: Page, page: Page,
coordinates: Coordinates coordinates: Coordinates,
listSelector: string,
) => { ) => {
try { try {
const elementInfo = await page.evaluate( if (listSelector !== '') {
async ({ x, y }) => { // Old implementation
const originalEl = document.elementFromPoint(x, y) as HTMLElement; const elementInfo = await page.evaluate(
if (originalEl) { async ({ x, y }) => {
let element = originalEl; const el = document.elementFromPoint(x, y) as HTMLElement;
if (el) {
const { parentElement } = el;
const element = parentElement?.tagName === 'A' ? parentElement : el;
let info: {
tagName: string;
hasOnlyText?: boolean;
innerText?: string;
url?: string;
imageUrl?: string;
attributes?: Record<string, string>;
innerHTML?: string;
outerHTML?: string;
} = {
tagName: element?.tagName ?? '',
};
if (element) {
info.attributes = Array.from(element.attributes).reduce(
(acc, attr) => {
acc[attr.name] = attr.value;
return acc;
},
{} as Record<string, string>
);
}
// Gather specific information based on the tag
if (element?.tagName === 'A') {
info.url = (element as HTMLAnchorElement).href;
info.innerText = element.innerText ?? '';
} else if (element?.tagName === 'IMG') {
info.imageUrl = (element as HTMLImageElement).src;
} else {
info.hasOnlyText = element?.children?.length === 0 &&
element?.innerText?.length > 0;
info.innerText = element?.innerText ?? '';
}
info.innerHTML = element.innerHTML;
info.outerHTML = element.outerHTML;
return info;
}
return null;
},
{ x: coordinates.x, y: coordinates.y },
);
return elementInfo;
} else {
// New implementation
const elementInfo = await page.evaluate(
async ({ x, y }) => {
const originalEl = document.elementFromPoint(x, y) as HTMLElement;
if (originalEl) {
let element = originalEl;
// if (originalEl.tagName === 'A') { const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE',
// element = originalEl; 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME',
// } else if (originalEl.parentElement?.tagName === 'A') { 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET',
// element = originalEl.parentElement; 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT',
// } else { 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT',
// Generic parent finding logic based on visual containment 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET',
const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT'
'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', ];
'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', while (element.parentElement) {
'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', const parentRect = element.parentElement.getBoundingClientRect();
'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', const childRect = element.getBoundingClientRect();
'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET',
'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A'
];
while (element.parentElement) {
const parentRect = element.parentElement.getBoundingClientRect();
const childRect = element.getBoundingClientRect();
if (!containerTags.includes(element.parentElement.tagName)) { if (!containerTags.includes(element.parentElement.tagName)) {
break; break;
}
const fullyContained =
parentRect.left <= childRect.left &&
parentRect.right >= childRect.right &&
parentRect.top <= childRect.top &&
parentRect.bottom >= childRect.bottom;
const significantOverlap =
(childRect.width * childRect.height) /
(parentRect.width * parentRect.height) > 0.5;
if (fullyContained && significantOverlap) {
element = element.parentElement;
} else {
break;
}
} }
// Check if parent visually contains the child let info: {
const fullyContained = tagName: string;
parentRect.left <= childRect.left && hasOnlyText?: boolean;
parentRect.right >= childRect.right && innerText?: string;
parentRect.top <= childRect.top && url?: string;
parentRect.bottom >= childRect.bottom; imageUrl?: string;
attributes?: Record<string, string>;
innerHTML?: string;
outerHTML?: string;
} = {
tagName: element?.tagName ?? '',
};
// Additional checks for more comprehensive containment if (element) {
const significantOverlap = info.attributes = Array.from(element.attributes).reduce(
(childRect.width * childRect.height) / (acc, attr) => {
(parentRect.width * parentRect.height) > 0.5; acc[attr.name] = attr.value;
return acc;
},
{} as Record<string, string>
);
}
if (fullyContained && significantOverlap) { if (element?.tagName === 'A') {
element = element.parentElement; info.url = (element as HTMLAnchorElement).href;
info.innerText = element.innerText ?? '';
} else if (element?.tagName === 'IMG') {
info.imageUrl = (element as HTMLImageElement).src;
} else { } else {
break; info.hasOnlyText = element?.children?.length === 0 &&
// } element?.innerText?.length > 0;
} } info.innerText = element?.innerText ?? '';
}
let info: { info.innerHTML = element.innerHTML;
tagName: string; info.outerHTML = element.outerHTML;
hasOnlyText?: boolean; return info;
innerText?: string;
url?: string;
imageUrl?: string;
attributes?: Record<string, string>;
innerHTML?: string;
outerHTML?: string;
} = {
tagName: element?.tagName ?? '',
};
if (element) {
info.attributes = Array.from(element.attributes).reduce(
(acc, attr) => {
acc[attr.name] = attr.value;
return acc;
},
{} as Record<string, string>
);
} }
return null;
// Existing tag-specific logic },
if (element?.tagName === 'A') { { x: coordinates.x, y: coordinates.y },
info.url = (element as HTMLAnchorElement).href; );
info.innerText = element.innerText ?? ''; return elementInfo;
} else if (element?.tagName === 'IMG') { }
info.imageUrl = (element as HTMLImageElement).src;
} else {
info.hasOnlyText = element?.children?.length === 0 &&
element?.innerText?.length > 0;
info.innerText = element?.innerText ?? '';
}
info.innerHTML = element.innerHTML;
info.outerHTML = element.outerHTML;
return info;
}
return null;
},
{ x: coordinates.x, y: coordinates.y },
);
return elementInfo;
} catch (error) { } catch (error) {
const { message, stack } = error as Error; const { message, stack } = error as Error;
console.error('Error while retrieving selector:', message); console.error('Error while retrieving selector:', message);
@@ -123,79 +167,103 @@ export const getElementInformation = async (
} }
}; };
export const getRect = async (page: Page, coordinates: Coordinates) => { export const getRect = async (page: Page, coordinates: Coordinates, listSelector: string) => {
try { try {
const rect = await page.evaluate( if (listSelector !== '') {
async ({ x, y }) => { // Old implementation
const originalEl = document.elementFromPoint(x, y) as HTMLElement; const rect = await page.evaluate(
if (originalEl) { async ({ x, y }) => {
let element = originalEl; const el = document.elementFromPoint(x, y) as HTMLElement;
if (el) {
const { parentElement } = el;
// Match the logic in recorder.ts for link clicks
const element = parentElement?.tagName === 'A' ? parentElement : el;
const rectangle = element?.getBoundingClientRect();
if (rectangle) {
return {
x: rectangle.x,
y: rectangle.y,
width: rectangle.width,
height: rectangle.height,
top: rectangle.top,
right: rectangle.right,
bottom: rectangle.bottom,
left: rectangle.left,
};
}
}
},
{ x: coordinates.x, y: coordinates.y },
);
return rect;
} else {
// New implementation
const rect = await page.evaluate(
async ({ x, y }) => {
const originalEl = document.elementFromPoint(x, y) as HTMLElement;
if (originalEl) {
let element = originalEl;
// if (originalEl.tagName === 'A') { const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE',
// element = originalEl; 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME',
// } else if (originalEl.parentElement?.tagName === 'A') { 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET',
// element = originalEl.parentElement; 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT',
// } else { 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT',
const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET',
'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT'
'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', ];
'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', while (element.parentElement) {
'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', const parentRect = element.parentElement.getBoundingClientRect();
'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', const childRect = element.getBoundingClientRect();
'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A'
];
while (element.parentElement) {
const parentRect = element.parentElement.getBoundingClientRect();
const childRect = element.getBoundingClientRect();
if (!containerTags.includes(element.parentElement.tagName)) { if (!containerTags.includes(element.parentElement.tagName)) {
break; break;
}
const fullyContained =
parentRect.left <= childRect.left &&
parentRect.right >= childRect.right &&
parentRect.top <= childRect.top &&
parentRect.bottom >= childRect.bottom;
const significantOverlap =
(childRect.width * childRect.height) /
(parentRect.width * parentRect.height) > 0.5;
if (fullyContained && significantOverlap) {
element = element.parentElement;
} else {
break;
}
} }
const rectangle = element?.getBoundingClientRect();
const fullyContained = if (rectangle) {
parentRect.left <= childRect.left && return {
parentRect.right >= childRect.right && x: rectangle.x,
parentRect.top <= childRect.top && y: rectangle.y,
parentRect.bottom >= childRect.bottom; width: rectangle.width,
height: rectangle.height,
const significantOverlap = top: rectangle.top,
(childRect.width * childRect.height) / right: rectangle.right,
(parentRect.width * parentRect.height) > 0.5; bottom: rectangle.bottom,
left: rectangle.left,
if (fullyContained && significantOverlap) { };
element = element.parentElement; }
} else {
break;
// }
}}
//element = element?.parentElement?.tagName === 'A' ? element?.parentElement : element;
const rectangle = element?.getBoundingClientRect();
if (rectangle) {
return {
x: rectangle.x,
y: rectangle.y,
width: rectangle.width,
height: rectangle.height,
top: rectangle.top,
right: rectangle.right,
bottom: rectangle.bottom,
left: rectangle.left,
};
} }
} return null;
}, },
{ x: coordinates.x, y: coordinates.y }, { x: coordinates.x, y: coordinates.y },
); );
return rect; return rect;
}
} catch (error) { } catch (error) {
const { message, stack } = error as Error; const { message, stack } = error as Error;
logger.log('error', `Error while retrieving selector: ${message}`); logger.log('error', `Error while retrieving selector: ${message}`);
logger.log('error', `Stack: ${stack}`); logger.log('error', `Stack: ${stack}`);
} }
} };
/** /**