From e37cf9936e36037c31aad65b4a1629fc550ea5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E8=82=B2=E6=96=B0?= <2635131263@qq.com> Date: Wed, 7 Oct 2026 21:07:07 +0800 Subject: [PATCH] feat(polaris): import bookmarks and refine category scrolling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 导入 29 条精选公开书签,排除重复与敏感入口 - 将滚动限制在分类页内层卡片区域,并适配窄屏 - 更新数据与页面测试 --- .../app/categories/[categoryId]/page.test.tsx | 14 +- ts/web/nextjs/polaris/src/app/globals.css | 162 +++++++++- ts/web/nextjs/polaris/src/data/data.test.ts | 33 +- ts/web/nextjs/polaris/src/data/links.json | 293 ++++++++++++++++++ .../components/category-content.test.tsx | 63 ++-- .../category/components/category-content.tsx | 22 +- .../polaris/src/services/services.test.ts | 10 +- 7 files changed, 547 insertions(+), 50 deletions(-) diff --git a/ts/web/nextjs/polaris/src/app/categories/[categoryId]/page.test.tsx b/ts/web/nextjs/polaris/src/app/categories/[categoryId]/page.test.tsx index 30e02f0..c9c435c 100644 --- a/ts/web/nextjs/polaris/src/app/categories/[categoryId]/page.test.tsx +++ b/ts/web/nextjs/polaris/src/app/categories/[categoryId]/page.test.tsx @@ -1,6 +1,8 @@ import { render, screen } from "@testing-library/react" import { describe, expect, it } from "vitest" +import { getLinksByCategory } from "@/services/links" + import CategoryPage, { dynamicParams, generateStaticParams } from "./page" describe("CategoryPage", () => { @@ -18,6 +20,7 @@ describe("CategoryPage", () => { }) it("通过 service 组合开发工具分类页", async () => { + const links = getLinksByCategory("dev") const page = await CategoryPage({ params: Promise.resolve({ categoryId: "dev" }), }) @@ -26,8 +29,8 @@ describe("CategoryPage", () => { expect( screen.getByRole("heading", { level: 1, name: "开发工具" }), ).toBeInTheDocument() - expect(screen.getByText("共 8 个")).toBeInTheDocument() - expect(container.querySelectorAll(".tool-card")).toHaveLength(8) + expect(screen.getByText(`共 ${links.length} 个`)).toBeInTheDocument() + expect(container.querySelectorAll(".tool-card")).toHaveLength(links.length) expect(screen.getByRole("link", { name: "开发工具" })).toHaveAttribute( "aria-current", "page", @@ -35,7 +38,8 @@ describe("CategoryPage", () => { expect(screen.queryByRole("searchbox")).not.toBeInTheDocument() }) - it("通过同一路由组合空分类页", async () => { + it("通过同一路由组合生活服务分类页", async () => { + const links = getLinksByCategory("life") const page = await CategoryPage({ params: Promise.resolve({ categoryId: "life" }), }) @@ -44,7 +48,7 @@ describe("CategoryPage", () => { expect( screen.getByRole("heading", { level: 1, name: "生活服务" }), ).toBeInTheDocument() - expect(screen.getByText("共 0 个")).toBeInTheDocument() - expect(screen.getByText("暂无工具")).toBeInTheDocument() + expect(screen.getByText(`共 ${links.length} 个`)).toBeInTheDocument() + expect(screen.queryByText("暂无工具")).not.toBeInTheDocument() }) }) diff --git a/ts/web/nextjs/polaris/src/app/globals.css b/ts/web/nextjs/polaris/src/app/globals.css index b0f8333..c8138d2 100644 --- a/ts/web/nextjs/polaris/src/app/globals.css +++ b/ts/web/nextjs/polaris/src/app/globals.css @@ -569,12 +569,16 @@ a { } .category-page { - min-height: 100svh; + height: 100svh; padding: 20px 20px 20px 0; } .category-page__panel { - min-height: calc(100svh - 40px); + display: flex; + height: 100%; + min-height: 0; + overflow: hidden; + flex-direction: column; padding: 48px clamp(32px, 4vw, 64px) 56px; background: var(--surface-sidebar); box-shadow: var(--shadow-panel); @@ -603,11 +607,35 @@ a { } .category-tools { + display: flex; + min-height: 0; + flex: 1; + flex-direction: column; margin-top: 36px; } +.category-tools__viewport { + min-width: 0; + min-height: 0; + overflow-y: auto; + overscroll-behavior-y: contain; + flex: 1; + padding: 16px; + border: 1px solid rgba(255, 255, 255, 0.36); + border-radius: 20px; + background: rgba(82, 113, 151, 0.09); + box-shadow: + inset 0 2px 10px rgba(29, 56, 93, 0.07), + inset 0 1px 0 rgba(255, 255, 255, 0.22); +} + +.category-tools__viewport:focus-visible { + outline-offset: -4px; +} + .category-section-header { display: flex; + flex: none; align-items: center; gap: 12px; margin-bottom: 20px; @@ -705,6 +733,7 @@ a { .tool-grid { display: grid; + align-content: start; margin: 0; padding: 0; list-style: none; @@ -726,6 +755,12 @@ a { text-align: center; } +.category-tools__viewport > .category-empty-state { + height: 100%; + min-height: 0; + padding-block: 20px; +} + .category-empty-state > svg { width: 36px; height: 36px; @@ -791,7 +826,8 @@ a { } .local-search-results, -.sidebar__nav { +.sidebar__nav, +.category-tools__viewport { scrollbar-color: rgba(100, 116, 139, 0.32) transparent; scrollbar-width: thin; } @@ -816,6 +852,126 @@ a { } } +@media (max-width: 999px) { + .tool-grid { + grid-template-columns: minmax(0, 1fr); + } +} + +@media (max-width: 767px) { + /* Keep category navigation usable without squeezing the boxed content. */ + .app-shell:has(.category-page) .sidebar { + inset: 12px 12px auto; + width: auto; + height: 124px; + } + + .app-shell:has(.category-page) .sidebar__header, + .app-shell:has(.category-page)[data-sidebar-collapsed="true"] + .sidebar__header { + min-height: 56px; + flex-direction: row; + padding: 0 12px; + } + + .app-shell:has(.category-page) .sidebar__header > button { + display: none; + } + + .app-shell:has(.category-page) .sidebar__brand { + justify-content: flex-start; + } + + .app-shell:has(.category-page) .sidebar__nav, + .app-shell:has(.category-page)[data-sidebar-collapsed="true"] .sidebar__nav { + flex-direction: row; + overflow-x: auto; + overflow-y: hidden; + padding: 8px 12px 12px; + } + + .app-shell:has(.category-page) .sidebar__categories { + flex: none; + flex-direction: row; + } + + .app-shell:has(.category-page) .sidebar__nav-item, + .app-shell:has(.category-page)[data-sidebar-collapsed="true"] + .sidebar__nav-item { + width: auto; + height: 40px; + min-height: 40px; + flex: none; + justify-content: flex-start; + padding-inline: 12px; + } + + .app-shell:has(.category-page) .sidebar__site-name.sr-only, + .app-shell:has(.category-page) .sidebar__label.sr-only { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: hidden; + clip: auto; + white-space: nowrap; + } + + .app-shell:has(.category-page) .app-shell__content { + margin-left: 0; + } + + .category-page { + padding: 148px 12px 12px; + } + + .category-page__panel { + padding: 28px 20px 36px; + } + + .category-tools__viewport { + padding: 12px; + } + + .category-header__title { + font-size: 32px; + } +} + +/* When the header would leave only a sliver of cards, let the page scroll instead. */ +@media (max-height: 540px), (max-width: 767px) and (max-height: 700px) { + .category-page { + height: auto; + min-height: 100svh; + } + + .category-page__panel { + height: auto; + min-height: calc(100svh - 40px); + overflow: visible; + } + + .category-tools { + flex: none; + } + + .category-tools__viewport { + overflow: visible; + flex: none; + } + + .category-tools__viewport > .category-empty-state { + height: auto; + min-height: 200px; + } +} + +@media (max-width: 767px) and (max-height: 700px) { + .category-page__panel { + min-height: calc(100svh - 160px); + } +} + @media (prefers-reduced-motion: reduce) { *, *::before, diff --git a/ts/web/nextjs/polaris/src/data/data.test.ts b/ts/web/nextjs/polaris/src/data/data.test.ts index bfe1124..7550243 100644 --- a/ts/web/nextjs/polaris/src/data/data.test.ts +++ b/ts/web/nextjs/polaris/src/data/data.test.ts @@ -27,9 +27,9 @@ describe("Polaris 静态数据", () => { } }) - it("包含 16 条合法链接且分类引用均存在", () => { + it("包含 45 条合法链接且分类引用均存在", () => { const categoryIds = new Set(categories.map(({ id }) => id)) - expect(links).toHaveLength(16) + expect(links).toHaveLength(45) expect(hasUniqueValues(links.map(({ id }) => id))).toBe(true) for (const link of links) { @@ -86,4 +86,33 @@ describe("Polaris 静态数据", () => { expect(Number.isFinite(settings.background.blur)).toBe(true) expect(Number.isFinite(settings.background.overlay)).toBe(true) }) + + describe("导入链接卫生检查", () => { + const normalizeUrl = (url: string) => url.replace(/\/+$/, "") + + it("全部链接 URL 唯一(忽略末尾斜杠)", () => { + const urls = links.map(({ url }) => normalizeUrl(url)) + expect(hasUniqueValues(urls)).toBe(true) + }) + + it("不包含本地或内网地址", () => { + for (const link of links) { + const { hostname } = new URL(link.url) + expect(hostname).toMatch(/^[\w.-]+$/) + expect(hostname).not.toBe("localhost") + expect(hostname).not.toMatch(/\.(local|lan|internal)$/) + expect(hostname).not.toMatch( + /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|169\.254\.|0\.0\.0\.0)/, + ) + } + }) + + it("不携带查询参数或片段,防止 sid/jumpuin 等会话标识混入", () => { + for (const link of links) { + const url = new URL(link.url) + expect([...url.searchParams.keys()]).toEqual([]) + expect(url.hash).toBe("") + } + }) + }) }) diff --git a/ts/web/nextjs/polaris/src/data/links.json b/ts/web/nextjs/polaris/src/data/links.json index f62db20..1c0885d 100644 --- a/ts/web/nextjs/polaris/src/data/links.json +++ b/ts/web/nextjs/polaris/src/data/links.json @@ -179,5 +179,298 @@ "pinnedSort": 80, "keywords": ["哔哩哔哩", "视频", "动漫", "社区"], "sort": 20 + }, + { + "id": "chatbot-arena", + "title": "Chatbot Arena", + "description": "大模型匿名对战与排行榜社区", + "url": "https://huggingface.co/spaces/lmarena-ai/chatbot-arena", + "categoryId": "ai", + "pinned": false, + "keywords": ["ai", "大模型", "排行榜", "对战"], + "sort": 20 + }, + { + "id": "go-stdlib-zh", + "title": "Go 标准库中文文档", + "description": "studygolang 维护的标准库中文参考", + "url": "https://studygolang.com/pkgdoc", + "categoryId": "dev", + "pinned": false, + "keywords": ["go", "golang", "标准库", "文档"], + "sort": 50 + }, + { + "id": "element-plus", + "title": "Element Plus", + "description": "Vue 3 组件库与组件总览文档", + "url": "https://element-plus.org", + "categoryId": "dev", + "pinned": false, + "keywords": ["vue", "组件库", "ui", "文档"], + "sort": 60 + }, + { + "id": "v-md-editor", + "title": "v-md-editor", + "description": "Vue Markdown 编辑器中文文档", + "url": "https://code-farmer-i.github.io/vue-markdown-editor/zh/", + "categoryId": "dev", + "pinned": false, + "keywords": ["vue", "markdown", "编辑器", "组件"], + "sort": 70 + }, + { + "id": "tsinghua-mirror", + "title": "清华镜像站", + "description": "清华 TUNA 开源软件镜像仓库", + "url": "https://mirrors.tuna.tsinghua.edu.cn", + "categoryId": "dev", + "pinned": false, + "keywords": ["镜像", "开源", "linux", "pypi"], + "sort": 100 + }, + { + "id": "haowallpaper", + "title": "哲风壁纸", + "description": "高质量免费壁纸下载站", + "url": "https://www.haowallpaper.com", + "categoryId": "design", + "pinned": false, + "keywords": ["壁纸", "素材", "背景", "4k"], + "sort": 20 + }, + { + "id": "leetcode", + "title": "LeetCode", + "description": "算法与数据结构刷题平台", + "url": "https://leetcode.cn", + "icon": "si:leetcode", + "categoryId": "learning", + "pinned": false, + "keywords": ["算法", "刷题", "面试", "题库"], + "sort": 10 + }, + { + "id": "socpk", + "title": "SOCPK", + "description": "手机 SoC 性能天梯图与排行", + "url": "https://www.socpk.com", + "categoryId": "efficiency", + "pinned": false, + "keywords": ["手机", "芯片", "天梯图", "性能"], + "sort": 90 + }, + { + "id": "wiki-52poke", + "title": "神奇宝贝百科", + "description": "宝可梦中文资料维基", + "url": "https://wiki.52poke.com/wiki/%E4%B8%BB%E9%A1%B5", + "categoryId": "media", + "pinned": false, + "keywords": ["宝可梦", "口袋妖怪", "攻略", "资料"], + "sort": 80 + }, + { + "id": "wiki-52poke-typechart", + "title": "宝可梦属性相克表", + "description": "宝可梦全属性克制关系速查", + "url": "https://wiki.52poke.com/wiki/%E5%B1%9E%E6%80%A7%E7%9B%B8%E5%85%8B%E8%A1%A8", + "categoryId": "media", + "pinned": false, + "keywords": ["宝可梦", "属性", "相克", "速查"], + "sort": 90 + }, + { + "id": "wiki-blhx", + "title": "碧蓝航线WIKI", + "description": "碧蓝航线官方中文百科", + "url": "https://wiki.biligame.com/blhx/%E9%A6%96%E9%A1%B5", + "categoryId": "media", + "pinned": false, + "keywords": ["碧蓝航线", "手游", "攻略", "wiki"], + "sort": 100 + }, + { + "id": "wiki-blhx-boss", + "title": "碧蓝航线月度BOSS解析", + "description": "大型作战月度 BOSS 攻略解析", + "url": "https://wiki.biligame.com/blhx/%E5%A4%A7%E5%9E%8B%E4%BD%9C%E6%88%98%E6%9C%88%E5%BA%A6BOSS%E8%A7%A3%E6%9E%90", + "categoryId": "media", + "pinned": false, + "keywords": ["碧蓝航线", "boss", "攻略", "大型作战"], + "sort": 110 + }, + { + "id": "wiki-fgo", + "title": "Mooncell", + "description": "FGO 中文维基与攻略资料站", + "url": "https://fgo.wiki/w/%E9%A6%96%E9%A1%B5", + "categoryId": "media", + "pinned": false, + "keywords": ["fgo", "mooncell", "从者", "攻略"], + "sort": 120 + }, + { + "id": "wiki-stardew", + "title": "星露谷物语中文维基", + "description": "星露谷物语官方中文资料维基", + "url": "https://zh.stardewvalleywiki.com", + "categoryId": "media", + "pinned": false, + "keywords": ["星露谷物语", "攻略", "作物", "wiki"], + "sort": 130 + }, + { + "id": "wiki-terraria", + "title": "泰拉瑞亚中文维基", + "description": "泰拉瑞亚官方中文百科", + "url": "https://terraria.wiki.gg/zh", + "categoryId": "media", + "pinned": false, + "keywords": ["泰拉瑞亚", "攻略", "物品", "wiki"], + "sort": 140 + }, + { + "id": "wiki-moegirl", + "title": "萌娘百科", + "description": "ACG 中文百科社区", + "url": "https://zh.moegirl.org.cn/zh-hans/Mainpage", + "categoryId": "media", + "pinned": false, + "keywords": ["动漫", "acg", "百科", "二次元"], + "sort": 150 + }, + { + "id": "farmgamehub", + "title": "星露谷洒水器规划器", + "description": "星露谷物语洒水器布局可视化工具", + "url": "https://www.farmgamehub.com/zh/tools/stardew-sprinklers", + "categoryId": "media", + "pinned": false, + "keywords": ["星露谷物语", "洒水器", "布局", "工具"], + "sort": 160 + }, + { + "id": "iodraw", + "title": "iodraw", + "description": "在线思维导图与白板工具", + "url": "https://www.iodraw.com/mind", + "categoryId": "design", + "pinned": false, + "keywords": ["思维导图", "脑图", "白板", "在线"], + "sort": 30 + }, + { + "id": "google-translate", + "title": "Google 翻译", + "description": "Google 在线机器翻译", + "url": "https://translate.google.com", + "icon": "si:googletranslate", + "categoryId": "efficiency", + "pinned": false, + "keywords": ["翻译", "翻译器", "英文", "中英"], + "sort": 40 + }, + { + "id": "bing-translate", + "title": "微软翻译", + "description": "微软必应在线翻译", + "url": "https://www.bing.com/translator", + "categoryId": "efficiency", + "pinned": false, + "keywords": ["翻译", "必应", "英文", "中英"], + "sort": 50 + }, + { + "id": "oschina-encrypt", + "title": "OSChina 加密解密", + "description": "在线加密解密工具集", + "url": "https://tool.oschina.net/encrypt", + "categoryId": "efficiency", + "pinned": false, + "keywords": ["加密", "解密", "base64", "工具"], + "sort": 60 + }, + { + "id": "tencent-cloud-console", + "title": "腾讯云控制台", + "description": "腾讯云服务管理控制台", + "url": "https://console.cloud.tencent.com", + "categoryId": "efficiency", + "pinned": false, + "keywords": ["腾讯云", "控制台", "云服务", "服务器"], + "sort": 70 + }, + { + "id": "aliyun-console", + "title": "阿里云轻量控制台", + "description": "阿里云轻量应用服务器控制台", + "url": "https://swas.console.aliyun.com", + "categoryId": "efficiency", + "pinned": false, + "keywords": ["阿里云", "控制台", "轻量服务器", "云服务"], + "sort": 80 + }, + { + "id": "spotify", + "title": "Spotify", + "description": "在线音乐流媒体播放器", + "url": "https://open.spotify.com", + "icon": "si:spotify", + "categoryId": "media", + "pinned": false, + "keywords": ["音乐", "播放器", "歌单", "流媒体"], + "sort": 30 + }, + { + "id": "nexusmods", + "title": "Nexus Mods", + "description": "星露谷等游戏 MOD 下载社区", + "url": "https://www.nexusmods.com/games/stardewvalley/mods", + "categoryId": "media", + "pinned": false, + "keywords": ["mod", "游戏", "星露谷物语", "下载"], + "sort": 40 + }, + { + "id": "pokemon-showdown", + "title": "Pokemon Showdown", + "description": "宝可梦在线对战模拟器", + "url": "https://play.pokemonshowdown.com", + "categoryId": "media", + "pinned": false, + "keywords": ["宝可梦", "对战", "游戏", "在线"], + "sort": 50 + }, + { + "id": "expreview", + "title": "超能网", + "description": "硬件评测与数码资讯媒体", + "url": "https://www.expreview.com", + "categoryId": "media", + "pinned": false, + "keywords": ["硬件", "评测", "数码", "装机"], + "sort": 60 + }, + { + "id": "chairo", + "title": "Chairo 导航", + "description": "二次元 ACG 网站导航", + "url": "https://www.chairo.cc", + "categoryId": "media", + "pinned": false, + "keywords": ["导航", "二次元", "acg", "动漫"], + "sort": 70 + }, + { + "id": "amap", + "title": "高德地图", + "description": "在线地图与出行导航", + "url": "https://www.amap.com", + "categoryId": "life", + "pinned": false, + "keywords": ["地图", "导航", "出行", "路线"], + "sort": 10 } ] diff --git a/ts/web/nextjs/polaris/src/features/category/components/category-content.test.tsx b/ts/web/nextjs/polaris/src/features/category/components/category-content.test.tsx index 04070a2..3b8a798 100644 --- a/ts/web/nextjs/polaris/src/features/category/components/category-content.test.tsx +++ b/ts/web/nextjs/polaris/src/features/category/components/category-content.test.tsx @@ -7,29 +7,39 @@ import { getLinksByCategory } from "@/services/links" import { CategoryContent } from "./category-content" describe("CategoryContent", () => { - it("展示开发工具标题、描述、数量和 8 张卡片", () => { + it("展示开发工具标题、描述、数量和链接卡片", () => { const category = getCategoryById("dev") if (!category) throw new Error("测试数据缺少 dev 分类") + const links = getLinksByCategory(category.id) const { container } = render( - , + , ) expect( screen.getByRole("heading", { level: 1, name: "开发工具" }), ).toBeInTheDocument() expect(screen.getByText(category.description)).toBeInTheDocument() - expect(screen.getByText("共 8 个")).toBeInTheDocument() - expect(container.querySelectorAll(".tool-card")).toHaveLength(8) + expect(screen.getByText(`共 ${links.length} 个`)).toBeInTheDocument() + expect(container.querySelectorAll(".tool-card")).toHaveLength(links.length) expect(screen.queryByText("暂无工具")).not.toBeInTheDocument() + const viewport = screen.getByRole("region", { name: "全部工具" }) + expect(viewport).toHaveAttribute("tabindex", "0") + expect(viewport).toContainElement( + screen.getByRole("list", { name: "工具列表" }), + ) + expect(viewport).not.toContainElement( + screen.getByText(`共 ${links.length} 个`), + ) + expect( + container.querySelector(".category-page__panel"), + ).not.toHaveAttribute("tabindex") + const toolLinks = screen .getByRole("list", { name: "工具列表" }) .querySelectorAll("a") - expect(toolLinks).toHaveLength(8) + expect(toolLinks).toHaveLength(links.length) for (const link of toolLinks) { expect(link).toHaveAttribute("target", "_blank") expect(link).toHaveAttribute("rel", "noopener noreferrer") @@ -37,19 +47,18 @@ describe("CategoryContent", () => { }) it("为空分类展示计数和纯浏览空状态", () => { + // life 分类当前已有数据,空状态契约改用空 links 夹具保证始终可测。 const category = getCategoryById("life") if (!category) throw new Error("测试数据缺少 life 分类") - render( - , - ) + render() expect(screen.getByText("共 0 个")).toBeInTheDocument() expect(screen.getByText("暂无工具")).toBeInTheDocument() expect(screen.getByText("这个分类下还没有添加链接。")).toBeInTheDocument() + const viewport = screen.getByRole("region", { name: "全部工具" }) + expect(viewport).toContainElement(screen.getByText("暂无工具")) + expect(viewport).not.toHaveAttribute("tabindex") expect(screen.queryByRole("button")).not.toBeInTheDocument() expect( screen.queryByRole("list", { name: "工具列表" }), @@ -57,24 +66,20 @@ describe("CategoryContent", () => { }) it.each([ - ["ai", 3], - ["dev", 8], - ["design", 1], - ["learning", 0], - ["efficiency", 2], - ["media", 2], - ["life", 0], - ] as const)("%s 分类展示正确数量", (categoryId, count) => { + "ai", + "dev", + "design", + "learning", + "efficiency", + "media", + "life", + ] as const)("%s 分类展示服务提供的数量", (categoryId) => { const category = getCategoryById(categoryId) if (!category) throw new Error(`测试数据缺少 ${categoryId} 分类`) + const links = getLinksByCategory(category.id) - render( - , - ) + render() - expect(screen.getByText(`共 ${count} 个`)).toBeInTheDocument() + expect(screen.getByText(`共 ${links.length} 个`)).toBeInTheDocument() }) }) diff --git a/ts/web/nextjs/polaris/src/features/category/components/category-content.tsx b/ts/web/nextjs/polaris/src/features/category/components/category-content.tsx index 8e2803e..8c0f7e6 100644 --- a/ts/web/nextjs/polaris/src/features/category/components/category-content.tsx +++ b/ts/web/nextjs/polaris/src/features/category/components/category-content.tsx @@ -15,16 +15,20 @@ export function CategoryContent({ category, links }: CategoryContentProps) { - + - {links.length > 0 ? ( - - ) : ( - - )} + 0 ? 0 : undefined} + > + {links.length > 0 ? ( + + ) : ( + + )} + diff --git a/ts/web/nextjs/polaris/src/services/services.test.ts b/ts/web/nextjs/polaris/src/services/services.test.ts index 9b96434..2be4b00 100644 --- a/ts/web/nextjs/polaris/src/services/services.test.ts +++ b/ts/web/nextjs/polaris/src/services/services.test.ts @@ -33,14 +33,18 @@ describe("静态数据 service", () => { const first = getLinksByCategory("dev") const second = getLinksByCategory("dev") - expect(first).toHaveLength(8) + expect(first).toHaveLength(12) expect(first.map(({ id }) => id)).toEqual([ "github", "gitee", "go", "go-zero", + "go-stdlib-zh", + "element-plus", + "v-md-editor", "docker", "kubernetes", + "tsinghua-mirror", "npm", "pnpm", ]) @@ -49,8 +53,10 @@ describe("静态数据 service", () => { expect(first[0].keywords).not.toBe(second[0].keywords) expect(getLinksByCategory("design").map(({ id }) => id)).toEqual([ "iconfont", + "haowallpaper", + "iodraw", ]) - expect(getLinksByCategory("life")).toEqual([]) + expect(getLinksByCategory("life").map(({ id }) => id)).toEqual(["amap"]) }) it("按 pinnedSort 返回首页 6 个常用链接", () => {