diff --git a/packages/core/src/scrapers/ebay.ts b/packages/core/src/scrapers/ebay.ts index bbd45b0..bdf26e4 100644 --- a/packages/core/src/scrapers/ebay.ts +++ b/packages/core/src/scrapers/ebay.ts @@ -84,7 +84,8 @@ function parseEbayPrice( currency = "CAD"; } else if ( cleaned.toUpperCase().includes("USD") || - cleaned.toUpperCase().includes("US $") + cleaned.toUpperCase().includes("US $") || + cleaned.toUpperCase().includes("US$") ) { currency = "USD"; } else if (cleaned.includes("£")) { diff --git a/packages/core/src/scrapers/kijiji.ts b/packages/core/src/scrapers/kijiji.ts index 425c663..8a7c44a 100644 --- a/packages/core/src/scrapers/kijiji.ts +++ b/packages/core/src/scrapers/kijiji.ts @@ -462,18 +462,16 @@ async function fetchSellerDetails( accountType?: string; }> { try { - const [reviewData, profileData] = await Promise.all([ - fetchGraphQLData( - GRAPHQL_QUERIES.getReviewSummary, - { userId: posterId }, - BASE_URL, - ), - fetchGraphQLData( - GRAPHQL_QUERIES.getProfileMetrics, - { profileId: posterId }, - BASE_URL, - ), - ]); + const reviewData = await fetchGraphQLData( + GRAPHQL_QUERIES.getReviewSummary, + { userId: posterId }, + BASE_URL, + ); + const profileData = await fetchGraphQLData( + GRAPHQL_QUERIES.getProfileMetrics, + { profileId: posterId }, + BASE_URL, + ); const reviewResponse = reviewData as GraphQLReviewResponse; const profileResponse = profileData as GraphQLProfileResponse; diff --git a/packages/core/test/ebay-core.test.ts b/packages/core/test/ebay-core.test.ts index 02dd14f..2e90c5f 100644 --- a/packages/core/test/ebay-core.test.ts +++ b/packages/core/test/ebay-core.test.ts @@ -234,6 +234,32 @@ describe("eBay Scraper Cookie Handling", () => { ]); }); + test("treats US dollar prices without space as USD", async () => { + global.fetch = mock(() => + Promise.resolve({ + ok: true, + text: () => + Promise.resolve(` + +
  • + +

    Stable Laptop Bundle

    + US$123.45 +
  • + + `), + }), + ) as typeof fetch; + + const results = await fetchEbayItems("laptop", 1000); + + expect(results).toEqual([ + expect.objectContaining({ + listingPrice: expect.objectContaining({ currency: "USD", cents: 12345 }), + }), + ]); + }); + test("maps pound prices to GBP", async () => { global.fetch = mock(() => Promise.resolve({ diff --git a/packages/core/test/kijiji-core.test.ts b/packages/core/test/kijiji-core.test.ts index e0cde78..75ca393 100644 --- a/packages/core/test/kijiji-core.test.ts +++ b/packages/core/test/kijiji-core.test.ts @@ -772,6 +772,63 @@ describe("fetchKijijiItems", () => { ); }); + test("fetchSellerDetails does not fire concurrent GraphQL requests", async () => { + const html = ` + + + + `; + + let activeAnvilRequests = 0; + let maxActiveAnvilRequests = 0; + + global.fetch = mock(async (input: string | URL | Request) => { + const url = typeof input === "string" ? input : input.toString(); + + if (url.includes("/anvil/api")) { + activeAnvilRequests++; + maxActiveAnvilRequests = Math.max( + maxActiveAnvilRequests, + activeAnvilRequests, + ); + await new Promise((resolve) => setTimeout(resolve, 50)); + activeAnvilRequests--; + return { + ok: true, + json: () => Promise.resolve({ data: { user: {} } }), + headers: { get: () => null }, + url, + }; + } + + throw new Error(`Unexpected URL: ${url}`); + }) as typeof fetch; + + await parseDetailedListing(html, "https://www.kijiji.ca", { + includeClientSideData: true, + sellerDataDepth: "detailed", + }); + + expect(maxActiveAnvilRequests).toBe(1); + }); + test("returns results and unstableResults when unstable mode is enabled", async () => { const searchHtml = `