Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 150 additions & 22 deletions apps/importer/src/shopee/__tests__/open-core-intelligence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ async function runSuite() {
['iPad Air 5 64GB', 'apple:ipad-air-5:64gb'],
['Apple iPad Air (5th generation) Wi-Fi 64GB', 'apple:ipad-air-5:64gb'],
['Apple iPad Air 6 128GB', 'apple:ipad-air-m2:128gb'],
['iPad Air 11 inch M2 128GB', 'apple:ipad-air-m2:128gb'],
['iPad Air 13 M3 256GB', 'apple:ipad-air-m3:256gb'],
['iPad Air 11 inch M2 128GB', 'apple:ipad-air-m2-11:128gb'],
['iPad Air 13 M3 256GB', 'apple:ipad-air-m3-13:256gb'],
['iPad Air 128GB', 'apple:ipad-air:128gb'],
['iPad Air 5 M1 64GB', 'apple:ipad-air-m1:64gb'],
['iPad Air M1 64GB', 'apple:ipad-air-m1:64gb'],
Expand All @@ -304,7 +304,7 @@ async function runSuite() {
['iPad 10 64GB so sánh với iPad A16', 'apple:ipad-10:64gb'],
['iPad 9 64GB chip A13 - A16 sắp về', 'apple:ipad-9:64gb'],
// "iPad 11 Pro" is the 11-inch Pro, not the 11th generation.
['iPad 11 Pro M4 256GB', 'apple:ipad-pro-m4:256gb'],
['iPad 11 Pro M4 256GB', 'apple:ipad-pro-m4-11:256gb'],
// Apple lines with no pattern of their own are not iPhones.
['Apple TV 4K (3rd generation) 128GB', 'apple:tv-4k-3:128gb'],
['Apple Vision Pro 256GB', 'apple:vision-pro:256gb'],
Expand Down Expand Up @@ -346,6 +346,134 @@ async function runSuite() {
for (const [title, expectedKey] of appleKeys819) {
assert.equal(keyOf(title), expectedKey, title);
}
// An Apple line sold in more than one size keys each size apart (DOS/Rate#1113).
// Labeled fixtures: title -> key.
const appleSizeKeys1113: Array<[string, string]> = [
// The names of the published masters key to their stored keys.
['Apple iPad Pro M5 11 inch 256GB', 'apple:ipad-pro-m5-11:256gb'],
['Apple MacBook Pro M4 Pro 14 inch 512GB', 'apple:macbook-pro-m4-pro-14:512gb'],
['Apple Watch Series 11 46mm GPS 64GB', 'apple:watch-series-11-46mm:64gb'],
// The other size is another product.
['iPad Pro M4 13 inch 512GB', 'apple:ipad-pro-m4-13:512gb'],
['iPad Pro 13" M5 1TB', 'apple:ipad-pro-m5-13:1024gb'],
['iPad Pro 11 2024 M4 256GB', 'apple:ipad-pro-m4-11:256gb'],
['iPad Pro 12.9 inch M2 128GB', 'apple:ipad-pro-m2-129:128gb'],
['iPad Pro 12,9 M2 128GB', 'apple:ipad-pro-m2-129:128gb'],
['MacBook Air 15 M3 16GB 512GB', 'apple:macbook-air-m3-15:512gb'],
['MacBook Air M2 8GB/256GB 13.6 inch', 'apple:macbook-air-m2-13:256gb'],
['MacBook Pro M4 Max 16 inch 48GB 1TB', keyOf('MacBook Pro M4 Max 16" 48GB 1TB')],
['Apple Watch Series 10 42mm GPS 64GB', 'apple:watch-series-10-42mm:64gb'],
['Apple Watch Series 11 GPS + Cellular 42mm', 'apple:watch-series-11-42mm:64gb'],
// A RAM size, a RAM/ROM pair or a core count is not a screen size, and a size with
// "inch" wins over a bare number.
['MacBook Air M3 16GB 256GB', 'apple:macbook-air-m3:256gb'],
['MacBook Air M4 16/256', 'apple:macbook-air-m4:unknown'],
['MacBook Pro M4 16-core GPU 14 inch 512GB', 'apple:macbook-pro-m4-14:512gb'],
['iPad Pro M4 11-core 256GB', 'apple:ipad-pro-m4:256gb'],
['MacBook Pro M4 Max 16 nhân CPU 40 nhân GPU 48GB 1TB', keyOf('MacBook Pro M4 Max 48GB 1TB')],
['MacBook Pro M3 Pro 18GB 512GB - 14 lõi GPU', 'apple:macbook-pro-m3-pro:512gb'],
['MacBook Air M3 16G 512G', keyOf('MacBook Air M3 512G')],
['MacBook Air M3 RAM 16 SSD 512GB', keyOf('MacBook Air M3 RAM SSD 512GB')],
// An Air comes in 13 and 15 inch; an OS version or a part code is no size.
['MacBook Air M2 8GB 256GB macOS 14 Sonoma', 'apple:macbook-air-m2:256gb'],
['MacBook Air M2 Z15S 8GB 256GB', 'apple:macbook-air-m2:256gb'],
['MacBook Air M3 256GB macOS 14', 'apple:macbook-air-m3:256gb'],
['HN Macbook Air M4 16 256 macbook', keyOf('Macbook Air M4 256')],
[
'MacBook Pro M4 Pro CPU 14 nhân GPU 20 nhân RAM 24GB SSD 512GB',
'apple:macbook-pro-m4-pro:512gb',
],
['iPad Pro M4 13.0 inch 256GB', 'apple:ipad-pro-m4-13:256gb'],
// A bundled accessory's size is not the product's; "GPS + Cellular" is the watch.
['iPad Pro M4 256GB + bàn phím 11 inch', 'apple:ipad-pro-m4:256gb'],
['iPad Pro 11 inch M4 + Magic Keyboard 256GB', 'apple:ipad-pro-m4-11:256gb'],
['MacBook Pro M4 Pro 16″ 24GB 512GB', 'apple:macbook-pro-m4-pro-16:512gb'],
// A title without its size keeps the size-less key and goes to Tier 2.
['Máy tính bảng Apple iPad Pro M4 256GB WiFi', 'apple:ipad-pro-m4:256gb'],
['Apple Watch Series 11 64GB', 'apple:watch-series-11:64gb'],
// One-size lines never carry a size.
['Apple Watch Ultra 3 49mm 64GB', 'apple:watch-ultra-3:64gb'],
['Apple Watch Ultra 2 49mm 64GB', 'apple:watch-ultra-2:64gb'],
['iPad Air 5 10.9 inch 64GB', 'apple:ipad-air-5:64gb'],
['iPad mini 7 8.3 inch 128GB', 'apple:ipad-mini-7:128gb'],
['MacBook Neo 13 inch 256GB', 'apple:macbook-neo:256gb'],
// A size after "cho" belongs to the product the accessory fits.
['Ốp lưng iPad Pro M4 13 inch cho iPad Pro 11', 'apple:ipad-pro-m4-13:unknown'],
];
for (const [title, expectedKey] of appleSizeKeys1113) {
assert.equal(keyOf(title), expectedKey, title);
}
// DOS/Rate#1124: a size outside a band phrase is the case's; a band's size counts only
// when the title names no other.
const watchBandSizeKeys1124: Array<[string, string]> = [
// Retailer titles: case size first, then the band.
[
'Apple Watch Series 11 - GPS, 46mm - Viền Nhôm Dây Cao Su S/M',
'apple:watch-series-11-46mm:64gb',
],
['Apple Watch Series 11 GPS 46mm viền nhôm dây thể thao', 'apple:watch-series-11-46mm:64gb'],
['Apple Watch Series 10 42mm (GPS) Viền Nhôm - Dây Vải', 'apple:watch-series-10-42mm:unknown'],
['Apple Watch Series 11 42mm viền thép dây Link Bracelet', 'apple:watch-series-11-42mm:64gb'],
// Two sizes, one of them the band's, leave the size-less key (Tier 2).
['Apple Watch Series 10 dây Sport Band 45mm cho mặt 42mm', 'apple:watch-series-10:unknown'],
['Apple Watch Series 10 Sport Loop 45mm cho mặt 46mm', 'apple:watch-series-10:unknown'],
['Apple Watch Series 10 Sport Band 45mm cho mặt 42mm', 'apple:watch-series-10:unknown'],
['Apple Watch SE 3 GPS Strap 40mm for 44mm case', 'apple:watch-se-3:unknown'],
['Apple Watch Series 10 Bracelet 42mm | 46mm', 'apple:watch-series-10-46mm:unknown'],
// A band is sold by the case it fits: a band's size alone is the case's.
[
'Apple Watch Series 10 GPS Dây Cao Su 46mm Chính Hãng VN/A',
'apple:watch-series-10-46mm:unknown',
],
['Apple Watch Series 9 GPS Sport Loop 45mm', 'apple:watch-series-9-45mm:unknown'],
['Apple Watch Series 11 GPS Viền Nhôm Dây Silicone 42mm', 'apple:watch-series-11-42mm:64gb'],
// The band phrase ends at ")", ",", "|", ";", an en dash or " - ": a case size after it
// wins over the band's.
['Apple Watch Series 11 GPS (Viền nhôm, dây cao su) 46mm', 'apple:watch-series-11-46mm:64gb'],
['Apple Watch Series 10 dây Sport Band 45mm, mặt 42mm', 'apple:watch-series-10-42mm:unknown'],
['Apple Watch Series 10 Dây Vải 45mm - 46mm GPS', 'apple:watch-series-10-46mm:unknown'],
['Apple Watch Series 10 Dây Vải 45mm; 46mm GPS', 'apple:watch-series-10-46mm:unknown'],
['Apple Watch Series 10 Dây Vải 45mm \u2013 46mm GPS', 'apple:watch-series-10-46mm:unknown'],
['Apple Watch Series 10 (dây vải 45mm) 46mm', 'apple:watch-series-10-46mm:unknown'],
// A glued "Sportband" is a band; "không dây" is wireless, no band.
['Apple Watch Series 10 Sportband 45mm cho mặt 42mm', 'apple:watch-series-10:unknown'],
['Apple Watch Series 10 sạc không dây 46mm cho dây 42mm', 'apple:watch-series-10-46mm:unknown'],
[
'Apple Watch Series 10 sạc kho\u0302ng dây 46mm cho dây 42mm',
'apple:watch-series-10-46mm:unknown',
],
// A size in brackets after the band is the case's when it is the only one.
[
'Apple Watch SE 2024 Viền nhôm dây cao su [40mm/GPS] - Mới',
'apple:watch-se-2024-40mm:unknown',
],
// A decomposed or upper-case "dây" is the same word; "Bandit" is no band.
['Apple Watch Series 10 da\u0302y vải 45mm, 42mm', 'apple:watch-series-10-42mm:unknown'],
['APPLE WATCH SERIES 10 DÂY VẢI 45MM, 42MM', 'apple:watch-series-10-42mm:unknown'],
['Apple Watch Series 10 Bandit 46mm cho mặt 42mm', 'apple:watch-series-10-46mm:unknown'],
];
for (const [title, expectedKey] of watchBandSizeKeys1124) {
assert.equal(keyOf(title), expectedKey, title);
}
// The registry has one entry per line: a sized title keeps the line's benchmark, but
// the entry's screen and battery describe only the size it was written for.
assert.equal(
normalizer.normalize(createDummyShopeeProduct('Apple Watch Series 11 GPS 42mm'))
.is_benchmark_enriched,
true
);
const ipadPro13 = normalizer.normalize(
createDummyShopeeProduct('iPad Pro M4 13 inch WiFi 256GB')
);
assert.equal(ipadPro13.screen_size_inch, 13);
assert.equal(ipadPro13.battery_mah, null);
const ipadPro11 = normalizer.normalize(createDummyShopeeProduct('iPad Pro M4 11 inch 256GB'));
assert.equal(ipadPro11.screen_size_inch, 11);
assert.equal(ipadPro11.battery_mah, 8160);
assert.equal(
normalizer.normalize(createDummyShopeeProduct('MacBook Air M3 15 inch 256GB')).screen_size_inch,
15
);
// iPad mini 6 and 7 are two models (DOS/Rate#819): "(A17 Pro)" is the 7th, "8.3 inch" is
// its screen, and a size or a year after "mini" is no generation.
assert.equal(keyOf('Apple iPad mini 6 Wi-Fi 64GB'), 'apple:ipad-mini-6:64gb');
Expand Down Expand Up @@ -403,15 +531,15 @@ async function runSuite() {
assert.ok(!/\s{2}/.test(ultra2.name), ultra2.name);
assert.equal(keyOf('Apple Watch Series'), 'apple:watch:unknown');
// A case size or storage after "SE" is not its generation: one SE, not "SE 40"; an
// ordinal one is ("SE 2nd Gen").
assert.equal(keyOf('Apple Watch SE 40mm'), 'apple:watch-se:unknown');
assert.equal(keyOf('Apple Watch SE 44mm GPS'), 'apple:watch-se:unknown');
assert.equal(keyOf('Apple Watch SE 44 mm GPS'), 'apple:watch-se:unknown');
assert.equal(keyOf('Apple Watch SE 2nd Gen GPS 44mm'), 'apple:watch-se-2:unknown');
assert.equal(keyOf('Apple Watch SE 3rd Gen 44mm'), 'apple:watch-se-3:unknown');
assert.equal(keyOf('Apple Watch SE 2022 40mm'), 'apple:watch-se-2022:unknown');
// ordinal one is ("SE 2nd Gen"). The case size keys apart (DOS/Rate#1113).
assert.equal(keyOf('Apple Watch SE 40mm'), 'apple:watch-se-40mm:unknown');
assert.equal(keyOf('Apple Watch SE 44mm GPS'), 'apple:watch-se-44mm:unknown');
assert.equal(keyOf('Apple Watch SE 44 mm GPS'), 'apple:watch-se-44mm:unknown');
assert.equal(keyOf('Apple Watch SE 2nd Gen GPS 44mm'), 'apple:watch-se-2-44mm:unknown');
assert.equal(keyOf('Apple Watch SE 3rd Gen 44mm'), 'apple:watch-se-3-44mm:unknown');
assert.equal(keyOf('Apple Watch SE 2022 40mm'), 'apple:watch-se-2022-40mm:unknown');
assert.equal(keyOf('Apple Watch SE 32GB'), 'apple:watch-se:32gb');
assert.equal(keyOf('Apple Watch SE2 40mm'), 'apple:watch-se2:unknown');
assert.equal(keyOf('Apple Watch SE2 40mm'), 'apple:watch-se2-40mm:unknown');
// With no line matched, the family the title names first, else iPhone.
assert.equal(keyOf('MacBook M4'), 'apple:macbook:unknown');
assert.equal(keyOf('Apple "iPad" 256GB'), 'apple:ipad:256gb');
Expand Down Expand Up @@ -532,20 +660,20 @@ async function runSuite() {
assert.equal(ipad.model_key, 'apple:ipad-pro-m4:256gb');
assert.equal(ipad.is_benchmark_enriched, true);
assert.equal(ipad.specifications.hardware?.chipset, 'Apple M4 (3 nm)');
// Retail titles put the chip after the screen size; it must still reach the key.
// Retail titles put the chip after the screen size; it must still reach the key, and
// the screen size keys the 11 and 13 inch models apart (DOS/Rate#1113).
for (const title of [
'Máy tính bảng Apple iPad Pro 11 inch M4 Wi-Fi 256GB',
'Apple iPad Pro 11 inch M4 WiFi 256GB - Chính hãng',
]) {
assert.equal(
normalizer.normalize(createDummyShopeeProduct(title)).model_key,
'apple:ipad-pro-m4:256gb',
title
);
const sized = normalizer.normalize(createDummyShopeeProduct(title));
assert.equal(sized.model_key, 'apple:ipad-pro-m4-11:256gb', title);
// The registry has one entry per line, so a sized title is still enriched.
assert.equal(sized.is_benchmark_enriched, true, title);
}
assert.equal(
normalizer.normalize(createDummyShopeeProduct('MacBook Air 13 inch M3 8GB 256GB')).model_key,
'apple:macbook-air-m3:256gb'
'apple:macbook-air-m3-13:256gb'
);
assert.equal(
normalizer.normalize(createDummyShopeeProduct('Tai nghe Apple AirPods Pro 2 USB-C MTJV3'))
Expand Down Expand Up @@ -623,7 +751,7 @@ async function runSuite() {
['Apple iPhone 18 Pro Max', 'apple:iphone-18-pro-max:unknown'],
['Apple iPhone 18 Pro', 'apple:iphone-18-pro:unknown'],
['Lenovo ThinkPad X1 Carbon Gen 12 Ultra 7 155U', 'lenovo:thinkpad-x1-carbon-gen-12:unknown'],
['Máy tính bảng iPad Pro M5 11 inch WiFi', 'apple:ipad-pro-m5:unknown'],
['Máy tính bảng iPad Pro M5 11 inch WiFi', 'apple:ipad-pro-m5-11:unknown'],
['Máy tính bảng Xiaomi Pad 7 Pro', 'xiaomi:pad-7-pro:unknown'],
];
for (const [title, expectedKey] of storagelessRegistryTitles) {
Expand All @@ -648,7 +776,7 @@ async function runSuite() {
// title naming it (the single-storage watches above keep their keys).
assert.equal(
normalizer.normalize(createDummyShopeeProduct('Apple Watch Series 11 GPS 46mm')).model_key,
'apple:watch-series-11:64gb'
'apple:watch-series-11-46mm:64gb'
);

const sonyBuds = normalizer.normalize(
Expand Down Expand Up @@ -1385,14 +1513,14 @@ async function runSuite() {
const ipadProM5Ram = normalizer.normalize(
createDummyShopeeProduct('iPad Pro 11 M5 2025 WiFi 12GB')
);
assert.equal(ipadProM5Ram.model_key, 'apple:ipad-pro-m5:unknown');
assert.equal(ipadProM5Ram.model_key, 'apple:ipad-pro-m5-11:unknown');
assert.equal(ipadProM5Ram.ram_gb, 12);
assert.equal(ipadProM5Ram.storage_gb, null);

const ipadAirRamOnly = normalizer.normalize(
createDummyShopeeProduct('iPad Air 11 M3 2025 WiFi 8GB')
);
assert.equal(ipadAirRamOnly.model_key, 'apple:ipad-air-m3:unknown');
assert.equal(ipadAirRamOnly.model_key, 'apple:ipad-air-m3-11:unknown');
assert.equal(ipadAirRamOnly.ram_gb, 8);
assert.equal(ipadAirRamOnly.storage_gb, null);

Expand Down
Loading
Loading