fix: preserve default scraper result contracts
This commit is contained in:
@@ -1,6 +1,20 @@
|
||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
||||
import type { EbayListingDetails } from "../src/scrapers/ebay";
|
||||
import fetchEbayItems from "../src/scrapers/ebay";
|
||||
|
||||
type Assert<T extends true> = T;
|
||||
type IsExact<T, U> =
|
||||
(<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2
|
||||
? (<G>() => G extends U ? 1 : 2) extends <G>() => G extends T ? 1 : 2
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
|
||||
const getDefaultEbayItems = async () => fetchEbayItems("laptop");
|
||||
type _EbayDefaultReturn = Assert<
|
||||
IsExact<Awaited<ReturnType<typeof getDefaultEbayItems>>, EbayListingDetails[]>
|
||||
>;
|
||||
|
||||
const originalFetch = global.fetch;
|
||||
const originalWarn = console.warn;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
||||
import {
|
||||
classifyFacebookResponse,
|
||||
type FacebookListingDetails,
|
||||
ensureFacebookCookies,
|
||||
extractFacebookBootstrapCandidates,
|
||||
extractFacebookItemData,
|
||||
@@ -14,6 +15,19 @@ import {
|
||||
import { formatCookiesForHeader } from "../src/utils/cookies";
|
||||
import { formatCentsToCurrency } from "../src/utils/format";
|
||||
|
||||
type Assert<T extends true> = T;
|
||||
type IsExact<T, U> =
|
||||
(<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2
|
||||
? (<G>() => G extends U ? 1 : 2) extends <G>() => G extends T ? 1 : 2
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
|
||||
const getDefaultFacebookItems = async () => fetchFacebookItems("chair");
|
||||
type _FacebookDefaultReturn = Assert<
|
||||
IsExact<Awaited<ReturnType<typeof getDefaultFacebookItems>>, FacebookListingDetails[]>
|
||||
>;
|
||||
|
||||
// Mock fetch globally
|
||||
const originalFetch = global.fetch;
|
||||
|
||||
@@ -492,17 +506,97 @@ describe("Facebook Marketplace Scraper Core Tests", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const results = await fetchFacebookItems("chair", 1, "toronto", 1, {
|
||||
const results = await fetchFacebookItems("chair", 1, "toronto", 25, {
|
||||
hideUnstableResults: true,
|
||||
});
|
||||
|
||||
expect(results).toEqual({
|
||||
results: [expect.objectContaining({ title: "Stable Chair Listing" })],
|
||||
results: [
|
||||
expect.objectContaining({ title: "Stable Chair Listing" }),
|
||||
expect.objectContaining({ title: "Another Stable Chair" }),
|
||||
],
|
||||
unstableResults: [
|
||||
expect.objectContaining({ title: "Suspiciously Cheap Chair" }),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test("unstable mode keeps MAX_ITEMS as the classification boundary", async () => {
|
||||
const mockSearchHtml = `<html><body><script>"XCometMarketplaceSearchController"</script><script>${JSON.stringify({
|
||||
payload: {
|
||||
resultGroups: [
|
||||
{
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
listing: {
|
||||
id: "1",
|
||||
marketplace_listing_title: "Boundary Stable Chair",
|
||||
listing_price: {
|
||||
amount: "100.00",
|
||||
formatted_amount: "CA$100",
|
||||
currency: "CAD",
|
||||
},
|
||||
is_live: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
listing: {
|
||||
id: "2",
|
||||
marketplace_listing_title: "Boundary Cheap Chair",
|
||||
listing_price: {
|
||||
amount: "50.00",
|
||||
formatted_amount: "CA$50",
|
||||
currency: "CAD",
|
||||
},
|
||||
is_live: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
listing: {
|
||||
id: "3",
|
||||
marketplace_listing_title: "Past Boundary Chair",
|
||||
listing_price: {
|
||||
amount: "110.00",
|
||||
formatted_amount: "CA$110",
|
||||
currency: "CAD",
|
||||
},
|
||||
is_live: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})}</script></body></html>`;
|
||||
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
text: () => Promise.resolve(mockSearchHtml),
|
||||
url: "https://www.facebook.com/marketplace/toronto/search?query=chair",
|
||||
headers: {
|
||||
get: () => null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const results = await fetchFacebookItems("chair", 1, "toronto", 2, {
|
||||
hideUnstableResults: true,
|
||||
});
|
||||
|
||||
expect(results).toEqual({
|
||||
results: [expect.objectContaining({ title: "Boundary Stable Chair" })],
|
||||
unstableResults: [
|
||||
expect.objectContaining({ title: "Boundary Cheap Chair" }),
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Data Extraction", () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
||||
import {
|
||||
buildSearchUrl,
|
||||
default as fetchKijijiItems,
|
||||
type DetailedListing,
|
||||
NetworkError,
|
||||
ParseError,
|
||||
RateLimitError,
|
||||
@@ -10,6 +11,19 @@ import {
|
||||
ValidationError,
|
||||
} from "../src/scrapers/kijiji";
|
||||
|
||||
type Assert<T extends true> = T;
|
||||
type IsExact<T, U> =
|
||||
(<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2
|
||||
? (<G>() => G extends U ? 1 : 2) extends <G>() => G extends T ? 1 : 2
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
|
||||
const getDefaultKijijiItems = async () => fetchKijijiItems("phone");
|
||||
type _KijijiDefaultReturn = Assert<
|
||||
IsExact<Awaited<ReturnType<typeof getDefaultKijijiItems>>, DetailedListing[]>
|
||||
>;
|
||||
|
||||
const originalFetch = global.fetch;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
Reference in New Issue
Block a user