mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-10-05 07:13:06 +08:00
1137 lines
52 KiB
TypeScript
1137 lines
52 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { JSDOM } from 'jsdom';
|
|
import { collectAmazonSearchPageData } from '../src/page-worker/web-injectors/amazon-search-parser';
|
|
|
|
const html = `
|
|
<main id="search">
|
|
<section data-component-type="sponsored-brand">
|
|
<span aria-label="Sponsored">Sponsored</span>
|
|
<a href="/stores/example"><span class="brand-logo">Example brand</span></a>
|
|
<h2>Example brand headline</h2>
|
|
<article data-asin="B000000001">
|
|
<h3>Brand product one</h3>
|
|
<a href="/dp/B000000001"><img src="https://example.com/brand-1.jpg" /></a>
|
|
</article>
|
|
<article data-asin="B000000002">
|
|
<h3>Brand product two</h3>
|
|
<a href="/dp/B000000002"><img src="https://example.com/brand-2.jpg" /></a>
|
|
</article>
|
|
</section>
|
|
|
|
<div class="s-main-slot">
|
|
<div class="ordinary-result-grid">
|
|
<article data-component-type="s-search-result" data-asin="B000000003">
|
|
<span>Amazon's Choice</span>
|
|
<h2>Organic product</h2>
|
|
<a href="/dp/B000000003"><img class="s-image" src="https://example.com/organic.jpg" /></a>
|
|
<span class="a-price"><span class="a-offscreen">$29.99</span></span>
|
|
</article>
|
|
|
|
<article data-component-type="s-search-result" data-asin="B000000004">
|
|
<a aria-label="View Sponsored information or leave ad feedback"></a>
|
|
<h2>Sponsored product</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB000000004"><img class="s-image" src="https://example.com/sp.jpg" /></a>
|
|
<span class="a-price"><span class="a-offscreen">$33.93</span></span>
|
|
<span class="a-price a-text-price" data-a-strike="true"><span class="a-offscreen">$39.99</span></span>
|
|
<span class="a-price a-text-price apex-priceperunit-value"><span class="a-offscreen">$0.28</span></span>
|
|
</article>
|
|
|
|
<article data-component-type="s-search-result" data-asin="B000000007">
|
|
<h2>Second organic product</h2>
|
|
<a href="/dp/B000000007"><img class="s-image" src="https://example.com/organic-2.jpg" /></a>
|
|
</article>
|
|
</div>
|
|
|
|
<section class="custom-sponsored-module">
|
|
<span>Sponsored</span>
|
|
<article data-asin="B000000005">
|
|
<h3>Unknown sponsored product</h3>
|
|
<a href="/dp/B000000005"><img src="https://example.com/unknown.jpg" /></a>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="bottom-sponsored-video-module">
|
|
<video></video>
|
|
<span>Sponsored</span>
|
|
<a href="/stores/video-brand"><span class="brand-logo">Video brand</span></a>
|
|
<h2>Video brand headline</h2>
|
|
<div class="puis-card-container">
|
|
<div class="puisg-row">
|
|
<article data-asin="B000000008">
|
|
<h3>Sponsored Brands video product</h3>
|
|
<a href="/dp/B000000008"><img src="https://example.com/brand-video.jpg" /></a>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section style="display: none">
|
|
<span>Sponsored</span>
|
|
<article data-asin="B000000009">
|
|
<h3 aria-label="Sponsored Ad - hidden preloaded product">Hidden preloaded product</h3>
|
|
<a href="/sspa/click?url=%2Fdp%2FB000000009">
|
|
<img src="https://example.com/hidden.jpg" />
|
|
</a>
|
|
</article>
|
|
</section>
|
|
|
|
<section aria-hidden="true">
|
|
<span>Sponsored</span>
|
|
<article data-asin="B000000010">
|
|
<h3>Hidden accessibility product</h3>
|
|
<a href="/dp/B000000010"><img src="https://example.com/hidden-aria.jpg" /></a>
|
|
</article>
|
|
</section>
|
|
</div>
|
|
|
|
<aside id="rightResultsATF" data-component-type="sponsored-display">
|
|
<span>Sponsored</span>
|
|
<article data-asin="B000000006">
|
|
<h3>Display product</h3>
|
|
<a href="/dp/B000000006"><img src="https://example.com/display.jpg" /></a>
|
|
</article>
|
|
</aside>
|
|
</main>
|
|
`;
|
|
|
|
const dom = new JSDOM(html, { url: 'https://www.amazon.com/s?k=mirror' });
|
|
Object.assign(globalThis, {
|
|
document: dom.window.document,
|
|
Node: dom.window.Node,
|
|
Element: dom.window.Element,
|
|
HTMLElement: dom.window.HTMLElement,
|
|
HTMLAnchorElement: dom.window.HTMLAnchorElement,
|
|
HTMLImageElement: dom.window.HTMLImageElement,
|
|
});
|
|
|
|
async function main() {
|
|
const items = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
assert.equal(items.length, 8);
|
|
|
|
const byAsin = new Map(items.map((item) => [item.asin, item]));
|
|
assert.equal(byAsin.get('B000000003')?.adType, 'organic');
|
|
assert.equal(byAsin.get('B000000004')?.adType, 'sponsoredProducts');
|
|
assert.equal(byAsin.get('B000000004')?.price, '$33.93');
|
|
assert.equal(byAsin.get('B000000004')?.strikethroughPrice, '$39.99');
|
|
assert.equal(byAsin.get('B000000004')?.link, 'https://www.amazon.com/dp/B000000004');
|
|
assert.equal(byAsin.get('B000000004')?.placement, 'middleOfSearch');
|
|
assert.equal(byAsin.get('B000000001')?.adType, 'sponsoredBrands');
|
|
assert.equal(byAsin.get('B000000002')?.adType, 'sponsoredBrands');
|
|
assert.equal(byAsin.get('B000000001')?.placement, 'topOfSearch');
|
|
assert.equal(byAsin.get('B000000001')?.placementId, byAsin.get('B000000002')?.placementId);
|
|
assert.equal(byAsin.get('B000000001')?.adProductIndex, 1);
|
|
assert.equal(byAsin.get('B000000002')?.adProductIndex, 2);
|
|
assert.equal(byAsin.get('B000000005')?.adType, 'sponsoredProducts');
|
|
assert.equal(byAsin.get('B000000005')?.placement, 'bottomOfSearch');
|
|
assert.equal(byAsin.get('B000000006')?.adType, 'sponsoredDisplay');
|
|
assert.equal(byAsin.get('B000000006')?.placement, 'sidebar');
|
|
assert.equal(byAsin.get('B000000008')?.adType, 'sponsoredBrands');
|
|
assert.equal(byAsin.get('B000000008')?.adFormat, 'video');
|
|
assert.equal(byAsin.get('B000000008')?.placement, 'bottomOfSearch');
|
|
assert.equal(items.filter((item) => item.asin === 'B000000008').length, 1);
|
|
assert.equal(byAsin.has('B000000009'), false);
|
|
assert.equal(byAsin.has('B000000010'), false);
|
|
assert.deepEqual(
|
|
items.map((item) => item.pageRank),
|
|
[1, 2, 3, 4, 5, 6, 7, 8],
|
|
);
|
|
|
|
const hiddenDisclosureDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<article data-component-type="s-search-result" data-asin="B000000011">
|
|
<h2>Visible organic product</h2>
|
|
<span
|
|
aria-label="Sponsored Ad - hidden disclosure"
|
|
style="position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0)"
|
|
>Sponsored</span>
|
|
<a href="/sspa/click?url=%2Fdp%2FB000000011">
|
|
<img class="s-image" src="https://example.com/visible-product.jpg" />
|
|
</a>
|
|
</article>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=mirror' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: hiddenDisclosureDom.window.document,
|
|
Node: hiddenDisclosureDom.window.Node,
|
|
Element: hiddenDisclosureDom.window.Element,
|
|
HTMLElement: hiddenDisclosureDom.window.HTMLElement,
|
|
HTMLAnchorElement: hiddenDisclosureDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: hiddenDisclosureDom.window.HTMLImageElement,
|
|
});
|
|
const hiddenDisclosureItems = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
assert.equal(hiddenDisclosureItems.length, 1);
|
|
assert.equal(hiddenDisclosureItems[0].adType, 'organic');
|
|
|
|
const groupedAdsDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<section class="shared-sponsored-products">
|
|
<span aria-label="Sponsored">Sponsored</span>
|
|
<a href="/stores/reolink" style="display: none">Hidden brand destination</a>
|
|
<article data-asin="B000000012">
|
|
<h3>Grouped sponsored product one</h3>
|
|
<a href="/dp/B000000012"><img src="https://example.com/grouped-1.jpg" /></a>
|
|
</article>
|
|
<article data-asin="B000000013">
|
|
<h3>Grouped sponsored product two</h3>
|
|
<a href="/dp/B000000013"><img src="https://example.com/grouped-2.jpg" /></a>
|
|
</article>
|
|
</section>
|
|
</div>
|
|
<aside class="left-sponsored-display" data-component-type="sponsored-display">
|
|
<span aria-label="Sponsored">Sponsored</span>
|
|
<article>
|
|
<h3>Left rail display product</h3>
|
|
<a href="/dp/B000000014"><img src="https://example.com/display-left.jpg" /></a>
|
|
</article>
|
|
</aside>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=mirror&page=8' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: groupedAdsDom.window.document,
|
|
Node: groupedAdsDom.window.Node,
|
|
Element: groupedAdsDom.window.Element,
|
|
HTMLElement: groupedAdsDom.window.HTMLElement,
|
|
HTMLAnchorElement: groupedAdsDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: groupedAdsDom.window.HTMLImageElement,
|
|
});
|
|
const groupedAds = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const groupedAdsByAsin = new Map(groupedAds.map((item) => [item.asin, item]));
|
|
assert.equal(groupedAdsByAsin.get('B000000012')?.adType, 'sponsoredProducts');
|
|
assert.equal(groupedAdsByAsin.get('B000000013')?.adType, 'sponsoredProducts');
|
|
assert.equal(
|
|
groupedAdsByAsin.get('B000000012')?.placementId,
|
|
groupedAdsByAsin.get('B000000013')?.placementId,
|
|
);
|
|
assert.equal(groupedAdsByAsin.get('B000000012')?.adProductIndex, 1);
|
|
assert.equal(groupedAdsByAsin.get('B000000013')?.adProductIndex, 2);
|
|
assert.equal(groupedAdsByAsin.get('B000000014')?.adType, 'sponsoredDisplay');
|
|
assert.equal(groupedAdsByAsin.get('B000000014')?.placement, 'sidebar');
|
|
|
|
const liveSponsoredBrandsDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<div class="s-widget AdHolder" data-cel-widget="CardInstanceArlo">
|
|
<div class="sb-headline">Arlo Security for any Home</div>
|
|
<span>Sponsored</span>
|
|
<div data-asin="B08AR2S6MC">
|
|
<span>Video Doorbell</span>
|
|
<a href="/sspa/click?ie=UTF8&url=%2Fdp%2FB08AR2S6MC">
|
|
<img data-src="https://example.com/arlo-1.jpg" src="https://example.com/arlo-1.jpg" />
|
|
</a>
|
|
</div>
|
|
<div data-asin="B0ARLO0002">
|
|
<span>Outdoor Camera</span>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0ARLO0002">
|
|
<img src="https://example.com/arlo-2.jpg" />
|
|
</a>
|
|
</div>
|
|
<div data-asin="B0ARLO0003">
|
|
<span>Bundle</span>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0ARLO0003">
|
|
<img src="https://example.com/arlo-3.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<article data-component-type="s-search-result" data-asin="B09RING001">
|
|
<span>Sponsored</span>
|
|
<h2>Ring Spotlight Cam Plus, Battery</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB09RING001"><img class="s-image" src="https://example.com/ring.jpg" /></a>
|
|
</article>
|
|
<article data-component-type="s-search-result" data-asin="B09ORG0001">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B09ORG0001"><img class="s-image" src="https://example.com/org.jpg" /></a>
|
|
</article>
|
|
<div class="sbv-container AdHolder">
|
|
<span>Sponsored</span>
|
|
<video></video>
|
|
<div class="sbv-copy">PROTECT YOUR EQUIPMENT & FACILITIES</div>
|
|
<div data-asin="B0VOSKER01" class="sbv-product">
|
|
<h2>Vosker VKX Solar Powered 4G-LTE Outdoor Cellular Security Camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0VOSKER01"><img src="https://example.com/vosker.jpg" /></a>
|
|
<span class="a-price"><span class="a-offscreen">$379.99</span></span>
|
|
</div>
|
|
</div>
|
|
<section class="brands-related-to-your-search AdHolder">
|
|
<h2>Brands related to your search</h2>
|
|
<span>Sponsored</span>
|
|
<div class="brand-card" data-asin="B0BRAND001">
|
|
<img src="https://example.com/vosker-brand.jpg" alt="VOSKER" />
|
|
<a href="/sspa/click?url=%2Fdp%2FB0BRAND001">VOSKER</a>
|
|
</div>
|
|
<div class="brand-card">
|
|
<img src="https://example.com/reolink-brand.jpg" alt="Reolink" />
|
|
<a href="/sspa/click?url=%2Fgp%2Fproduct%2FB0BRAND002">Reolink</a>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=home+security' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: liveSponsoredBrandsDom.window.document,
|
|
Node: liveSponsoredBrandsDom.window.Node,
|
|
Element: liveSponsoredBrandsDom.window.Element,
|
|
HTMLElement: liveSponsoredBrandsDom.window.HTMLElement,
|
|
HTMLAnchorElement: liveSponsoredBrandsDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: liveSponsoredBrandsDom.window.HTMLImageElement,
|
|
});
|
|
const liveSponsoredBrands = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const liveByAsin = new Map(liveSponsoredBrands.map((item) => [item.asin, item]));
|
|
assert.equal(liveByAsin.get('B08AR2S6MC')?.adType, 'sponsoredBrands');
|
|
assert.equal(liveByAsin.get('B0ARLO0002')?.adType, 'sponsoredBrands');
|
|
assert.equal(liveByAsin.get('B0ARLO0003')?.adType, 'sponsoredBrands');
|
|
assert.equal(liveByAsin.get('B08AR2S6MC')?.placement, 'topOfSearch');
|
|
assert.equal(liveByAsin.get('B09RING001')?.adType, 'sponsoredProducts');
|
|
assert.equal(liveByAsin.get('B09ORG0001')?.adType, 'organic');
|
|
assert.equal(liveByAsin.get('B0VOSKER01')?.adType, 'sponsoredBrands');
|
|
assert.equal(liveByAsin.get('B0VOSKER01')?.adFormat, 'video');
|
|
assert.equal(liveByAsin.get('B0VOSKER01')?.placement, 'bottomOfSearch');
|
|
assert.equal(liveByAsin.get('B0BRAND001')?.adType, 'sponsoredBrands');
|
|
assert.equal(liveByAsin.get('B0BRAND002')?.adType, 'sponsoredBrands');
|
|
|
|
const lazyImageDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<div class="s-widget AdHolder">
|
|
<div class="sb-headline">Arlo Security for any Home</div>
|
|
<span>Sponsored</span>
|
|
<div data-asin="B08AR2LAZY">
|
|
<h3>Video Doorbell</h3>
|
|
<a href="/sspa/click?url=%2Fdp%2FB08AR2LAZY">
|
|
<img data-src="https://example.com/arlo-lazy.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=home+security' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: lazyImageDom.window.document,
|
|
Node: lazyImageDom.window.Node,
|
|
Element: lazyImageDom.window.Element,
|
|
HTMLElement: lazyImageDom.window.HTMLElement,
|
|
HTMLAnchorElement: lazyImageDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: lazyImageDom.window.HTMLImageElement,
|
|
});
|
|
const lazyImageItems = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
assert.equal(lazyImageItems.length, 1);
|
|
assert.equal(lazyImageItems[0]?.asin, 'B08AR2LAZY');
|
|
assert.equal(lazyImageItems[0]?.adType, 'sponsoredBrands');
|
|
assert.equal(lazyImageItems[0]?.imageSrc, 'https://example.com/arlo-lazy.jpg');
|
|
|
|
const classificationBoundaryDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<div class="s-widget AdHolder" data-cel-widget="CardInstanceArlo">
|
|
<div class="sb-headline">Arlo Security for any Home</div>
|
|
<span>Sponsored</span>
|
|
<div data-asin="B08ARLOSB1">
|
|
<span>Video Doorbell</span>
|
|
<a href="/sspa/click?url=%2Fdp%2FB08ARLOSB1">
|
|
<img src="https://example.com/arlo-banner-1.jpg" />
|
|
</a>
|
|
</div>
|
|
<div data-asin="B08ARLOSB2">
|
|
<span>Outdoor Camera</span>
|
|
<a href="/sspa/click?url=%2Fdp%2FB08ARLOSB2">
|
|
<img src="https://example.com/arlo-banner-2.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="s-widget AdHolder" data-cel-widget="search-grid-wrap">
|
|
<article data-component-type="s-search-result" data-asin="B0ARLOGRID" class="AdHolder">
|
|
<span>Sponsored</span>
|
|
<h2>Arlo Video Doorbell Wired</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0ARLOGRID">
|
|
<img class="s-image" src="https://example.com/arlo-grid.jpg" />
|
|
</a>
|
|
</article>
|
|
<article data-component-type="s-search-result" data-asin="B09RINGSP1" class="AdHolder">
|
|
<span>Sponsored</span>
|
|
<h2>Ring Spotlight Cam Plus, Battery</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB09RINGSP1">
|
|
<img class="s-image" src="https://example.com/ring-grid.jpg" />
|
|
</a>
|
|
</article>
|
|
<article data-component-type="s-search-result" data-asin="B09ORGKEEP">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B09ORGKEEP"><img class="s-image" src="https://example.com/org-keep.jpg" /></a>
|
|
</article>
|
|
</div>
|
|
|
|
<article data-component-type="s-search-result" data-asin="B0TRACKSP1" class="AdHolder">
|
|
<span>Sponsored</span>
|
|
<h2>Arlo Go 2 LTE Security Camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0TRACKSP1">
|
|
<img class="s-image" src="https://example.com/arlo-track.jpg" />
|
|
</a>
|
|
<a href="/gp/product/B0TRACKXX2">related tracking</a>
|
|
</article>
|
|
|
|
<article data-component-type="s-search-result" data-asin="B0DEMOSP01" class="AdHolder">
|
|
<span>Sponsored</span>
|
|
<h2>Ring Indoor Cam with demo</h2>
|
|
<video></video>
|
|
<a href="/dp/B0DEMOSP01">
|
|
<img class="s-image" src="https://example.com/ring-demo.jpg" />
|
|
</a>
|
|
</article>
|
|
|
|
<div class="sbv-container AdHolder">
|
|
<span>Sponsored</span>
|
|
<video></video>
|
|
<div class="sbv-copy">PROTECT YOUR EQUIPMENT & FACILITIES</div>
|
|
<div data-asin="B0VOSKER02" class="sbv-product">
|
|
<h2>Vosker VKX Solar Powered 4G-LTE Outdoor Cellular Security Camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0VOSKER02">
|
|
<img src="https://example.com/vosker-2.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<article data-component-type="s-search-result" data-asin="B0NOADVID1">
|
|
<h2>Organic with product video</h2>
|
|
<video></video>
|
|
<a href="/dp/B0NOADVID1">
|
|
<img class="s-image" src="https://example.com/organic-video.jpg" />
|
|
</a>
|
|
</article>
|
|
|
|
<section class="brands-related-to-your-search AdHolder">
|
|
<h2>Brands related to your search</h2>
|
|
<span>Sponsored</span>
|
|
<div class="brand-card" data-asin="B0BRANDWL1">
|
|
<img src="https://example.com/vosker-wall.jpg" alt="VOSKER" />
|
|
<a href="/sspa/click?url=%2Fdp%2FB0BRANDWL1">VOSKER</a>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
<aside id="rightResultsATF" data-component-type="sponsored-display">
|
|
<span>Sponsored</span>
|
|
<article data-asin="B0SIDEBAR1">
|
|
<h3>Sidebar display product</h3>
|
|
<a href="/dp/B0SIDEBAR1"><img src="https://example.com/sidebar.jpg" /></a>
|
|
</article>
|
|
</aside>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=home+security' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: classificationBoundaryDom.window.document,
|
|
Node: classificationBoundaryDom.window.Node,
|
|
Element: classificationBoundaryDom.window.Element,
|
|
HTMLElement: classificationBoundaryDom.window.HTMLElement,
|
|
HTMLAnchorElement: classificationBoundaryDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: classificationBoundaryDom.window.HTMLImageElement,
|
|
});
|
|
const classificationBoundary = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const boundaryByAsin = new Map(classificationBoundary.map((item) => [item.asin, item]));
|
|
assert.equal(boundaryByAsin.get('B08ARLOSB1')?.adType, 'sponsoredBrands');
|
|
assert.equal(boundaryByAsin.get('B08ARLOSB2')?.adType, 'sponsoredBrands');
|
|
assert.equal(boundaryByAsin.get('B0ARLOGRID')?.adType, 'sponsoredProducts');
|
|
assert.equal(boundaryByAsin.get('B09RINGSP1')?.adType, 'sponsoredProducts');
|
|
assert.equal(boundaryByAsin.get('B09ORGKEEP')?.adType, 'organic');
|
|
assert.equal(boundaryByAsin.get('B0TRACKSP1')?.adType, 'sponsoredProducts');
|
|
assert.equal(boundaryByAsin.has('B0TRACKXX2'), false);
|
|
assert.equal(boundaryByAsin.get('B0DEMOSP01')?.adType, 'sponsoredProducts');
|
|
assert.equal(boundaryByAsin.get('B0VOSKER02')?.adType, 'sponsoredBrands');
|
|
assert.equal(boundaryByAsin.get('B0VOSKER02')?.adFormat, 'video');
|
|
assert.equal(boundaryByAsin.get('B0NOADVID1')?.adType, 'organic');
|
|
assert.equal(boundaryByAsin.get('B0BRANDWL1')?.adType, 'sponsoredBrands');
|
|
assert.equal(boundaryByAsin.get('B0SIDEBAR1')?.adType, 'sponsoredDisplay');
|
|
assert.equal(boundaryByAsin.get('B0SIDEBAR1')?.placement, 'sidebar');
|
|
|
|
const thematicLoomDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<article data-component-type="s-search-result" data-asin="B0ORGLOOM1">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B0ORGLOOM1"><img class="s-image" src="https://example.com/org-loom.jpg" /></a>
|
|
</article>
|
|
|
|
<div
|
|
class="s-widget-container celwidget slot=MAIN template=FEATURED_ASINS_LIST widget=loom-desktop-bottom-slot_sptw-trending-hardlines-bottom-na-page2plus-t1"
|
|
cel_widget_id="MAIN-FEATURED_ASINS_LIST-22"
|
|
>
|
|
<div class="a-section s-include-content-margin">
|
|
<h2 id="loom-desktop-bottom-slot_featuredasins-heading">Trending now</h2>
|
|
<span
|
|
data-action="multi-ad-feedback-form-trigger"
|
|
data-multi-ad-feedback-form-trigger='{"adPlacementMetaData":{"slotName":"sp_search_thematic_btf-trending-hardlines","feedbackType":"sponsoredProductsLoom"}}'
|
|
>
|
|
<a aria-label="View Sponsored information or leave ad feedback" class="s-widget-sponsored-label-text">Sponsored</a>
|
|
</span>
|
|
<span data-component-type="s-searchgrid-carousel" class="s-searchgrid-carousel">
|
|
<ol class="a-carousel">
|
|
<li class="a-carousel-card" aria-hidden="false">
|
|
<div data-asin="B0B2MPYRKP" class="s-result-item">
|
|
<div class="puis-card-container">
|
|
<h2>iulock Keypad Smart Door Lock</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0B2MPYRKP&widgetName=sp_search_thematic_btf">
|
|
<img class="s-image" src="https://example.com/iulock.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="a-carousel-card" aria-hidden="false">
|
|
<div data-asin="B0F8HS3RRT" class="s-result-item">
|
|
<div class="puis-card-container">
|
|
<h2>Richelock fingerprint lock</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0F8HS3RRT&widgetName=sp_search_thematic_btf">
|
|
<img class="s-image" src="https://example.com/richelock.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="a-carousel-card" aria-hidden="false">
|
|
<div data-asin="B0GQX6GHK2" class="s-result-item">
|
|
<div class="puis-card-container">
|
|
<h2>Swann MaxRanger4K</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0GQX6GHK2&widgetName=sp_search_thematic_btf">
|
|
<img class="s-image" src="https://example.com/swann.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="a-carousel-card" aria-hidden="false" style="visibility: hidden;">
|
|
<div data-asin="B0CN95HR9V" class="s-result-item">
|
|
<div class="puis-card-container">
|
|
<h2>Solar Fence Lights</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0CN95HR9V&widgetName=sp_search_thematic_btf">
|
|
<img class="s-image" src="https://example.com/solar.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="a-carousel-card" aria-hidden="true" style="visibility: hidden;">
|
|
<div data-asin="B07M68S376" class="s-result-item">
|
|
<div class="puis-card-container">
|
|
<h2>SECRUI Wireless Doorbell</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB07M68S376&widgetName=sp_search_thematic_btf">
|
|
<img class="s-image" src="https://example.com/secrui.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</ol>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sbv-container AdHolder">
|
|
<span>Sponsored</span>
|
|
<video></video>
|
|
<div class="sbv-copy">PROTECT YOUR EQUIPMENT & FACILITIES</div>
|
|
<div data-asin="B0VOSKERLM" class="sbv-product">
|
|
<h2>Vosker VKX Solar Powered camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0VOSKERLM"><img src="https://example.com/vosker-loom.jpg" /></a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="s-widget AdHolder" data-cel-widget="CardInstanceArlo">
|
|
<div class="sb-headline">Arlo Security for any Home</div>
|
|
<span>Sponsored</span>
|
|
<div data-asin="B08ARLOLM1">
|
|
<span>Video Doorbell</span>
|
|
<a href="/sspa/click?url=%2Fdp%2FB08ARLOLM1">
|
|
<img src="https://example.com/arlo-loom.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=home+security' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: thematicLoomDom.window.document,
|
|
Node: thematicLoomDom.window.Node,
|
|
Element: thematicLoomDom.window.Element,
|
|
HTMLElement: thematicLoomDom.window.HTMLElement,
|
|
HTMLAnchorElement: thematicLoomDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: thematicLoomDom.window.HTMLImageElement,
|
|
});
|
|
const thematicLoom = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const thematicByAsin = new Map(thematicLoom.map((item) => [item.asin, item]));
|
|
assert.equal(thematicByAsin.get('B0ORGLOOM1')?.adType, 'organic');
|
|
assert.equal(thematicByAsin.get('B0B2MPYRKP')?.adType, 'sponsoredProducts');
|
|
assert.equal(thematicByAsin.get('B0F8HS3RRT')?.adType, 'sponsoredProducts');
|
|
assert.equal(thematicByAsin.get('B0GQX6GHK2')?.adType, 'sponsoredProducts');
|
|
assert.equal(
|
|
thematicByAsin.get('B0B2MPYRKP')?.placementId,
|
|
thematicByAsin.get('B0F8HS3RRT')?.placementId,
|
|
);
|
|
assert.equal(thematicByAsin.get('B0B2MPYRKP')?.adProductIndex, 1);
|
|
assert.equal(thematicByAsin.get('B0F8HS3RRT')?.adProductIndex, 2);
|
|
assert.equal(thematicByAsin.get('B0GQX6GHK2')?.adProductIndex, 3);
|
|
assert.equal(thematicByAsin.has('B0CN95HR9V'), false);
|
|
assert.equal(thematicByAsin.has('B07M68S376'), false);
|
|
assert.equal(thematicByAsin.get('B0VOSKERLM')?.adType, 'sponsoredBrands');
|
|
assert.equal(thematicByAsin.get('B0VOSKERLM')?.adFormat, 'video');
|
|
assert.equal(thematicByAsin.get('B08ARLOLM1')?.adType, 'sponsoredBrands');
|
|
|
|
const organicLoomDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<article data-component-type="s-search-result" data-asin="B0ORGCFV01">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B0ORGCFV01"><img class="s-image" src="https://example.com/org-cfv.jpg" /></a>
|
|
</article>
|
|
|
|
<div
|
|
class="s-widget-container celwidget slot=MAIN template=FEATURED_ASINS_LIST widget=loom-desktop-bottom-slot_sptw-frequently-viewed-bottom-na"
|
|
cel_widget_id="MAIN-FEATURED_ASINS_LIST-23"
|
|
>
|
|
<div class="a-section s-include-content-margin">
|
|
<h2 id="loom-desktop-bottom-slot_featuredasins-heading">Customers frequently viewed</h2>
|
|
<span
|
|
data-action="multi-ad-feedback-form-trigger"
|
|
data-multi-ad-feedback-form-trigger='{"adPlacementMetaData":{"slotName":"sp_search_thematic_btf-frequently-viewed","feedbackType":"sponsoredProductsLoom"}}'
|
|
></span>
|
|
<span
|
|
aria-label="Sponsored"
|
|
style="position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0)"
|
|
>Sponsored</span>
|
|
<span data-component-type="s-searchgrid-carousel" class="s-searchgrid-carousel">
|
|
<ol class="a-carousel">
|
|
<li class="a-carousel-card" aria-hidden="false">
|
|
<div data-asin="B0CFV00001" class="s-result-item">
|
|
<div class="puis-card-container">
|
|
<h2>ANNKE security camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0CFV00001&widgetName=sp_search_thematic_btf">
|
|
<img class="s-image" src="https://example.com/annke.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="a-carousel-card" aria-hidden="false">
|
|
<div data-asin="B0CFV00002" class="s-result-item">
|
|
<div class="puis-card-container">
|
|
<h2>Night Owl camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0CFV00002&widgetName=sp_search_thematic_btf">
|
|
<img class="s-image" src="https://example.com/nightowl.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</ol>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="s-widget-container celwidget slot=MAIN template=FEATURED_ASINS_LIST widget=loom-desktop-bottom-slot_sptw-recently-bought-bottom-na"
|
|
cel_widget_id="MAIN-FEATURED_ASINS_LIST-24"
|
|
>
|
|
<h2>Recently bought and rated</h2>
|
|
<span data-component-type="s-searchgrid-carousel" class="s-searchgrid-carousel">
|
|
<ol class="a-carousel">
|
|
<li class="a-carousel-card" aria-hidden="false">
|
|
<div data-asin="B0RBR00001" class="s-result-item">
|
|
<div class="puis-card-container">
|
|
<h2>Rated doorbell</h2>
|
|
<a href="/dp/B0RBR00001">
|
|
<img class="s-image" src="https://example.com/rated.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</ol>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=home+security' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: organicLoomDom.window.document,
|
|
Node: organicLoomDom.window.Node,
|
|
Element: organicLoomDom.window.Element,
|
|
HTMLElement: organicLoomDom.window.HTMLElement,
|
|
HTMLAnchorElement: organicLoomDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: organicLoomDom.window.HTMLImageElement,
|
|
});
|
|
const organicLoom = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const organicLoomByAsin = new Map(organicLoom.map((item) => [item.asin, item]));
|
|
assert.equal(organicLoomByAsin.get('B0ORGCFV01')?.adType, 'organic');
|
|
assert.equal(organicLoomByAsin.get('B0CFV00001')?.adType, 'organic');
|
|
assert.equal(organicLoomByAsin.get('B0CFV00002')?.adType, 'organic');
|
|
assert.equal(organicLoomByAsin.get('B0RBR00001')?.adType, 'organic');
|
|
assert.equal(
|
|
organicLoom.some((item) => item.adType === 'sponsoredProducts'),
|
|
false,
|
|
);
|
|
|
|
const organicVideoStripDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<article data-component-type="s-search-result" data-asin="B0ORGVID01">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B0ORGVID01"><img class="s-image" src="https://example.com/org-vid.jpg" /></a>
|
|
</article>
|
|
|
|
<div class="sbv-container AdHolder">
|
|
<video></video>
|
|
<a href="/stores/chamberlain"><span class="brand-logo">Chamberlain</span></a>
|
|
<div class="sb-headline">Chamberlain myQ</div>
|
|
<div class="sbv-copy">Control your garage from anywhere</div>
|
|
<div data-asin="B0NOSBV001" class="sbv-product">
|
|
<h2>Chamberlain garage opener</h2>
|
|
<a href="/dp/B0NOSBV001"><img src="https://example.com/chamberlain-nosponsored.jpg" /></a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sbv-container AdHolder">
|
|
<span
|
|
aria-label="Sponsored"
|
|
style="position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0)"
|
|
>Sponsored</span>
|
|
<video></video>
|
|
<a href="/stores/vosker"><span class="brand-logo">Vosker</span></a>
|
|
<div class="sbv-copy">PROTECT YOUR EQUIPMENT</div>
|
|
<div data-asin="B0HIDDENSB" class="sbv-product">
|
|
<h2>Vosker solar camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0HIDDENSB"><img src="https://example.com/vosker-hidden.jpg" /></a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sbv-container AdHolder">
|
|
<span>Sponsored</span>
|
|
<video></video>
|
|
<a href="/stores/vosker"><span class="brand-logo">Vosker</span></a>
|
|
<div class="sbv-copy">PROTECT YOUR EQUIPMENT</div>
|
|
<div data-asin="B0YESBV001" class="sbv-product">
|
|
<h2>Vosker with visible sponsored</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0YESBV001"><img src="https://example.com/vosker-yes.jpg" /></a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=home+security' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: organicVideoStripDom.window.document,
|
|
Node: organicVideoStripDom.window.Node,
|
|
Element: organicVideoStripDom.window.Element,
|
|
HTMLElement: organicVideoStripDom.window.HTMLElement,
|
|
HTMLAnchorElement: organicVideoStripDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: organicVideoStripDom.window.HTMLImageElement,
|
|
});
|
|
const organicVideoStrip = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const videoStripByAsin = new Map(organicVideoStrip.map((item) => [item.asin, item]));
|
|
assert.equal(videoStripByAsin.get('B0ORGVID01')?.adType, 'organic');
|
|
assert.equal(videoStripByAsin.get('B0NOSBV001')?.adType, 'organic');
|
|
assert.equal(videoStripByAsin.get('B0HIDDENSB')?.adType, 'organic');
|
|
assert.equal(videoStripByAsin.get('B0YESBV001')?.adType, 'sponsoredBrands');
|
|
assert.equal(videoStripByAsin.get('B0YESBV001')?.adFormat, 'video');
|
|
assert.deepEqual(
|
|
organicVideoStrip.filter((item) => item.adType === 'sponsoredBrands').map((item) => item.asin),
|
|
['B0YESBV001'],
|
|
);
|
|
|
|
const liveBrandWallDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<article data-component-type="s-search-result" data-asin="B0ORGGRID1">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B0ORGGRID1"><img class="s-image" src="https://example.com/org-grid.jpg" /></a>
|
|
</article>
|
|
|
|
<div
|
|
class="s-result-item s-widget AdHolder s-flex-full-width"
|
|
data-asin=""
|
|
cel_widget_id="multi-brand-creative-desktop_loom-desktop-brand-footer-slot_46"
|
|
>
|
|
<span aria-label="Brands related to your search" role="heading">Brands related to your search</span>
|
|
<span aria-label="Leave feedback on Sponsored ad" tabindex="0" role="button">Sponsored</span>
|
|
<div class="sbx-desktop" data-aid="301209426575402" data-slot="desktop-hsa-3psl">
|
|
<a href="/stores/page/VOSKER?lp_asins=B0D5Z3V464%2CB0FY2QCXRJ%2CB0FY2MRK56&lp_query=home%20security&lp_slot=desktop-hsa-3psl">
|
|
<img
|
|
alt="Vosker"
|
|
src="https://images-na.ssl-images-amazon.com/images/G/01/x-locale/common/grey-pixel.gif"
|
|
data-src="https://example.com/vosker-wall.jpg"
|
|
/>
|
|
</a>
|
|
</div>
|
|
<div class="sbx-desktop" data-aid="301252396855702" data-slot="desktop-hsa-3psl">
|
|
<a href="/stores/page/REOLINK?lp_asins=B0C2PY2KTJ%2CB0FM45MSS8&lp_slot=desktop-hsa-3psl">
|
|
<img
|
|
alt="REOLINK"
|
|
src="https://images-na.ssl-images-amazon.com/images/G/01/x-locale/common/grey-pixel.gif"
|
|
data-src="https://example.com/reolink-wall.jpg"
|
|
/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=home+security' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: liveBrandWallDom.window.document,
|
|
Node: liveBrandWallDom.window.Node,
|
|
Element: liveBrandWallDom.window.Element,
|
|
HTMLElement: liveBrandWallDom.window.HTMLElement,
|
|
HTMLAnchorElement: liveBrandWallDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: liveBrandWallDom.window.HTMLImageElement,
|
|
});
|
|
const liveBrandWall = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const liveBrandWallByAsin = new Map(liveBrandWall.map((item) => [item.asin, item]));
|
|
assert.equal(liveBrandWallByAsin.get('B0ORGGRID1')?.adType, 'organic');
|
|
assert.equal(liveBrandWallByAsin.get('B0D5Z3V464')?.adType, 'sponsoredBrands');
|
|
assert.equal(liveBrandWallByAsin.get('B0C2PY2KTJ')?.adType, 'sponsoredBrands');
|
|
assert.equal(liveBrandWallByAsin.get('B0D5Z3V464')?.title, 'Vosker');
|
|
assert.equal(
|
|
liveBrandWallByAsin.get('B0D5Z3V464')?.imageSrc,
|
|
'https://example.com/vosker-wall.jpg',
|
|
);
|
|
assert.equal(
|
|
liveBrandWallByAsin.get('B0C2PY2KTJ')?.imageSrc,
|
|
'https://example.com/reolink-wall.jpg',
|
|
);
|
|
assert.equal(liveBrandWallByAsin.has('B0FY2QCXRJ'), false);
|
|
|
|
const influencerPicksDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<article data-component-type="s-search-result" data-asin="B0ORGINF01">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B0ORGINF01"><img class="s-image" src="https://example.com/org-inf.jpg" /></a>
|
|
</article>
|
|
|
|
<section class="s-widget-container celwidget">
|
|
<h2>Picks from Amazon Influencers</h2>
|
|
<span>Sponsored</span>
|
|
<span>From Amazon Influencer storefronts</span>
|
|
<div data-asin="B0ANNKE001" class="puis-card-container">
|
|
<h2>ANNKE 4K PoE Security Camera System</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0ANNKE001"><img src="https://example.com/annke.jpg" /></a>
|
|
</div>
|
|
<div data-asin="B0NIGHTO01" class="puis-card-container">
|
|
<h2>Night Owl DVR</h2>
|
|
<a href="/shop/nightowlstorefront"><span>storefront</span></a>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0NIGHTO01"><img src="https://example.com/nightowl.jpg" /></a>
|
|
</div>
|
|
<div data-asin="B0SOLAR001" class="puis-card-container">
|
|
<h2>Solar Street Lights</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0SOLAR001"><img src="https://example.com/solar.jpg" /></a>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="s-widget AdHolder">
|
|
<div class="creative-collage">
|
|
<img src="https://example.com/jobsite.jpg" alt="jobsite" />
|
|
<img src="https://example.com/camera-closeup.jpg" alt="camera" />
|
|
<button aria-label="Pause"></button>
|
|
<img src="https://example.com/packaging.jpg" alt="packaging" />
|
|
</div>
|
|
<div data-asin="B0VOSKERVK">
|
|
<span>Sponsored</span>
|
|
<h2>Vosker VKX Solar Powered 4G-LTE Outdoor Cellular Security Camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0VOSKERVK">
|
|
<img src="https://example.com/vosker-vkx.jpg" />
|
|
</a>
|
|
<span class="a-price"><span class="a-offscreen">$379.99</span></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sbv-container AdHolder">
|
|
<div class="sbv-creative">
|
|
<img src="https://example.com/jobsite2.jpg" alt="PROTECT YOUR INVESTMENT" />
|
|
<img src="https://example.com/closeup2.jpg" alt="CELLULAR SECURITY CAMERA" />
|
|
<button class="sbv-pause" aria-label="Pause"></button>
|
|
</div>
|
|
<div data-asin="B0VOSKERSB" class="sbv-product">
|
|
<span>Sponsored</span>
|
|
<h2>Vosker VKX Solar Powered camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0VOSKERSB">
|
|
<img src="https://example.com/vosker-sb.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<article data-component-type="s-search-result" data-asin="B0VIEWVOSK">
|
|
<span>Sponsored</span>
|
|
<span class="previously-viewed-badge">Previously viewed</span>
|
|
<h2>Vosker VKX Solar Powered 4G-LTE Outdoor Cellular Security Camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0VIEWVOSK">
|
|
<img class="s-image" src="https://example.com/vosker-grid.jpg" />
|
|
</a>
|
|
<span class="a-price"><span class="a-offscreen">$149.99</span></span>
|
|
</article>
|
|
<article data-component-type="s-search-result" data-asin="B0OWLLABS1">
|
|
<span>Sponsored</span>
|
|
<h2>Owl Labs Meeting Owl 3</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0OWLLABS1">
|
|
<img class="s-image" src="https://example.com/owl.jpg" />
|
|
</a>
|
|
<span class="a-price"><span class="a-offscreen">$1,099.00</span></span>
|
|
</article>
|
|
|
|
<div class="s-related-searches AdHolder">
|
|
<h2>Related searches</h2>
|
|
<a href="/s?k=home+security+camera">home security camera</a>
|
|
<a href="/s?k=outdoor+camera">outdoor camera</a>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=home+security' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: influencerPicksDom.window.document,
|
|
Node: influencerPicksDom.window.Node,
|
|
Element: influencerPicksDom.window.Element,
|
|
HTMLElement: influencerPicksDom.window.HTMLElement,
|
|
HTMLAnchorElement: influencerPicksDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: influencerPicksDom.window.HTMLImageElement,
|
|
});
|
|
const influencerPicks = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const influencerByAsin = new Map(influencerPicks.map((item) => [item.asin, item]));
|
|
assert.equal(influencerByAsin.get('B0ORGINF01')?.adType, 'organic');
|
|
assert.equal(influencerByAsin.get('B0ANNKE001')?.adType, 'sponsoredProducts');
|
|
assert.equal(influencerByAsin.get('B0NIGHTO01')?.adType, 'sponsoredProducts');
|
|
assert.equal(influencerByAsin.get('B0SOLAR001')?.adType, 'sponsoredProducts');
|
|
assert.equal(
|
|
influencerByAsin.get('B0ANNKE001')?.placementId,
|
|
influencerByAsin.get('B0NIGHTO01')?.placementId,
|
|
);
|
|
assert.equal(influencerByAsin.get('B0VOSKERVK')?.adType, 'sponsoredBrands');
|
|
assert.equal(influencerByAsin.get('B0VOSKERVK')?.adFormat, 'standard');
|
|
assert.equal(influencerByAsin.get('B0VOSKERSB')?.adType, 'sponsoredBrands');
|
|
assert.equal(influencerByAsin.get('B0VIEWVOSK')?.adType, 'sponsoredProducts');
|
|
assert.equal(influencerByAsin.get('B0OWLLABS1')?.adType, 'sponsoredProducts');
|
|
assert.equal(
|
|
influencerPicks.some((item) => /related searches/i.test(item.title)),
|
|
false,
|
|
);
|
|
|
|
const shopByTypeDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<article data-component-type="s-search-result" data-asin="B0ORGCAM01">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B0ORGCAM01"><img class="s-image" src="https://example.com/org-cam.jpg" /></a>
|
|
</article>
|
|
|
|
<div class="sbv-container AdHolder">
|
|
<span>Sponsored</span>
|
|
<video></video>
|
|
<a href="/stores/stealthcam"><span class="brand-logo">STEALTH CAM</span></a>
|
|
<div class="sb-headline">STEALTH CAM</div>
|
|
<div data-asin="B0STEALTH1" class="sbv-product">
|
|
<h2>Stealth Cam trail camera</h2>
|
|
<a href="/sspa/click?url=%2Fdp%2FB0STEALTH1">
|
|
<img src="https://example.com/stealth.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="s-widget-container celwidget slot=MAIN template=SHOP_BY_TYPE widget=shop-by-type-desktop"
|
|
cel_widget_id="MAIN-SHOP_BY_TYPE-8"
|
|
>
|
|
<h2>Shop cameras by type</h2>
|
|
<span>Sponsored</span>
|
|
<div class="shop-by-type-column">
|
|
<h3>security system</h3>
|
|
<div>
|
|
<h2>Night Owl 2-Way Audio</h2>
|
|
<a href="https://redirect.amazon.com/x/c/abc123?url=%2Fdp%2FB0NIGHTOWL">
|
|
<img
|
|
src="https://images-na.ssl-images-amazon.com/images/G/01/x-locale/common/grey-pixel.gif"
|
|
data-src="https://example.com/nightowl-type.jpg"
|
|
/>
|
|
</a>
|
|
<span class="a-price"><span class="a-offscreen">$549.99</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="shop-by-type-column">
|
|
<h3>webcam</h3>
|
|
<div>
|
|
<h2>Prisual PTZ Camera</h2>
|
|
<a href="https://redirect.amazon.com/x/c/def456?url=%2Fdp%2FB0PRISUAL1">
|
|
<img
|
|
src="https://images-na.ssl-images-amazon.com/images/G/01/x-locale/common/grey-pixel.gif"
|
|
data-src="https://example.com/prisual-type.jpg"
|
|
/>
|
|
</a>
|
|
<span class="a-price"><span class="a-offscreen">$494.09</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="shop-by-type-column">
|
|
<h3>instant print</h3>
|
|
<div>
|
|
<h2>Kids Instant Print Camera</h2>
|
|
<a href="https://redirect.amazon.com/x/c/ghi789?url=%2Fdp%2FB0KIDINST1">
|
|
<img data-src="https://example.com/instant-type.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="shop-by-type-column">
|
|
<h3>point and shoot</h3>
|
|
<div>
|
|
<h2>Digital Camera 4K</h2>
|
|
<a href="https://redirect.amazon.com/x/c/jkl012?url=%2Fdp%2FB0POINTSH1">
|
|
<img data-src="https://example.com/pointshoot-type.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="shop-by-type-column">
|
|
<h3>film camera</h3>
|
|
<div>
|
|
<h2>Kodak EKTAR H35N</h2>
|
|
<a href="https://direct.amazon.com/x/c/QmToken/https://www.amazon.com/dp/B0KODAKH35">
|
|
<img data-src="https://example.com/kodak-type.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=camera' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: shopByTypeDom.window.document,
|
|
Node: shopByTypeDom.window.Node,
|
|
Element: shopByTypeDom.window.Element,
|
|
HTMLElement: shopByTypeDom.window.HTMLElement,
|
|
HTMLAnchorElement: shopByTypeDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: shopByTypeDom.window.HTMLImageElement,
|
|
});
|
|
const shopByType = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const shopByTypeByAsin = new Map(shopByType.map((item) => [item.asin, item]));
|
|
const shopByTypeAsins = ['B0NIGHTOWL', 'B0PRISUAL1', 'B0KIDINST1', 'B0POINTSH1', 'B0KODAKH35'];
|
|
const shopByTypeItems = shopByTypeAsins.map((asin) => shopByTypeByAsin.get(asin));
|
|
assert.ok(shopByTypeItems.filter(Boolean).length > 0, 'Shop by type cards must be collected');
|
|
assert.equal(shopByTypeItems.filter(Boolean).length, 5);
|
|
assert.equal(shopByTypeByAsin.get('B0NIGHTOWL')?.asin, 'B0NIGHTOWL');
|
|
assert.equal(shopByTypeByAsin.get('B0NIGHTOWL')?.title, 'Night Owl 2-Way Audio');
|
|
assert.equal(
|
|
shopByTypeByAsin.get('B0NIGHTOWL')?.imageSrc,
|
|
'https://example.com/nightowl-type.jpg',
|
|
);
|
|
assert.equal(shopByTypeByAsin.get('B0NIGHTOWL')?.price, '$549.99');
|
|
assert.equal(shopByTypeByAsin.get('B0PRISUAL1')?.asin, 'B0PRISUAL1');
|
|
assert.equal(shopByTypeByAsin.get('B0PRISUAL1')?.title, 'Prisual PTZ Camera');
|
|
assert.equal(shopByTypeByAsin.get('B0KIDINST1')?.asin, 'B0KIDINST1');
|
|
assert.equal(shopByTypeByAsin.get('B0KIDINST1')?.title, 'Kids Instant Print Camera');
|
|
assert.equal(shopByTypeByAsin.get('B0POINTSH1')?.asin, 'B0POINTSH1');
|
|
assert.equal(shopByTypeByAsin.get('B0POINTSH1')?.title, 'Digital Camera 4K');
|
|
assert.equal(shopByTypeByAsin.get('B0KODAKH35')?.asin, 'B0KODAKH35');
|
|
assert.equal(shopByTypeByAsin.get('B0KODAKH35')?.title, 'Kodak EKTAR H35N');
|
|
assert.ok(shopByTypeItems.every((item) => item?.asin && item.title && item.imageSrc));
|
|
assert.equal(shopByTypeByAsin.get('B0ORGCAM01')?.adType, 'organic');
|
|
assert.equal(shopByTypeByAsin.get('B0STEALTH1')?.adType, 'sponsoredBrands');
|
|
assert.equal(shopByTypeByAsin.get('B0STEALTH1')?.adFormat, 'video');
|
|
assert.ok(shopByTypeItems.every((item) => item?.adType === 'sponsoredProducts'));
|
|
assert.equal(
|
|
shopByTypeByAsin.get('B0NIGHTOWL')?.placementId,
|
|
shopByTypeByAsin.get('B0PRISUAL1')?.placementId,
|
|
);
|
|
assert.equal(shopByTypeByAsin.get('B0NIGHTOWL')?.adProductIndex, 1);
|
|
assert.equal(shopByTypeByAsin.get('B0PRISUAL1')?.adProductIndex, 2);
|
|
assert.equal(
|
|
shopByType.some((item) =>
|
|
/^(security system|webcam|instant print|point and shoot|film camera)$/i.test(item.title),
|
|
),
|
|
false,
|
|
);
|
|
|
|
const organicShopByTypeDom = new JSDOM(
|
|
`
|
|
<main id="search">
|
|
<div class="s-main-slot">
|
|
<article data-component-type="s-search-result" data-asin="B0ORGTYPE0">
|
|
<h2>Organic camera</h2>
|
|
<a href="/dp/B0ORGTYPE0"><img class="s-image" src="https://example.com/org-type0.jpg" /></a>
|
|
</article>
|
|
|
|
<div
|
|
class="s-widget-container celwidget slot=MAIN template=SHOP_BY_TYPE widget=shop-by-type-desktop"
|
|
cel_widget_id="MAIN-SHOP_BY_TYPE-9"
|
|
>
|
|
<h2>Shop cameras by type</h2>
|
|
<div class="shop-by-type-column">
|
|
<h3>webcam</h3>
|
|
<div data-asin="B0ORGTYPE1">
|
|
<h2>Organic type webcam</h2>
|
|
<a href="/dp/B0ORGTYPE1">
|
|
<img class="s-image" src="https://example.com/org-type1.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="shop-by-type-column">
|
|
<h3>film camera</h3>
|
|
<div>
|
|
<h2>Organic type film camera</h2>
|
|
<a href="https://redirect.amazon.com/x/c/org?url=%2Fdp%2FB0ORGTYPE2">
|
|
<img data-src="https://example.com/org-type2.jpg" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
`,
|
|
{ url: 'https://www.amazon.com/s?k=camera' },
|
|
);
|
|
Object.assign(globalThis, {
|
|
document: organicShopByTypeDom.window.document,
|
|
Node: organicShopByTypeDom.window.Node,
|
|
Element: organicShopByTypeDom.window.Element,
|
|
HTMLElement: organicShopByTypeDom.window.HTMLElement,
|
|
HTMLAnchorElement: organicShopByTypeDom.window.HTMLAnchorElement,
|
|
HTMLImageElement: organicShopByTypeDom.window.HTMLImageElement,
|
|
});
|
|
const organicShopByType = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
|
const organicShopByTypeByAsin = new Map(organicShopByType.map((item) => [item.asin, item]));
|
|
assert.equal(organicShopByTypeByAsin.get('B0ORGTYPE1')?.asin, 'B0ORGTYPE1');
|
|
assert.equal(organicShopByTypeByAsin.get('B0ORGTYPE1')?.title, 'Organic type webcam');
|
|
assert.equal(organicShopByTypeByAsin.get('B0ORGTYPE2')?.asin, 'B0ORGTYPE2');
|
|
assert.equal(organicShopByTypeByAsin.get('B0ORGTYPE2')?.title, 'Organic type film camera');
|
|
assert.equal(organicShopByTypeByAsin.get('B0ORGTYPE0')?.adType, 'organic');
|
|
assert.equal(organicShopByTypeByAsin.get('B0ORGTYPE1')?.adType, 'organic');
|
|
assert.equal(organicShopByTypeByAsin.get('B0ORGTYPE2')?.adType, 'organic');
|
|
assert.equal(
|
|
organicShopByType.some(
|
|
(item) => item.adType === 'sponsoredProducts' || item.adType === 'sponsoredBrands',
|
|
),
|
|
false,
|
|
);
|
|
|
|
console.log('Amazon search parser fixtures passed.');
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exitCode = 1;
|
|
});
|