utils.test.js
1 import { describe, expect, it } from 'vitest'; 2 import { canonicalizeProductUrl, dedupeSearchItems, normalizeProductId, normalizeSearchItem, sanitizeSearchItems, } from './utils.js'; 3 describe('normalizeProductId', () => { 4 it('extracts product id from canonical path', () => { 5 expect(normalizeProductId('https://www.coupang.com/vp/products/123456789')).toBe('123456789'); 6 }); 7 it('preserves numeric ids', () => { 8 expect(normalizeProductId('987654321')).toBe('987654321'); 9 }); 10 }); 11 describe('canonicalizeProductUrl', () => { 12 it('normalizes relative Coupang paths', () => { 13 expect(canonicalizeProductUrl('/vp/products/123456789?itemId=1', '')).toBe('https://www.coupang.com/vp/products/123456789'); 14 }); 15 it('builds url from product id', () => { 16 expect(canonicalizeProductUrl('', '123456789')).toBe('https://www.coupang.com/vp/products/123456789'); 17 }); 18 }); 19 describe('normalizeSearchItem', () => { 20 it('maps raw fields into compare-ready shape', () => { 21 const item = normalizeSearchItem({ 22 productId: '123456789', 23 productName: '무선 마우스', 24 salePrice: '29,900원', 25 originalPrice: '39,900원', 26 rating: '4.8', 27 reviewCount: '1,234', 28 sellerName: '쿠팡', 29 badge: ['ROCKET', 'TOMORROW', '무료배송'], 30 categoryName: 'PC', 31 url: '/vp/products/123456789?itemId=1', 32 }, 0); 33 expect(item).toMatchObject({ 34 rank: 1, 35 product_id: '123456789', 36 title: '무선 마우스', 37 price: 29900, 38 original_price: 39900, 39 rating: 4.8, 40 review_count: 1234, 41 rocket: '로켓배송', 42 delivery_type: '무료배송', 43 delivery_promise: '내일도착', 44 seller: '쿠팡', 45 category: 'PC', 46 url: 'https://www.coupang.com/vp/products/123456789', 47 }); 48 }); 49 }); 50 describe('sanitizeSearchItems', () => { 51 it('drops duplicates and invalid rows', () => { 52 const rows = [ 53 normalizeSearchItem({ productId: '1', productName: 'A', price: '1000', url: '/vp/products/1' }, 0), 54 normalizeSearchItem({ productId: '1', productName: 'A', price: '1000', url: '/vp/products/1' }, 1), 55 normalizeSearchItem({ productId: '', productName: '', price: '1000' }, 2), 56 normalizeSearchItem({ productId: '2', productName: 'B', price: '2000', url: '/vp/products/2' }, 3), 57 ]; 58 expect(dedupeSearchItems(rows)).toHaveLength(3); 59 expect(sanitizeSearchItems(rows, 10)).toHaveLength(2); 60 expect(sanitizeSearchItems(rows, 10).map(item => item.rank)).toEqual([1, 2]); 61 }); 62 });