fix: correct ebay title filtering and type contracts

This commit is contained in:
2026-04-27 02:04:48 -04:00
parent b73faa35da
commit 224e83ac4c
5 changed files with 69 additions and 8 deletions

View File

@@ -205,16 +205,18 @@ function parseEbayListings(
"opens in a new window or tab", "opens in a new window or tab",
]; ];
let shortened = false;
for (const uiString of uiStrings) { for (const uiString of uiStrings) {
const uiIndex = title.indexOf(uiString); const uiIndex = title.indexOf(uiString);
if (uiIndex !== -1) { if (uiIndex !== -1) {
title = title.substring(0, uiIndex).trim(); title = title.substring(0, uiIndex).trim();
shortened = true;
break; // Only remove one UI string per title break; // Only remove one UI string per title
} }
} }
// If the title became empty or too short after cleaning, skip this item // If the title was shortened by UI cleaning and became too short, skip this item
if (title.length < 10) { if (shortened && title.length < 10) {
continue; continue;
} }
} }

View File

@@ -899,6 +899,11 @@ export function parseFacebookAds(
if (Number.isNaN(dollars)) continue; if (Number.isNaN(dollars)) continue;
cents = Math.round(dollars * 100); cents = Math.round(dollars * 100);
} else if (
typeof priceObj.formatted_amount === "string" &&
priceObj.formatted_amount.toUpperCase() === "FREE"
) {
cents = 0;
} else { } else {
continue; // No price available continue; // No price available
} }
@@ -1192,8 +1197,7 @@ export default async function fetchFacebookItems(
? new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic) ? new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic)
: null; : null;
const totalProgress = ads.length; const totalProgress = ads.length;
const currentProgress = 0; progressBar?.start(totalProgress, 0);
progressBar?.start(totalProgress, currentProgress);
const items = parseFacebookAds(ads); const items = parseFacebookAds(ads);

View File

@@ -254,7 +254,7 @@ export function resolveCategoryId(category?: number | string): number {
function matchesPriceFilters( function matchesPriceFilters(
listing: DetailedListing, listing: DetailedListing,
searchOptions: Required<SearchOptions>, searchOptions: SearchOptions,
): boolean { ): boolean {
const cents = listing.listingPrice?.cents; const cents = listing.listingPrice?.cents;
@@ -971,9 +971,7 @@ export default async function fetchKijijiItems(
console.log( console.log(
`\nParsed ${filteredListings.length} detailed listings.`, `\nParsed ${filteredListings.length} detailed listings.`,
); );
return unstableMode.hideUnstableResults return finalizeResults(filteredListings);
? finalizeResults(filteredListings)
: finalizeResults(filteredListings);
} }
// Re-export error classes for convenience // Re-export error classes for convenience

View File

@@ -419,6 +419,33 @@ describe("eBay Scraper Cookie Handling", () => {
]); ]);
}); });
test("keeps short titles that were not shortened by UI cleaning", async () => {
global.fetch = mock(() =>
Promise.resolve({
ok: true,
text: () =>
Promise.resolve(`
<html><body>
<li class="s-item">
<a href="/itm/123"></a>
<h3>Free Bike</h3>
<span class="s-item__price">CA $0.00</span>
</li>
</body></html>
`),
}),
) as typeof fetch;
const results = await fetchEbayItems("bike", 1000);
expect(results).toEqual([
expect.objectContaining({
title: "Free Bike",
listingPrice: expect.objectContaining({ cents: 0, currency: "CAD" }),
}),
]);
});
test("accepts higher fallback prices without price classes", async () => { test("accepts higher fallback prices without price classes", async () => {
global.fetch = mock(() => global.fetch = mock(() =>
Promise.resolve({ Promise.resolve({

View File

@@ -1804,6 +1804,36 @@ describe("Facebook Marketplace Scraper Core Tests", () => {
}), }),
]); ]);
}); });
test("keeps free search listings when amount is missing but formatted_amount is FREE", () => {
const ads = [
{
node: {
listing: {
id: "free-no-amount",
marketplace_listing_title: "Free Sofa",
listing_price: {
formatted_amount: "FREE",
currency: "CAD",
},
is_live: true,
},
},
},
];
const results = parseFacebookAds(ads);
expect(results).toEqual([
expect.objectContaining({
title: "Free Sofa",
listingPrice: expect.objectContaining({
cents: 0,
amountFormatted: "FREE",
}),
}),
]);
});
}); });
}); });