fix(amazon-shopping): verify prime and delivery filters

This commit is contained in:
2026-04-15 20:28:16 -05:00
parent a81a055ec6
commit fda0602ac9
20 changed files with 605 additions and 36 deletions
+18
View File
@@ -43,6 +43,13 @@ describe("amazon-shopping CLI", () => {
"200",
"--max-unit-price",
"4",
"--min-width",
"77",
"--require-prime",
"--delivery-by",
"tomorrow",
"--sort-by",
"price",
"--max-search-pages",
"3",
"--skip-details",
@@ -53,6 +60,10 @@ describe("amazon-shopping CLI", () => {
assert.equal(request.filters.minRating, 4.5);
assert.equal(request.filters.minReviews, 200);
assert.equal(request.filters.maxUnitPrice, 4);
assert.equal(request.filters.minWidthInches, 77);
assert.equal(request.filters.requirePrime, true);
assert.equal(request.filters.deliveryBy, "tomorrow");
assert.equal(request.filters.sortBy, "price");
assert.equal(request.maxSearchPages, 3);
assert.equal(request.skipDetails, true);
assert.equal(request.dryRun, true);
@@ -105,6 +116,13 @@ describe("amazon-shopping CLI", () => {
);
});
it("rejects unsupported sort modes", () => {
assert.throws(
() => parseCliRequest(["usb c cable", "--sort-by", "rating"]),
/sort-by must be either price or relevance/
);
});
it("builds the Amazon search URL without live network access", () => {
assert.equal(
buildSearchUrl("100w led bulbs"),
@@ -74,4 +74,45 @@ describe("extractDetailPage", () => {
assert.equal(details.availability, "In Stock");
assert.deepEqual(details.specs, [{ name: "Wattage", value: "15 watts" }]);
});
it("preserves a search-card Prime signal when detail delivery text omits Prime", () => {
const details = extractDetailPage(`
<h1 id="productTitle">Prime Sofa Bed</h1>
<div id="mir-layout-DELIVERY_BLOCK-slot-PRIMARY_DELIVERY_MESSAGE_LARGE">FREE delivery Tomorrow. Details</div>
<table>
<tr><td>Product Dimensions</td><td>35"D x 83"W x 31"H</td></tr>
</table>
`, {
asin: "B0PRIME123",
title: "Prime Sofa Bed",
url: "https://www.amazon.com/dp/B0PRIME123",
delivery: { display: "FREE delivery Tomorrow", free: true, prime: true },
specs: [],
bullets: [],
matchedFilters: [],
missingFields: [],
extractionNotes: []
});
assert.equal(details.delivery?.prime, true);
assert.equal(details.delivery?.free, true);
});
it("does not treat Prime in a detail title as Prime delivery", () => {
const details = extractDetailPage(`
<h1 id="productTitle">Prime Sofa Bed</h1>
<div id="mir-layout-DELIVERY_BLOCK-slot-PRIMARY_DELIVERY_MESSAGE_LARGE">FREE delivery Tomorrow. Details</div>
`, {
asin: "B0TITLE123",
title: "Prime Sofa Bed",
url: "https://www.amazon.com/dp/B0TITLE123",
specs: [],
bullets: [],
matchedFilters: [],
missingFields: [],
extractionNotes: []
});
assert.equal(details.delivery?.prime, false);
});
});
@@ -72,4 +72,55 @@ describe("applyFiltersAndLimit", () => {
assert.deepEqual(result.results.map((item) => item.asin), ["B0DUP0001", "B0UNIQUE1"]);
});
it("applies width, Prime, and delivery-by filters", () => {
const result = applyFiltersAndLimit([
product({
asin: "B0MATCH001",
rating: 4.3,
reviewCount: 250,
price: { amount: 399, currency: "USD", display: "$399.00" },
delivery: { display: "FREE delivery Tomorrow", free: true, prime: true },
specs: [{ name: "Product Dimensions", value: "35\"D x 83\"W x 31\"H" }]
}),
product({
asin: "B0NOPRIME1",
rating: 4.5,
reviewCount: 300,
delivery: { display: "FREE delivery Tomorrow", free: true, prime: false },
specs: [{ name: "Product Dimensions", value: "35\"D x 84\"W x 31\"H" }]
}),
product({
asin: "B0NARROW01",
rating: 4.6,
reviewCount: 300,
delivery: { display: "FREE delivery Tomorrow", free: true, prime: true },
specs: [{ name: "Product Dimensions", value: "35\"D x 65\"W x 31\"H" }]
})
], {
includeKeywords: [],
excludeKeywords: [],
minRating: 4,
minReviews: 200,
minWidthInches: 77,
requirePrime: true,
deliveryBy: "tomorrow"
}, 10);
assert.deepEqual(result.results.map((item) => item.asin), ["B0MATCH001"]);
assert.match(result.filteredOutReasons["B0NOPRIME1"]?.join(" ") ?? "", /Prime delivery not verified/);
assert.match(result.filteredOutReasons["B0NARROW01"]?.join(" ") ?? "", /width 65/);
assert.ok(result.results[0]?.matchedFilters.includes("width >= 77 inches"));
assert.ok(result.results[0]?.matchedFilters.includes("Prime delivery"));
assert.ok(result.results[0]?.matchedFilters.includes("delivery by tomorrow"));
});
it("sorts by price when requested", () => {
const result = applyFiltersAndLimit([
product({ asin: "B0EXPENSIV", rating: 4.9, reviewCount: 1000, price: { amount: 500, currency: "USD", display: "$500.00" } }),
product({ asin: "B0CHEAPER1", rating: 4.1, reviewCount: 300, price: { amount: 200, currency: "USD", display: "$200.00" } })
], { includeKeywords: [], excludeKeywords: [], sortBy: "price" }, 10);
assert.deepEqual(result.results.map((item) => item.asin), ["B0CHEAPER1", "B0EXPENSIV"]);
});
});
@@ -0,0 +1,56 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { extractWidthInches, formatWidthInches } from "../src/product-metrics.js";
import type { ProductSearchResult } from "../src/types.js";
function product(overrides: Partial<ProductSearchResult>): ProductSearchResult {
return {
asin: "B0WIDTH001",
title: "Base Product",
url: "https://www.amazon.com/dp/B0WIDTH001",
specs: [],
bullets: [],
matchedFilters: [],
missingFields: [],
extractionNotes: [],
...overrides
};
}
describe("product metrics", () => {
it("extracts explicit W dimensions from overall product specs", () => {
const width = extractWidthInches(product({
specs: [{ name: "Product Dimensions", value: "35\"D x 83.4\"W x 31\"H" }]
}));
assert.equal(width, 83.4);
});
it("uses dimension order labels when W is not repeated in the value", () => {
const width = extractWidthInches(product({
specs: [{ name: "Item Dimensions D x W x H", value: "35 x 108 x 31 inches" }]
}));
assert.equal(width, 108);
});
it("ignores non-overall width specs before falling back to title width", () => {
const width = extractWidthInches(product({
title: "83 Inch Sofa Bed",
specs: [
{ name: "Seat Interior Width", value: "65 Inches" },
{ name: "Arm Width", value: "5 Inches" },
{ name: "Minimum Required Door Width", value: "72 Inches" }
]
}));
assert.equal(width, 83);
});
it("formats unknown and decimal widths", () => {
assert.equal(formatWidthInches(undefined), "unknown");
assert.equal(formatWidthInches(83.4), "83.4\"");
assert.equal(formatWidthInches(108), "108\"");
});
});
@@ -42,4 +42,27 @@ describe("parseNaturalLanguageRequest", () => {
assert.equal(parsed.limit, 5);
assert.equal(parsed.filters.maxPrice, 30);
});
it("extracts sofa width, Prime, and delivery urgency filters", () => {
const parsed = parseNaturalLanguageRequest(
"sofa bed of 77inches or wider in width, review score of 4 stars and higher, 200+ reviews and shipped with prime, color beige if possible, delivery by tomorrow"
);
assert.equal(parsed.query, "sofa bed color beige if possible");
assert.equal(parsed.filters.minWidthInches, 77);
assert.equal(parsed.filters.minRating, 4);
assert.equal(parsed.filters.ratingComparison, "gte");
assert.equal(parsed.filters.minReviews, 200);
assert.equal(parsed.filters.reviewCountComparison, "gte");
assert.equal(parsed.filters.requirePrime, true);
assert.equal(parsed.filters.deliveryBy, "tomorrow");
});
it("extracts overnight delivery requests", () => {
const parsed = parseNaturalLanguageRequest("queen sleeper sofa with overnight shipping and Prime");
assert.equal(parsed.query, "queen sleeper sofa");
assert.equal(parsed.filters.requirePrime, true);
assert.equal(parsed.filters.deliveryBy, "overnight");
});
});
@@ -52,4 +52,46 @@ describe("report", () => {
assert.match(markdown, /price missing/);
assert.match(markdown, /https:\/\/www\.amazon\.com\/dp\/B0TEST0001/);
});
it("creates a table template with constraint status markers", () => {
const markdown = createMarkdownReport(createResponse({
query: "sofa bed beige",
filters: {
includeKeywords: [],
excludeKeywords: [],
minRating: 4,
minReviews: 200,
minWidthInches: 77,
requirePrime: true,
deliveryBy: "tomorrow"
},
limit: 10,
maxSearchPages: 2,
filteredOutCount: 3,
warnings: [],
now: () => new Date("2026-04-15T00:00:00.000Z"),
results: [{
asin: "B0SOFABED1",
title: "HONBAY Modular Sectional Sleeper",
url: "https://www.amazon.com/dp/B0SOFABED1",
price: { amount: 539.99, currency: "USD", display: "$539.99" },
rating: 4.1,
reviewCount: 242,
delivery: { display: "FREE delivery Tomorrow", free: true, prime: true },
specs: [{ name: "Item Dimensions D x W x H", value: "83.4\"D x 83.4\"W x 35\"H" }],
bullets: [],
matchedFilters: [],
missingFields: ["starBreakdown"],
extractionNotes: []
}]
}));
assert.match(markdown, /## Best Matches/);
assert.match(markdown, /\| # \| Product \| Price \| Rating \| Reviews \| Width \| Prime \| Delivery \| Link \|/);
assert.match(markdown, /HONBAY Modular Sectional Sleeper/);
assert.match(markdown, /83\.4" OK/);
assert.match(markdown, /Prime OK/);
assert.match(markdown, /Tomorrow OK/);
assert.match(markdown, /\[Amazon\]\(https:\/\/www\.amazon\.com\/dp\/B0SOFABED1\)/);
});
});
@@ -62,4 +62,34 @@ describe("extractSearchPage", () => {
assert.equal(extracted.products.length, 1);
assert.equal(extracted.products[0]?.price, undefined);
});
it("detects Prime badges even when visible delivery text omits the word Prime", () => {
const extracted = extractSearchPage(`
<div data-asin="B0PRIME123">
<h2><a href="/dp/B0PRIME123">Prime Sofa Bed</a></h2>
<span class="a-price"><span class="a-offscreen">$299.99</span></span>
<span aria-label="4.4 out of 5 stars"></span>
<span aria-label="246 ratings"></span>
<i class="a-icon a-icon-prime" aria-label="Amazon Prime"></i>
<span>FREE delivery Tomorrow</span>
</div>
`, "https://www.amazon.com/s?k=sofa+bed");
assert.equal(extracted.products.length, 1);
assert.equal(extracted.products[0]?.delivery?.prime, true);
assert.equal(extracted.products[0]?.delivery?.free, true);
assert.match(extracted.products[0]?.delivery?.display ?? "", /Tomorrow/);
});
it("does not treat Prime in a product title as Prime delivery", () => {
const extracted = extractSearchPage(`
<div data-asin="B0TITLE123">
<h2><a href="/dp/B0TITLE123">Prime Sofa Bed</a></h2>
<span>FREE delivery Tomorrow</span>
</div>
`, "https://www.amazon.com/s?k=sofa+bed");
assert.equal(extracted.products.length, 1);
assert.equal(extracted.products[0]?.delivery?.prime, false);
});
});