import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; import { extractOzonCharacteristics, extractOzonDescription, extractOzonJsonLdDescription, extractOzonGalleryImageUrls, extractOzonGalleryVideos, } from '../lib/ozon-parser.ts'; import { dedupeImageUrl, normaliseImageUrl } from '../lib/product-media.ts'; test('Ozon gallery fixture only returns product-gallery images', async () => { const fixture = await readFile( new URL('../../../packages/reference/ozon1.html', import.meta.url), 'utf8', ); const state = fixture.match( /id="state-webGallery-[^"]+"\s+data-state='([^']+)'/, )?.[1]; assert.ok(state, 'Ozon gallery widget state must exist in the fixture'); const urls = extractOzonGalleryImageUrls( JSON.parse(state), 'https://www.ozon.ru/product/ryukzak-5387005639/', ); assert.equal(urls.length, 11); assert.ok(urls.every((url) => /\/s3\/multimedia-/.test(url))); assert.ok( urls.every( (url) => !/logo_ozon|payments-cdn|recommend|review/i.test(url), ), ); }); test('Ozon short characteristics fixture returns its product parameters', async () => { const fixture = await readFile( new URL('../../../packages/reference/ozon1.html', import.meta.url), 'utf8', ); const state = fixture.match( /id="state-webShortCharacteristics-[^"]+"\s+data-state='([^']+)'/, )?.[1]; assert.ok(state, 'Ozon short characteristics widget state must exist'); const attributes = extractOzonCharacteristics([ { ...JSON.parse(state), __atlasWidgetName: 'webShortCharacteristics-fixture', }, ]); assert.equal(attributes.length, 5); assert.deepEqual( attributes.slice(0, 3).map(({ name, value }) => ({ name, value })), [ { name: '类型', value: '背包' }, { name: '原产国', value: '中国' }, { name: '材料', value: 'Cordura 1000D' }, ], ); }); test('Ozon rich annotation converts to description text without images', () => { const description = extractOzonDescription([ { __atlasWidgetName: 'webDescription-fixture', richAnnotationJson: JSON.stringify([ { type: 'text', content: '耐用的背包' }, { type: 'image', src: 'https://ir.ozone.ru/s3/example.jpg' }, { type: 'text', text: '适合旅行' }, ]), }, ]); assert.equal(description?.value, '耐用的背包\n适合旅行'); }); test('Ozon JSON-LD Product description is the fallback when rich content is absent', () => { const root = { scripts: [{ type: 'application/ld+json', textContent: JSON.stringify({ '@context': 'http://schema.org', '@type': 'Product', name: '喷雾器', description: '来自 Ozon JSON-LD 的完整商品描述', }), }], } as unknown as Document; assert.equal( extractOzonJsonLdDescription(root)?.value, '来自 Ozon JSON-LD 的完整商品描述', ); }); test('Ozon full characteristics merge with the five short header characteristics', () => { const attributes = extractOzonCharacteristics([ { __atlasWidgetName: 'webShortCharacteristics-fixture', characteristics: [{ id: 'type', title: { textRs: [{ content: '类型' }] }, values: [{ text: '喷雾器' }] }], }, { __atlasWidgetName: 'webCharacteristics-fixture', characteristics: [{ id: 'weight', title: { textRs: [{ content: '商品重量,克' }] }, values: [{ text: '385' }] }], }, ]); assert.deepEqual(attributes.map(({ name, value }) => ({ name, value })), [ { name: '类型', value: '喷雾器' }, { name: '商品重量,克', value: '385' }, ]); }); test('Ozon thumbnail variants normalise to one original URL', () => { const original = 'https://ir.ozone.ru/s3/multimedia-1-9/13597160877.jpg'; const thumbnail = 'https://ir.ozone.ru/s3/multimedia-1-9/wc100/13597160877.jpg?width=100&quality=80'; assert.equal(normaliseImageUrl(thumbnail, original, 'ozon'), original); assert.equal( dedupeImageUrl(thumbnail, 'ozon'), dedupeImageUrl(original, 'ozon'), ); }); test('Ozon gallery only accepts direct product MP4/WebM videos and retains poster', () => { assert.deepEqual( extractOzonGalleryVideos( { videos: [ { url: 'https://cdn.ozon.ru/product/demo.mp4', coverImage: 'https://ir.ozone.ru/s3/multimedia-1/demo.jpg', }, { videoUrl: 'https://cdn.ozon.ru/product/demo.mp4' }, { url: 'https://cdn.ozon.ru/product/stream.m3u8' }, { url: 'blob:https://www.ozon.ru/opaque' }, ], }, 'https://www.ozon.ru/product/demo-1/', ), [{ url: 'https://cdn.ozon.ru/product/demo.mp4', posterUrl: 'https://ir.ozone.ru/s3/multimedia-1/demo.jpg', }], ); }); test('Alibaba transformed image URLs normalise before de-duplication', () => { const original = 'https://cbu01.alicdn.com/img/ibank/O1CN01.jpg'; const transformed = `${original}_400x400.jpg?width=400`; assert.equal(normaliseImageUrl(transformed, original, '1688'), original); });