From 1ad4b5d97abd9fe7c8c3ed59e7f35f7561aa94f6 Mon Sep 17 00:00:00 2001 From: Rohit Date: Sun, 26 Jan 2025 18:51:54 +0530 Subject: [PATCH 1/3] feat: nth-child selector generation for child selectors --- server/src/workflow-management/selector.ts | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 8a9096ec..3f44a01e 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -1654,6 +1654,31 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates } } + if (element.parentElement) { + // Look for identical siblings + const siblings = Array.from(element.parentElement.children); + const identicalSiblings = siblings.filter(sibling => { + if (sibling === element) return false; + + let siblingSelector = sibling.tagName.toLowerCase(); + const siblingClassName = typeof sibling.className === 'string' ? sibling.className : ''; + if (siblingClassName) { + const siblingClasses = siblingClassName.split(/\s+/).filter(Boolean); + const validSiblingClasses = siblingClasses.filter(cls => !cls.startsWith('!') && !cls.includes(':')); + if (validSiblingClasses.length > 0) { + siblingSelector += '.' + validSiblingClasses.map(cls => CSS.escape(cls)).join('.'); + } + } + + return siblingSelector === selector; + }); + + if (identicalSiblings.length > 0) { + const position = siblings.indexOf(element) + 1; + selector += `:nth-child(${position})`; + } + } + return selector; } @@ -1894,6 +1919,31 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates } } + if (element.parentElement) { + // Look for identical siblings + const siblings = Array.from(element.parentElement.children); + const identicalSiblings = siblings.filter(sibling => { + if (sibling === element) return false; + + let siblingSelector = sibling.tagName.toLowerCase(); + const siblingClassName = typeof sibling.className === 'string' ? sibling.className : ''; + if (siblingClassName) { + const siblingClasses = siblingClassName.split(/\s+/).filter(Boolean); + const validSiblingClasses = siblingClasses.filter(cls => !cls.startsWith('!') && !cls.includes(':')); + if (validSiblingClasses.length > 0) { + siblingSelector += '.' + validSiblingClasses.map(cls => CSS.escape(cls)).join('.'); + } + } + + return siblingSelector === selector; + }); + + if (identicalSiblings.length > 0) { + const position = siblings.indexOf(element) + 1; + selector += `:nth-child(${position})`; + } + } + return selector; } @@ -2025,6 +2075,31 @@ export const getChildSelectors = async (page: Page, parentSelector: string): Pro } } + if (element.parentElement) { + // Look for identical siblings + const siblings = Array.from(element.parentElement.children); + const identicalSiblings = siblings.filter(sibling => { + if (sibling === element) return false; + + let siblingSelector = sibling.tagName.toLowerCase(); + const siblingClassName = typeof sibling.className === 'string' ? sibling.className : ''; + if (siblingClassName) { + const siblingClasses = siblingClassName.split(/\s+/).filter(Boolean); + const validSiblingClasses = siblingClasses.filter(cls => !cls.startsWith('!') && !cls.includes(':')); + if (validSiblingClasses.length > 0) { + siblingSelector += '.' + validSiblingClasses.map(cls => CSS.escape(cls)).join('.'); + } + } + + return siblingSelector === selector; + }); + + if (identicalSiblings.length > 0) { + const position = siblings.indexOf(element) + 1; + selector += `:nth-child(${position})`; + } + } + return selector; } From 4a7b111dcdd17b6192a789be087a80be40fad60b Mon Sep 17 00:00:00 2001 From: Rohit Date: Fri, 31 Jan 2025 00:42:19 +0530 Subject: [PATCH 2/3] feat: modify selectors with nth-child --- src/components/browser/BrowserWindow.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/browser/BrowserWindow.tsx b/src/components/browser/BrowserWindow.tsx index 9f62a22d..0f5119f3 100644 --- a/src/components/browser/BrowserWindow.tsx +++ b/src/components/browser/BrowserWindow.tsx @@ -263,6 +263,11 @@ export const BrowserWindow = () => { } if (getList === true && !listSelector) { + let cleanedSelector = highlighterData.selector; + if (cleanedSelector.includes('nth-child')) { + cleanedSelector = cleanedSelector.replace(/:nth-child\(\d+\)/g, ''); + } + setListSelector(highlighterData.selector); notify(`info`, t('browser_window.attribute_modal.notifications.list_select_success')); setCurrentListId(Date.now()); @@ -275,13 +280,25 @@ export const BrowserWindow = () => { // Add fields to the list if (options.length === 1) { const attribute = options[0].value; + let currentSelector = highlighterData.selector; + + if (currentSelector.includes('>')) { + const [firstPart, ...restParts] = currentSelector.split('>').map(p => p.trim()); + const listSelectorRightPart = listSelector.split('>').pop()?.trim().replace(/:nth-child\(\d+\)/g, ''); + + if (firstPart.includes('nth-child') && + firstPart.replace(/:nth-child\(\d+\)/g, '') === listSelectorRightPart) { + currentSelector = `${firstPart.replace(/:nth-child\(\d+\)/g, '')} > ${restParts.join(' > ')}`; + } + } + const newField: TextStep = { id: Date.now(), type: 'text', label: `Label ${Object.keys(fields).length + 1}`, data: data, selectorObj: { - selector: highlighterData.selector, + selector: currentSelector, tag: highlighterData.elementInfo?.tagName, shadow: highlighterData.elementInfo?.isShadowRoot, attribute From 2d78cbf08da6470a48e29419667f36f6c00087d8 Mon Sep 17 00:00:00 2001 From: Rohit Date: Fri, 31 Jan 2025 01:08:07 +0530 Subject: [PATCH 3/3] feat: set modified list selector --- src/components/browser/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/browser/BrowserWindow.tsx b/src/components/browser/BrowserWindow.tsx index 0f5119f3..ffeb0df5 100644 --- a/src/components/browser/BrowserWindow.tsx +++ b/src/components/browser/BrowserWindow.tsx @@ -268,7 +268,7 @@ export const BrowserWindow = () => { cleanedSelector = cleanedSelector.replace(/:nth-child\(\d+\)/g, ''); } - setListSelector(highlighterData.selector); + setListSelector(cleanedSelector); notify(`info`, t('browser_window.attribute_modal.notifications.list_select_success')); setCurrentListId(Date.now()); setFields({});