refactor: handle facebook route-aware failure states
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
||||
import fetchFacebookItems from "../src/scrapers/facebook";
|
||||
import fetchFacebookItems, { fetchFacebookItem } from "../src/scrapers/facebook";
|
||||
|
||||
// Mock fetch globally
|
||||
const originalFetch = global.fetch;
|
||||
@@ -125,7 +125,7 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
ok: true,
|
||||
text: () =>
|
||||
Promise.resolve(
|
||||
`<html><body><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
`<html><body><script>"XCometMarketplaceSearchController"</script><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
),
|
||||
headers: {
|
||||
get: () => null,
|
||||
@@ -180,7 +180,7 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
ok: true,
|
||||
text: () =>
|
||||
Promise.resolve(
|
||||
`<html><body><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
`<html><body><script>"XCometMarketplaceSearchController"</script><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
),
|
||||
headers: {
|
||||
get: () => null,
|
||||
@@ -221,7 +221,7 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
ok: true,
|
||||
text: () =>
|
||||
Promise.resolve(
|
||||
`<html><body><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
`<html><body><script>"XCometMarketplaceSearchController"</script><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
),
|
||||
headers: {
|
||||
get: () => null,
|
||||
@@ -254,6 +254,76 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
test("should return empty array for auth-gated search HTML", async () => {
|
||||
const authGatedSearchHtml = `
|
||||
<html>
|
||||
<body>
|
||||
<script>"XCometMarketplaceSearchController"</script>
|
||||
<a href="/marketplace/item/123456789/">
|
||||
<span>Vintage Lamp</span>
|
||||
<span>CA$45</span>
|
||||
<span>Toronto, ON</span>
|
||||
</a>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
url: "https://www.facebook.com/login/?next=%2Fmarketplace%2Ftoronto%2Fsearch",
|
||||
text: () => Promise.resolve(authGatedSearchHtml),
|
||||
headers: {
|
||||
get: () => null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const results = await fetchFacebookItems("lamp", 1, "toronto", 25);
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
test("should return empty array when search request lands on unknown route", async () => {
|
||||
const wrongRouteHtml = `<html><body><script>${JSON.stringify({
|
||||
payload: {
|
||||
resultGroups: [
|
||||
{
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
listing: {
|
||||
id: "1",
|
||||
marketplace_listing_title: "Leaked Search Result",
|
||||
listing_price: {
|
||||
amount: "75.00",
|
||||
formatted_amount: "CA$75",
|
||||
currency: "CAD",
|
||||
},
|
||||
is_live: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})}</script></body></html>`;
|
||||
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
url: "https://www.facebook.com/marketplace/toronto/",
|
||||
text: () => Promise.resolve(wrongRouteHtml),
|
||||
headers: {
|
||||
get: () => null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const results = await fetchFacebookItems("lamp", 1, "toronto", 25);
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
test("should handle network errors", async () => {
|
||||
global.fetch = mock(() => Promise.reject(new Error("Network error")));
|
||||
|
||||
@@ -320,7 +390,7 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
ok: true,
|
||||
text: () =>
|
||||
Promise.resolve(
|
||||
`<html><body><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
`<html><body><script>"XCometMarketplaceSearchController"</script><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
),
|
||||
headers: {
|
||||
get: () => null,
|
||||
@@ -393,7 +463,7 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
ok: true,
|
||||
text: () =>
|
||||
Promise.resolve(
|
||||
`<html><body><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
`<html><body><script>"XCometMarketplaceSearchController"</script><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
),
|
||||
headers: {
|
||||
get: () => null,
|
||||
@@ -462,7 +532,7 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
ok: true,
|
||||
text: () =>
|
||||
Promise.resolve(
|
||||
`<html><body><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
`<html><body><script>"XCometMarketplaceSearchController"</script><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
),
|
||||
headers: {
|
||||
get: () => null,
|
||||
@@ -533,7 +603,7 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
ok: true,
|
||||
text: () =>
|
||||
Promise.resolve(
|
||||
`<html><body><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
`<html><body><script>"XCometMarketplaceSearchController"</script><script>${JSON.stringify(mockSearchData)}</script></body></html>`,
|
||||
),
|
||||
headers: {
|
||||
get: () => null,
|
||||
@@ -599,4 +669,45 @@ describe("Facebook Marketplace Scraper Integration Tests", () => {
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Item Fetch Function", () => {
|
||||
test("should return null for unavailable item responses", async () => {
|
||||
const unavailableItemHtml = `
|
||||
<html>
|
||||
<body>
|
||||
<script>${JSON.stringify({
|
||||
payload: {
|
||||
listing: {
|
||||
id: "related-123",
|
||||
__typename: "GroupCommerceProductItem",
|
||||
marketplace_listing_title: "Related Listing",
|
||||
formatted_price: { text: "CA$90" },
|
||||
listing_price: {
|
||||
amount: "90.00",
|
||||
currency: "CAD",
|
||||
amount_with_offset: "90.00",
|
||||
},
|
||||
is_live: true,
|
||||
},
|
||||
},
|
||||
})}</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
url: "https://www.facebook.com/marketplace/toronto/?unavailable_product=1",
|
||||
text: () => Promise.resolve(unavailableItemHtml),
|
||||
headers: {
|
||||
get: () => null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await fetchFacebookItem("123");
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user