From 500537f8abcb917cfac6089cc68a9dff3d36eb27 Mon Sep 17 00:00:00 2001 From: MOHAN Date: Sun, 2 Aug 2026 16:10:05 +0530 Subject: [PATCH] fix(bikegear): use real clicks for product detail navigation --- scripts/testBikegearProxy.js | 33 ++++++++++++------ .../sources/bikegear/scraper.js | 34 +++++++++++++------ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/scripts/testBikegearProxy.js b/scripts/testBikegearProxy.js index d6784cd..dec0169 100644 --- a/scripts/testBikegearProxy.js +++ b/scripts/testBikegearProxy.js @@ -130,7 +130,9 @@ async function main() { let response; if (TEST_URL !== WARM_URL && !TEST_URL.includes("?page=")) { - response = await clickLinkForUrl(page, TEST_URL); + const clickResult = await clickLinkForUrl(page, TEST_URL); + response = clickResult.response; + console.log(`[BIKEGEAR-PROXY-TEST] clickedLink=${clickResult.clicked}`); } if (!response) { response = await page.goto(TEST_URL, { @@ -167,19 +169,30 @@ async function main() { } async function clickLinkForUrl(page, url) { + const locator = page.locator(`xpath=//a[@href=${xpathString(url)}]`).first(); + const count = await locator.count().catch(() => 0); + if (!count) return { clicked: false, response: null }; + const navigationPromise = page.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 45000 }) .catch(() => null); - const clicked = await page.evaluate((targetUrl) => { - const links = Array.from(document.querySelectorAll("a[href]")); - const link = links.find((a) => a.href === targetUrl); - if (!link) return false; - link.click(); - return true; - }, url).catch(() => false); - if (!clicked) return null; + await locator.scrollIntoViewIfNeeded().catch(() => {}); + await locator.hover({ timeout: 5000 }).catch(() => {}); + await page.waitForTimeout(500); + await locator.click({ timeout: 10000 }).catch(() => null); await page.waitForURL(url, { timeout: 45000 }).catch(() => {}); - return navigationPromise; + + return { + clicked: true, + response: await navigationPromise, + }; +} + +function xpathString(value) { + const text = String(value); + if (!text.includes("'")) return `'${text}'`; + if (!text.includes('"')) return `"${text}"`; + return `concat('${text.replace(/'/g, `', "'", '`)}')`; } main().catch((error) => { diff --git a/src/business-logic/import-pipeline/sources/bikegear/scraper.js b/src/business-logic/import-pipeline/sources/bikegear/scraper.js index 56ee940..4529cc8 100644 --- a/src/business-logic/import-pipeline/sources/bikegear/scraper.js +++ b/src/business-logic/import-pipeline/sources/bikegear/scraper.js @@ -202,8 +202,9 @@ async function fetchHtmlWithBrowser(url, referer = null) { } await page.waitForTimeout(2500); - response = await clickLinkForUrl(page, url); - if (response) { + const clickResult = await clickLinkForUrl(page, url); + response = clickResult.response; + if (clickResult.clicked) { // Navigation happened through the listing page link. } else { response = await page.goto(url, { @@ -255,19 +256,30 @@ async function fetchHtmlWithBrowser(url, referer = null) { } async function clickLinkForUrl(page, url) { + const locator = page.locator(`xpath=//a[@href=${xpathString(url)}]`).first(); + const count = await locator.count().catch(() => 0); + if (!count) return { clicked: false, response: null }; + const navigationPromise = page.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 45000 }) .catch(() => null); - const clicked = await page.evaluate((targetUrl) => { - const links = Array.from(document.querySelectorAll("a[href]")); - const link = links.find((a) => a.href === targetUrl); - if (!link) return false; - link.click(); - return true; - }, url).catch(() => false); - if (!clicked) return null; + await locator.scrollIntoViewIfNeeded().catch(() => {}); + await locator.hover({ timeout: 5000 }).catch(() => {}); + await page.waitForTimeout(500); + await locator.click({ timeout: 10000 }).catch(() => null); await page.waitForURL(url, { timeout: 45000 }).catch(() => {}); - return navigationPromise; + + return { + clicked: true, + response: await navigationPromise, + }; +} + +function xpathString(value) { + const text = String(value); + if (!text.includes("'")) return `'${text}'`; + if (!text.includes('"')) return `"${text}"`; + return `concat('${text.replace(/'/g, `', "'", '`)}')`; } // ── HTTP fetch (axios) ─────────────────────────────────────────────────────