152 lines
5.5 KiB
TypeScript
152 lines
5.5 KiB
TypeScript
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);
|
|
});
|