import test from "node:test"; import assert from "node:assert/strict"; import type { ReactNode } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { SiteProvider } from "../src/components/providers/SiteProvider"; import { SiteHeader } from "../src/components/layout/SiteHeader"; import { MobileTabbar } from "../src/components/layout/MobileTabbar"; import { HomePage } from "../src/features/catalog/HomePage"; import { HomeRoadPage } from "../src/features/content/HomeRoadPage"; import { SiteNavigationPage } from "../src/features/content/SiteNavigationPage"; import { brandAssets } from "../src/lib/brand-assets"; import { toPublicPath } from "../src/lib/routes"; import { allListURL, chapterPlayURL, categoryURL, listThemeSegment, listUpdateStatus, moreListURL, moreSeriesListURL, navHasMegaMenu, navHref, navIsActive, navIsDownload, pageURL, videoPlayURL } from "../src/lib/view-helpers"; const data = { requestPath: "/dramas/", isLoggedIn: true, isRegistered: true, userID: 7, userName: "观众", recharge: 100, appConfig: {}, config: {}, categoryList: [ { name: "首页", urlValue: "/", categoryType: 1 }, { name: "短剧", urlValue: "/list", categoryType: 1 }, { name: "专题", urlValue: "/topic", categoryType: 1 }, { name: "排行榜", urlValue: "/rank", categoryType: 1 }, { name: "回家的路", urlValue: "/home-road/", categoryType: 1 }, ], themeList: [{ id: 1, slug: "romance", name: "爱情" }], }; const render = (children: ReactNode) => renderToStaticMarkup({children}); const hrefs = (html: string) => [...html.matchAll(/]*\bhref="([^"]*)"/g)].map((match) => match[1].replaceAll("&", "&")); function assertPublicLinks(html: string): string[] { const links = hrefs(html); for (const href of links.filter((value) => value.startsWith("/") && !value.startsWith("//"))) assert.equal(href, toPublicPath(href), "internal link should reach its page directly: " + href); return links; } test("shared view helpers produce business URLs and keep nested menu activation boundaries", () => { assert.equal(chapterPlayURL("story/a", 7, 101), "/drama/story%2Fa/101/"); assert.equal(pageURL("/play/story%2fa?x=a+b&x=%2b#ep%202"), "/drama/story%2fa/?x=a+b&x=%2b#ep%202"); assert.equal(videoPlayURL("movie", 9), "/series/watch/movie/"); assert.equal(categoryURL("romance", 1), "/collection/romance/"); assert.equal(allListURL("romance", 1, 2, 1, 3), "/dramas/romance/2/1/page/3/"); assert.equal(moreListURL("picks", 1, 0, "", 0, 1), "/curated/dramas/picks/"); assert.equal(moreSeriesListURL("movies", 2, 0, "", 0, 1), "/curated/series/movies/"); assert.equal(navHref("/playlet"), "/dramas/"); assert.equal(navHref("/topic/romance/"), "/collection/romance/"); assert.equal(navHref("/download-app/"), "/app/download/"); assert.equal(navHref("/home-road/"), "/return-home/"); assert.equal(navIsDownload("/app/download/", "客户端下载"), true); assert.equal(navHasMegaMenu("/dramas/"), true); assert.equal(listThemeSegment("/dramas/romance/2/1/"), "romance"); assert.equal(listUpdateStatus("/dramas/romance/2/1/"), 2); assert.equal(navIsActive("/series/", "/series/watch/movie/"), false); assert.equal(navIsActive("/dramas/", "/dramas/romance/"), true); }); test("navigation configuration resolves both legacy and normalized collection-detail aliases without decoding slugs", () => { for (const root of ["/topic", "/collections"]) { assert.equal(navHref(root + "/热门?q=a"), "/collection/%E7%83%AD%E9%97%A8/?q=a"); assert.equal(navHref(root + "/story%2fa%252F1?x=a+b&x=%2b#ep%202"), "/collection/story%2fa%252F1/?x=a+b&x=%2b#ep%202"); assert.equal(navHref(root + "/page/2/"), "/collections/page/2/"); assert.equal(navHref(root + "/sort/2/"), "/collections/sort/2/"); } }); test("SSR header exposes direct canonical URLs and no account entries", () => { const html = render(); const links = assertPublicLinks(html); for (const expected of ["/dramas/", "/collections/", "/rankings/", "/discover/search/", "/return-home/"]) assert.ok(links.includes(expected), expected); assert.ok(!links.some((link) => link.startsWith("/account")), "account links removed"); assert.doesNotMatch(html, /data-login-open|hk-user-menu/); }); test("configured header submenus expose canonical links, external link attributes and download actions", () => { const html = render(); const links = assertPublicLinks(html); assert.ok(links.includes("/collection/romance/")); assert.ok(links.includes("/rankings/")); for (const href of ["https://huangguo.com", "//partner.test/path"]) { const anchor = [...html.matchAll(/]*>/g)].map(([tag]) => tag).find((tag) => tag.includes(`href="${href}"`)); assert.ok(anchor, href); assert.match(anchor, /target="_blank"/); assert.match(anchor, /rel="sponsored nofollow noopener"/); } assert.match(html, /]*href="https:\/\/downloads\.test\/app"[^>]*data-download-open=""/); const toggles = [...html.matchAll(/]*>/g)].map(([tag]) => tag).filter((tag) => tag.includes("hk-site-navigation__submenu-toggle")); assert.equal(toggles.length, 2); const controls = toggles.map((tag) => { assert.match(tag, /aria-expanded="false"/); const id = /aria-controls="([^"]+)"/.exec(tag)?.[1]; assert.ok(id); assert.ok(html.includes(`id="${id}"`), "submenu control points to its rendered panel"); return id; }); assert.equal(new Set(controls).size, controls.length); }); test("configured lists take precedence over the short-drama mega menu while missing or empty lists retain normal navigation", () => { const configured = render(); assert.match(configured, /data-navigation-submenu="desktop"/); assert.doesNotMatch(configured, /aria-label="短剧快捷筛选"/); for (const translationList of [undefined, [], null, {}, "invalid"]) { const html = render(); assert.doesNotMatch(html, /data-navigation-submenu="desktop"/); assert.match(html, /aria-label="短剧快捷筛选"/); const links = assertPublicLinks(html); assert.ok(links.includes("/dramas/")); assert.ok(links.includes("/collections/")); } }); test("mobile TabBar shows only content tabs and stays hidden on nested series pages", () => { const links = assertPublicLinks(renderToStaticMarkup()); assert.deepEqual(links, ["/", "/dramas/", "/series/"]); for (const requestPath of ["/account/", "/series/watch/movie/"]) assert.equal(renderToStaticMarkup(), "", requestPath); assert.match(renderToStaticMarkup(), /aria-current="page"[^>]*data-navigation-key="mobile_short"/); }); test("home Hero, cards and curated sections all navigate directly to new page addresses", () => { const work = { id: 7, slug: "story", playName: "作品", totalEpisode: 8, lastChapterId: 101, lastEpisode: 2, cover: "/static/brand/header.svg", banner: "/static/brand/header.svg" }; const html = render(); const links = assertPublicLinks(html); for (const expected of ["/drama/story/", "/dramas/", "/curated/dramas/picks/", "/curated/series/movies/", "/series/watch/movie/", "/collection/romance/", "/collections/", "/app/download/"]) assert.ok(links.includes(expected), expected); }); test("home road renders current branding, configured access routes and official contact links", () => { const html = render(); const links = assertPublicLinks(html); for (const expected of ["https://new.huakui.example/", "/dramas/", "https://backup.example/path?x=a+b&x=%2b", "mailto:help@huakui.example", "https://t.me/huakui_official", "https://t.me/huakui_support", "https://x.com/huakui"]) assert.ok(links.includes(expected), expected); assert.ok(html.includes(`src="${brandAssets.navigationLogo}"`)); assert.match(html, /alt="花魁短剧"/); assert.match(html, /花魁短剧 · 官方最新地址发布页/); assert.match(html, /huakui\.example\/return-home/); assert.match(html, /如何保存入口?/); assert.doesNotMatch(html, /黄果短剧|huangguoaiai@gmail\.com|href="\/home-road\//); }); test("home road keeps an accessible fallback when configured routes or contacts are absent or unsafe", () => { for (const appConfig of [{}, { newURLAddr: "javascript:alert(1)", foreverURLAddr: "//unknown.example/", newURLAddr1: "https://unsafe.example/\\path", newURLAddr2: "data:text/html,unsafe", navPageList: [{ url: "javascript:alert(2)" }], gmailAddr: "invalid", tgGroup: "javascript:alert(3)", }]) { const html = render(); assert.ok(hrefs(html).every((href) => href === "/")); assert.match(html, /永久入口正在更新/); assert.match(html, /官方联系方式正在更新/); assert.match(html, /回家的路页面有什么作用?/); assert.doesNotMatch(html, /javascript:|data:text|huangguoaiai@gmail\.com/); } }); test("site navigation uses the supplied home road destination and hides an unavailable entry", () => { for (const homeRoadURL of ["/return-home/", "https://main.huakui.test/return-home/", "https://files.huakui.test/access.pdf", ""]) { const html = render(); const homeRoadLink = [...html.matchAll(/]*>[\s\S]*?<\/a>/g)] .map(([anchor]) => anchor).find((anchor) => anchor.includes(">回家的路")); if (!homeRoadURL) assert.equal(homeRoadLink, undefined); else { assert.ok(homeRoadLink); assert.ok(homeRoadLink.includes(`href="${homeRoadURL}"`)); if (homeRoadURL.startsWith("https://")) assert.match(homeRoadLink, /target="_blank"/); } } });