fix: correct ebay title filtering and type contracts
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user