fix(catalog): filter nav-crumb junk from PL24 legacy group parser

parseFordGroupsFromHtml leaked header/breadcrumb links into the category
list as pseudo-categories that drill to nothing: Hyundai/Kia "Portal"
(portal.action), logout, and Nissan "Repair & Maintenance Information"
(external https://eu.nissan.biz/). Skip these in both the primary tr-row
parser and the <a href> fallback. Real categories (group.action /
group-detail.action / json-(main|sub)-group.action) are unaffected, and
Volvo's vin-group.action?...group1=... real categories are kept.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-05 14:24:21 +03:00
parent 0d64e22c8d
commit f83973c628
2 changed files with 52 additions and 0 deletions

View File

@@ -60,3 +60,45 @@ describe("PL24FordLegacyService.parseP4VehicleResponse — model-selection page
expect(r).toBeNull();
});
});
// parseFordGroupsFromHtml leaks header/breadcrumb nav links into the category list
// (Hyundai/Kia "Portal", logout, Nissan "Repair & Maintenance Information" → eu.nissan.biz).
// These must be filtered; real categories (group.action / group-detail.action /
// json-(main|sub)-group.action, and Volvo vin-group.action?...group1=...) must survive.
const pg = svc as unknown as {
parseFordGroupsFromHtml(
html: string,
serviceName: string,
familyId: string,
): Array<{ code: string; nameTr: string; nameEn: string; linkPath: string }>;
};
describe("PL24FordLegacyService.parseFordGroupsFromHtml — nav-crumb junk filter", () => {
it("drops Portal / logout / external nav-crumbs, keeps real categories", () => {
const html = [
"<table>",
'<tr class="tc-data-row" url="group-detail.action?catId=20&lang=tr" caption="MOTOR"><td>MOTOR</td></tr>',
'<tr class="tc-data-row" url="portal.action?lang=tr" caption="Portal"><td>Portal</td></tr>',
'<tr class="tc-data-row" url="logout.action" caption="Logout"><td>Logout</td></tr>',
'<tr class="tc-data-row" url="https://eu.nissan.biz/" caption="Repair &amp; Maintenance Information"><td>Repair</td></tr>',
"</table>",
].join("");
const groups = pg.parseFordGroupsFromHtml(html, "hyundai_parts", "");
const names = groups.map((g) => g.nameTr);
expect(names).toContain("MOTOR");
expect(names).not.toContain("Portal");
expect(names).not.toContain("Logout");
expect(groups.some((g) => g.linkPath.includes("eu.nissan.biz"))).toBe(false);
});
it("keeps Volvo vin-group.action?...group1=... (real category) but drops the bare breadcrumb", () => {
const html = [
'<tr class="tc-data-row" url="vin-group.action?group1=2&lang=tr" caption="Motor"><td>Motor</td></tr>',
'<tr class="tc-data-row" url="vin-group.action?lang=tr" caption="Geri"><td>Geri</td></tr>',
].join("");
const groups = pg.parseFordGroupsFromHtml(html, "volvo_parts", "");
const names = groups.map((g) => g.nameTr);
expect(names).toContain("Motor");
expect(names).not.toContain("Geri");
});
});

View File

@@ -3526,6 +3526,14 @@ export class PL24FordLegacyService {
const url = urlMatch[1];
// Skip vehicle.action rows — those are sub-model selectors, not part group links
if (url.includes("vehicle.action")) continue;
// Skip header/breadcrumb navigation links that leak into the group table as
// pseudo-categories: Hyundai/Kia "Portal" (portal.action), logout, and Nissan's
// "Repair & Maintenance Information" external link (https://eu.nissan.biz/). Real
// categories use group.action / group-detail.action / json-(main|sub)-group.action,
// none of which match these. Volvo's vin-group.action?...group1=... is kept.
if (/(portal|logout)\.action/i.test(url)) continue;
if (url.includes("vin-group.action") && !url.includes("group1=")) continue;
if (/^https?:\/\//i.test(url) && !/\.action(\?|$)/i.test(url)) continue;
if (seen.has(url)) continue;
seen.add(url);
@@ -3588,6 +3596,8 @@ export class PL24FordLegacyService {
const url = match[1];
const name = match[2].replace(/<[^>]+>/g, "").trim();
if (!name || seen.has(url)) continue;
// Same nav-crumb guard as the primary parser (vin-group.action breadcrumb).
if (url.includes("vin-group.action") && !url.includes("group1=")) continue;
seen.add(url);
const linkPath = url.startsWith("/") ? url : `${basePath}/${url}`;
groups.push({