Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: better nested elements capture in capture list #187

Merged
merged 9 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions server/src/workflow-management/classes/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,8 @@ export class WorkflowGenerator {
if (this.listSelector !== '') {
const childSelectors = await getChildSelectors(page, this.listSelector || '');
this.socket.emit('highlighter', { rect, selector: displaySelector, elementInfo, childSelectors })
console.log(`Child Selectors: ${childSelectors}`)
console.log(`Parent Selector: ${this.listSelector}`)
} else {
this.socket.emit('highlighter', { rect, selector: displaySelector, elementInfo });
}
Expand Down
26 changes: 20 additions & 6 deletions server/src/workflow-management/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,7 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
export const getChildSelectors = async (page: Page, parentSelector: string): Promise<string[]> => {
try {
const childSelectors = await page.evaluate((parentSelector: string) => {
// Function to get a non-unique selector based on tag and class (if present)
function getNonUniqueSelector(element: HTMLElement): string {
let selector = element.tagName.toLowerCase();

Expand All @@ -811,6 +812,7 @@ export const getChildSelectors = async (page: Page, parentSelector: string): Pro
return selector;
}

// Function to generate selector path from an element to its parent
function getSelectorPath(element: HTMLElement | null): string {
if (!element || !element.parentElement) return '';

Expand All @@ -820,22 +822,33 @@ export const getChildSelectors = async (page: Page, parentSelector: string): Pro
return `${parentSelector} > ${elementSelector}`;
}

function getAllDescendantSelectors(element: HTMLElement, stopAtParent: HTMLElement | null): string[] {
// Function to recursively get all descendant selectors
function getAllDescendantSelectors(element: HTMLElement): string[] {
let selectors: string[] = [];
const children = Array.from(element.children) as HTMLElement[];

for (const child of children) {
selectors.push(getSelectorPath(child));
selectors = selectors.concat(getAllDescendantSelectors(child, stopAtParent));
const childPath = getSelectorPath(child);
if (childPath) {
selectors.push(childPath); // Add direct child path
selectors = selectors.concat(getAllDescendantSelectors(child)); // Recursively process descendants
}
}

return selectors;
}

const parentElement = document.querySelector(parentSelector) as HTMLElement;
if (!parentElement) return [];
// Find all occurrences of the parent selector in the DOM
const parentElements = Array.from(document.querySelectorAll(parentSelector)) as HTMLElement[];
const allChildSelectors = new Set<string>(); // Use a set to ensure uniqueness

// Process each parent element and its descendants
parentElements.forEach((parentElement) => {
const descendantSelectors = getAllDescendantSelectors(parentElement);
descendantSelectors.forEach((selector) => allChildSelectors.add(selector)); // Add selectors to the set
});

return getAllDescendantSelectors(parentElement, parentElement);
return Array.from(allChildSelectors); // Convert the set back to an array
}, parentSelector);

return childSelectors || [];
Expand All @@ -845,6 +858,7 @@ export const getChildSelectors = async (page: Page, parentSelector: string): Pro
}
};


/**
* Returns the first pair from the given workflow that contains the given selector
* inside the where condition, and it is the only selector there.
Expand Down
16 changes: 11 additions & 5 deletions src/components/molecules/RunContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,19 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
const firstKey = Object.keys(row.serializableOutput)[0];
const data = row.serializableOutput[firstKey];
if (Array.isArray(data)) {
setTableData(data);
if (data.length > 0) {
setColumns(Object.keys(data[0]));
// Filter out completely empty rows
const filteredData = data.filter(row =>
Object.values(row).some(value => value !== undefined && value !== "")
);
setTableData(filteredData);
if (filteredData.length > 0) {
setColumns(Object.keys(filteredData[0]));
}
}
}
}, [row.serializableOutput]);


// Function to convert table data to CSV format
const convertToCSV = (data: any[], columns: string[]): string => {
const header = columns.join(',');
Expand All @@ -53,7 +58,6 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
return [header, ...rows].join('\n');
};

// Function to download CSV file when called
const downloadCSV = () => {
const csvContent = convertToCSV(tableData, columns);
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
Expand Down Expand Up @@ -140,7 +144,9 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
{tableData.map((row, index) => (
<TableRow key={index}>
{columns.map((column) => (
<TableCell key={column}>{row[column]}</TableCell>
<TableCell key={column}>
{row[column] === undefined || row[column] === "" ? "-" : row[column]}
</TableCell>
))}
</TableRow>
))}
Expand Down