Files
web_huakui_web/tests/catalog-react.test.tsx
2026-10-23 15:07:10 +07:00

190 lines
5.9 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { renderToStaticMarkup } from "react-dom/server";
import {
DramaCard,
PlayRecommendationCard,
SeriesCard,
} from "../src/components/catalog/Cards";
import { Pagination } from "../src/components/catalog/Pagination";
import { PromoGrid } from "../src/components/catalog/Promotions";
import {
CatalogGrid,
ListPage,
SeriesPage,
} from "../src/features/catalog/BrowsePages";
import {
allListURL,
array,
cardTags,
moreListURL,
moreSeriesListURL,
searchResultsURL,
seriesListURL,
tagPageURL,
topicDetailURL,
} from "../src/features/catalog/model";
const item = {
id: 7,
slug: "都市 剧",
playName: "作品名",
title: "备用标题",
cover: "/static/brand/header.svg",
totalEpisode: 8,
updateStatus: 1,
isFeatured: true,
tagIds: [2],
tagNames: ["爱情"],
tagSlugs: ["romance"],
themeIds: [3],
themeNames: ["爱情", "都市"],
themeSlugs: ["love", "city"],
};
const ads = [
{
id: 1,
images: "/static/brand/header.svg",
link: "https://promo.test/",
title: "推荐",
},
];
test("React card variants preserve watch URLs, episode gates, badges and linked deduplicated tags", () => {
const card = renderToStaticMarkup(<DramaCard item={item} />);
assert.match(card, /class="hk-drama-card hk-drama-card--list"/);
assert.match(
card,
/href="\/drama\/都市%20剧\/"|href="\/drama\/%E9%83%BD%E5%B8%82%20%E5%89%A7\/"/,
);
// Episode count and serial state sit in the cover corner; the line under the title lists genres.
assert.match(
card,
/<span class="hk-drama-card__eps" data-status="serial"><b>8<\/b> 集 · 连更<\/span><\/a>/,
);
assert.match(
card,
/<p class="hk-drama-card__meta hk-drama-card__genres"><a href="\/tags\/romance\/">爱情<\/a><span class="hk-drama-card__genre-sep" aria-hidden="true">\/<\/span>/,
);
assert.doesNotMatch(card, /hk-drama-card__ep"/);
assert.match(card, /hk-badge hk-badge--hot/);
assert.match(card, /href="\/tags\/romance\/"/);
assert.deepEqual(
cardTags(item).map((tag) => tag.Name),
["爱情", "都市"],
);
const recommendation = renderToStaticMarkup(
<PlayRecommendationCard item={item} />,
);
assert.match(recommendation, /hk-drama-card hk-drama-card--play/);
assert.match(recommendation, /hk-drama-card__eps" data-status="serial"><b>8<\/b> 集 · 连更</);
assert.doesNotMatch(recommendation, /hk-badge--hot/);
const video = renderToStaticMarkup(
<SeriesCard
item={{
id: 9,
slug: "movie",
title: "剧集",
mediaType: 2,
videoCount: 12,
topicNames: ["动画"],
coverImg: "/static/brand/header.svg",
}}
/>,
);
assert.match(video, /href="\/series\/watch\/movie\/"/);
assert.match(video, /全 12 集/);
assert.match(video, /动画/);
});
test("React list advertising keeps independent desktop and mobile row positions and clone attribution", () => {
const items = array(
Array.from({ length: 25 }, (_, index) => ({ ...item, id: index + 1 })),
);
const grid = renderToStaticMarkup(<CatalogGrid items={items} ads={ads} />);
assert.equal((grid.match(/class="hk-drama-card hk-drama-card--list"/g) || []).length, 25);
assert.equal((grid.match(/class="hk-drama-browse-mid-promo hk-desktop-only"/g) || []).length, 2);
assert.equal((grid.match(/class="hk-drama-browse-mid-promo hk-mobile-only"/g) || []).length, 4);
const videoGrid = renderToStaticMarkup(
<CatalogGrid items={items.slice(0, 16)} ads={ads} series />,
);
assert.equal(
(videoGrid.match(/class="hk-series-list-promo hk-desktop-only"/g) || []).length,
1,
);
assert.equal(
(videoGrid.match(/class="hk-series-list-promo hk-mobile-only"/g) || []).length,
3,
);
const promo = renderToStaticMarkup(<PromoGrid items={ads} />);
assert.equal((promo.match(/rel="sponsored nofollow noopener"/g) || []).length, 2);
assert.equal((promo.match(/tabindex="-1"/g) || []).length, 1);
});
test("React filter and pagination links preserve the source path-only canonical route contracts", () => {
assert.equal(allListURL("", 0, 0, 1, 1), "/dramas/");
assert.equal(
allListURL("都市", 3, 2, 3, 2),
"/dramas/%E9%83%BD%E5%B8%82/2/3/page/2/",
);
assert.equal(seriesListURL(2, 4, 3, 2), "/series/anime/4/3/page/2/");
assert.equal(moreListURL("featured", 1, 0, "", 0, 1), "/curated/dramas/featured/");
assert.equal(
moreSeriesListURL("", 3, 1, "city", 0, 2),
"/curated/series/3/1/city/page/2/",
);
assert.equal(tagPageURL("romance", 1, 2, 1), "/tags/romance/sort/2/");
assert.equal(
topicDetailURL("city", 1, 3, 2),
"/collection/city/sort/3/page/2/",
);
assert.equal(searchResultsURL("a/b"), "/search/a%2Fb/");
const html = renderToStaticMarkup(
<Pagination
pagination={{
Visible: true,
HasPrev: false,
HasNext: true,
NextURL: "/dramas/all/0/1/page/2/",
Items: [
{ Label: "1", URL: "/dramas/", Active: true },
{ Label: "…", Dots: true },
],
TotalText: "共 3 页",
}}
/>,
);
assert.match(html, /hk-pagination__btn--prev hk-is-disabled/);
assert.match(html, /aria-current="page"/);
assert.match(html, /href="\/dramas\/all\/0\/1\/page\/2\/"/);
assert.match(html, /hk-pagination__ellipsis/);
assert.match(html, /共 3 页/);
const list = renderToStaticMarkup(
<ListPage
data={{
list: [],
themeList: [{ id: 3, slug: "city", name: "都市" }],
themeId: 3,
themeSlug: "city",
updateStatus: 2,
sortType: 3,
}}
/>,
);
assert.match(list, /href="\/dramas\/city\/2\/3\/"/);
assert.match(list, /暂无短剧/);
const series = renderToStaticMarkup(
<SeriesPage
data={{
list: [],
topicList: [{ id: 4, name: "爱情" }],
topicId: 4,
mediaType: 2,
sortType: 1,
}}
/>,
);
assert.match(series, /href="\/series\/anime\/4\/1\/"/);
assert.match(series, /暂无剧集/);
});