Files
atlas-seller-kit/apps/extension/test/1688-gallery.test.mts
T

66 lines
4.1 KiB
TypeScript

import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { createRequire } from 'node:module';
import test from 'node:test';
import { collectedProductPayloadSchema } from '@atlas/contracts';
import { collectPlatformDomAssets, normaliseImageUrl } from '../lib/product-media.ts';
import { collectCurrentProduct } from '../lib/source-parsers.ts';
// 使用 WXT 已有的 HTML 解析依赖构建离线 DOM,不加载样本里的远程媒体。
const require = createRequire(import.meta.url);
const wxtRequire = createRequire(require.resolve('wxt'));
const { parseHTML } = wxtRequire('linkedom');
const pageUrl = 'https://detail.1688.com/offer/1012823100668.html';
const videoUrl = 'https://cloud.video.taobao.com/qa-product.mp4';
const poster = 'https://cbu01.alicdn.com/img/ibank/O1CN01lCwIgP1UjKGylKwAN_!!2563422553-0-cib.jpg';
async function fixture(extra = ''): Promise<Document> {
const html = await readFile(new URL('./fixtures/1688-gallery-background.html', import.meta.url), 'utf8');
const { document, window } = parseHTML(`<html><body>${html}${extra}</body></html>`);
window.getComputedStyle = (element: HTMLElement) => ({ backgroundImage: element.style.backgroundImage });
Object.defineProperty(document, 'scripts', { get: () => document.querySelectorAll('script') });
for (const element of document.querySelectorAll('.v-image-cover')) {
Object.defineProperty(element, 'offsetWidth', { value: 60 });
Object.defineProperty(element, 'offsetHeight', { value: 60 });
}
return document;
}
test('用户画廊样本的 15 张背景缩略图完整采集、还原原图,排除参数与视频控件', async () => {
const assets = collectPlatformDomAssets('1688', await fixture(), pageUrl);
assert.equal(assets.length, 15);
assert.ok(assets.every((asset) => asset.mediaType === 'image' && asset.role === 'source_main'));
assert.equal(assets[0].url, poster);
assert.ok(assets.every((asset) => asset.url.endsWith('.jpg') && !asset.url.includes('_b.jpg')));
assert.ok(assets.every((asset) => !/undefined|\.svg/.test(asset.url)));
assert.equal(normaliseImageUrl('undefined', pageUrl, '1688'), undefined);
});
test('画廊播放器可采集真实视频及封面,忽略推荐视频、blob 和 HLS', async () => {
const root = await fixture(`<div class="od-picture-gallery"><video src="${videoUrl}"></video><video src="blob:https://detail.1688.com/opaque"></video><video src="https://cloud.video.taobao.com/stream.m3u8"></video></div><aside><video src="https://cloud.video.taobao.com/recommend.mp4"></video></aside>`);
const assets = collectPlatformDomAssets('1688', root, pageUrl);
const videos = assets.filter((asset) => asset.mediaType === 'video');
assert.equal(videos.length, 1);
assert.equal(videos[0].url, videoUrl);
assert.equal(videos[0].posterUrl, poster);
assert.equal(assets.filter((asset) => asset.mediaType === 'image').length, 15);
});
test('脚本画廊数据与 DOM 视频去重,完整采集载荷符合 API 契约', async () => {
const gallery = { fields: { video: { url: videoUrl, videoThumbnailURL: poster } } };
const root = await fixture(`<div class="od-picture-gallery"><video><source src="${videoUrl}"></video></div><script>window.context=${JSON.stringify({ gallery })};</script>`);
const payload = await collectCurrentProduct(root, pageUrl);
assert.equal(payload.assets.filter((asset) => asset.mediaType === 'video').length, 1);
assert.equal(payload.assets.filter((asset) => asset.mediaType === 'image').length, 15);
assert.equal(collectedProductPayloadSchema.safeParse(payload).success, true);
});
test('尚未挂载播放器时仍可从明确的 gallery 字面值读取视频直链', async () => {
const gallery = { fields: { video: { url: '', videoAddress: videoUrl, videoThumbnailURL: poster } } };
const payload = await collectCurrentProduct(await fixture(`<script>window.context=${JSON.stringify({ gallery })};</script>`), pageUrl);
const videos = payload.assets.filter((asset) => asset.mediaType === 'video');
assert.equal(videos.length, 1);
assert.equal(videos[0].url, videoUrl);
assert.equal(videos[0].posterUrl, poster);
});