From a366bd1400ed5400f767bdd1b985fd1245bbdb93 Mon Sep 17 00:00:00 2001 From: zhangxiang Date: Mon, 28 Sep 2026 10:45:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(qa):=20=E7=AC=AC=E4=BA=94=E8=BD=AE=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=80=A7=E8=B4=A8=E9=87=8F=E4=BF=9D=E9=9A=9C=E7=BC=BA?= =?UTF-8?q?=E9=99=B7=E4=BF=AE=E5=A4=8D=20+=20=E5=8D=95=E5=85=83/=E9=9B=86?= =?UTF-8?q?=E6=88=90=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 安全/鉴权:登出清服务端 httpOnly 令牌、登录 IP 限流前置 bcrypt、停用账号 令牌在 6 个内联 role handler 失效、admin/items 存在性 oracle 认证前置、 同意偏好 sanitize fail-closed、api-crypto 按 HTTP 方法判体。 CMS:workflow 状态更新+审计收进交互式 $transaction(失败即回滚)、编辑器 richtext 走 textarea 防换行损毁、array/object 子字段按作用域读写防污染、 about/contact/erp-upgrade 补 ISR revalidate 与路由映射。 UI/a11y:AnimatedCounter 入视口前显起点、CLS 累计上报、MetricCard 动画入 effect 并卸载取消 rAF、后台状态四态映射/搜索防抖+序号守卫/媒体单 input/ 吞错改横幅/表单 label-aria 关联。 新增对应判别性单测与 pagination/rate-limit/client-ip/sanitize/validate-content-data 等模块及 tests-integration 真库集成层。 --- .../(marketing)/about/about-content-v4.tsx | 43 +- .../(marketing)/about/brand/brand-content.tsx | 18 +- src/app/(marketing)/about/page.test.tsx | 18 - src/app/(marketing)/about/page.tsx | 2 + .../(marketing)/cases/[slug]/client.test.tsx | 85 +++ src/app/(marketing)/cases/[slug]/client.tsx | 16 +- .../(marketing)/cases/[slug]/page.test.tsx | 15 + src/app/(marketing)/cases/[slug]/page.tsx | 5 +- .../(marketing)/cases/cases-content-v3.tsx | 30 +- .../contact/contact-content-v3.test.tsx | 12 + .../contact/contact-content-v3.tsx | 33 +- src/app/(marketing)/contact/page.tsx | 2 + src/app/(marketing)/home-content-v15.test.tsx | 2 +- src/app/(marketing)/home-content-v15.tsx | 36 +- .../methodology/methodology-content.tsx | 8 +- .../news/[slug]/NewsDetailClient.tsx | 18 +- src/app/(marketing)/news/news-content-v3.tsx | 6 +- .../news/news-detail-content-v3.tsx | 8 +- .../erp-upgrade/erp-upgrade-content-v2.tsx | 10 +- .../(marketing)/products/erp-upgrade/page.tsx | 2 + .../products/product-detail-content-v3.tsx | 18 +- .../products/products-content-v3.test.tsx | 18 +- .../products/products-content-v3.tsx | 21 +- .../products/standalone/[id]/client.tsx | 16 +- .../services/service-detail-content-v4.tsx | 137 ++-- .../services/services-content-v3.tsx | 32 +- .../solutions/solution-detail-content-v3.tsx | 24 +- .../solutions/solutions-content-v3.tsx | 22 +- src/app/(marketing)/team/team-content-v3.tsx | 25 +- src/app/(marketing)/template.tsx | 2 +- src/app/__tests__/cms-page-revalidate.test.ts | 23 + .../[modelCode]/[itemId]/page.test.tsx | 616 ++++++++++++++++++ .../content/[modelCode]/[itemId]/page.tsx | 507 ++++++++------ .../admin/content/[modelCode]/page.test.tsx | 154 +++++ src/app/admin/content/[modelCode]/page.tsx | 32 +- src/app/admin/login/page.tsx | 9 +- src/app/admin/media/page.tsx | 29 +- src/app/admin/notifications/page.tsx | 11 +- src/app/admin/page.tsx | 11 +- src/app/admin/roles/page.tsx | 18 +- src/app/admin/users/page.tsx | 40 +- src/app/admin/zones/page.tsx | 16 +- .../admin/items/[id]/workflow/route.test.ts | 17 + .../api/admin/items/[id]/workflow/route.ts | 9 +- src/app/api/admin/items/route.test.ts | 277 +++++++- src/app/api/admin/items/route.ts | 76 ++- src/app/api/admin/media/route.test.ts | 37 ++ src/app/api/admin/media/route.ts | 46 +- src/app/api/admin/roles/route.test.ts | 134 +++- src/app/api/admin/roles/route.ts | 37 +- src/app/api/admin/stats/route.ts | 11 +- src/app/api/admin/users/route.test.ts | 320 +++++++++ src/app/api/admin/users/route.ts | 94 ++- src/app/api/auth/login/route.test.ts | 175 +++++ src/app/api/auth/login/route.ts | 34 +- src/app/api/auth/refresh/route.test.ts | 158 +++++ src/app/api/auth/refresh/route.ts | 20 +- src/app/api/cms/draft/disable/route.test.ts | 83 +++ src/app/api/cms/draft/disable/route.ts | 9 + src/app/api/cms/draft/enable/route.test.ts | 135 ++++ src/app/api/cms/draft/enable/route.ts | 20 +- src/app/api/cms/revalidate/route.test.ts | 29 + src/app/api/cms/revalidate/route.ts | 16 + src/app/api/contact/route.ts | 116 ++-- src/app/error.tsx | 8 +- src/app/global-error.test.tsx | 33 + src/app/global-error.tsx | 76 +++ src/app/globals.css | 128 +--- src/app/layout.tsx | 41 +- src/app/not-found-content.tsx | 10 +- src/app/privacy/page.test.tsx | 34 +- src/app/privacy/page.tsx | 5 +- src/app/terms/page.test.tsx | 38 +- src/app/terms/page.tsx | 5 +- src/components/admin/admin-layout.tsx | 14 +- src/components/admin/auth-context.test.tsx | 61 ++ src/components/admin/auth-context.tsx | 9 + src/components/analytics/CookieConsent.tsx | 19 +- .../analytics/GoogleAnalytics.test.tsx | 101 ++- src/components/analytics/GoogleAnalytics.tsx | 42 +- .../analytics/PerformanceTracker.test.tsx | 16 + .../analytics/PerformanceTracker.tsx | 4 +- src/components/cms/RichTextEditor.test.tsx | 314 --------- src/components/cms/RichTextEditor.tsx | 204 ------ src/components/content/sections.test.tsx | 381 ++++++++++- src/components/content/sections.tsx | 30 +- src/components/content/testimonials.test.tsx | 55 -- src/components/content/testimonials.tsx | 155 ----- .../detail/detail-case-studies-section.tsx | 8 +- src/components/detail/detail-case-study.tsx | 2 +- .../detail/detail-certification.tsx | 2 +- .../detail/detail-certifications-section.tsx | 2 +- src/components/detail/detail-cta-section.tsx | 6 +- src/components/detail/detail-hero.tsx | 38 +- .../detail/detail-product-value.tsx | 50 +- .../detail/detail-trust-section.tsx | 36 +- src/components/detail/detail.test.tsx | 57 +- src/components/detail/index.ts | 1 - src/components/detail/l3-signals-slot.tsx | 4 +- src/components/detail/list-page-hero.test.tsx | 152 ----- src/components/detail/list-page-hero.tsx | 166 ----- .../detail/micro-interactions.test.tsx | 30 + src/components/detail/micro-interactions.tsx | 35 +- src/components/detail/product-card.tsx | 4 +- src/components/detail/service-value.tsx | 8 +- .../detail/solution-service-card.tsx | 18 +- src/components/detail/solution-value.tsx | 16 +- .../examples/ContactFormAnalyticsExample.tsx | 107 --- src/components/layout/footer.tsx | 2 +- src/components/layout/header.test.tsx | 68 +- src/components/layout/header.tsx | 31 +- src/components/layout/index.ts | 7 - src/components/layout/mega-dropdown.test.tsx | 88 +++ src/components/layout/mega-dropdown.tsx | 34 +- src/components/layout/mobile-tab-bar.tsx | 4 +- src/components/layout/page-nav.tsx | 43 -- src/components/sections/case-card.tsx | 2 +- .../sections/case-detail-page.test.tsx | 529 +++++++++++++++ src/components/sections/case-detail-page.tsx | 251 ++++--- src/components/sections/challenge-section.tsx | 4 +- src/components/sections/cta-section.tsx | 6 +- .../hero-particle-field-engine.test.tsx | 507 ++++++++++++++ .../sections/hero-particle-field.tsx | 6 +- src/components/sections/hero-section-v2.tsx | 22 +- src/components/sections/index.ts | 24 - src/components/sections/industry-grid.tsx | 4 +- src/components/sections/insight-card.tsx | 2 +- .../sections/methodology-section.tsx | 12 +- src/components/sections/product-card.tsx | 12 +- .../sections/product-matrix-section.tsx | 6 +- src/components/sections/question-card.tsx | 6 +- src/components/sections/section-header.tsx | 6 +- src/components/sections/sections.test.tsx | 43 +- src/components/sections/service-card.tsx | 6 +- .../sections/social-proof-section.tsx | 22 +- src/components/sections/stats-bar.tsx | 131 ---- src/components/sections/why-us-section.tsx | 24 +- src/components/seo/structured-data.test.tsx | 18 + src/components/seo/structured-data.tsx | 23 +- src/components/ui/accordion.test.tsx | 128 ---- src/components/ui/accordion.tsx | 65 -- src/components/ui/animated-counter.test.tsx | 37 +- src/components/ui/animated-counter.tsx | 4 +- src/components/ui/back-to-top.tsx | 5 +- src/components/ui/brand-visuals.tsx | 18 +- src/components/ui/button.test.tsx | 25 +- src/components/ui/button.tsx | 18 +- src/components/ui/challenge-card.tsx | 6 +- src/components/ui/cta-button.tsx | 8 +- src/components/ui/dialog.test.tsx | 131 ---- src/components/ui/dialog.tsx | 124 ---- src/components/ui/flip-clock.tsx | 148 ----- src/components/ui/index.ts | 201 ------ src/components/ui/loading-skeleton.test.tsx | 150 ----- src/components/ui/loading-skeleton.tsx | 96 --- src/components/ui/loading-state.tsx | 4 +- src/components/ui/metric-card.test.tsx | 104 --- src/components/ui/metric-card.tsx | 123 ---- src/components/ui/milestone-timeline.tsx | 129 ---- src/components/ui/motion-provider.tsx | 9 + src/components/ui/page-transition.tsx | 4 +- src/components/ui/product-card.tsx | 8 +- src/components/ui/scroll-progress.test.tsx | 28 +- src/components/ui/scroll-progress.tsx | 2 +- src/components/ui/scroll-reveal.tsx | 11 +- src/components/ui/select.test.tsx | 136 ---- src/components/ui/select.tsx | 168 ----- src/components/ui/stats-showcase.tsx | 142 ---- src/components/ui/tabs.test.tsx | 148 ----- src/components/ui/tabs.tsx | 65 -- src/hooks/use-count-up.test.ts | 6 +- src/hooks/use-focus-trap.test.tsx | 209 +++++- src/hooks/use-focus-trap.ts | 120 +++- src/hooks/use-keyboard-shortcuts.test.ts | 293 --------- src/hooks/use-keyboard-shortcuts.ts | 45 -- .../use-swipe-gesture.components.test.tsx | 60 +- src/hooks/use-swipe-gesture.tsx | 14 +- src/instrumentation-client.ts | 94 +++ src/instrumentation.ts | 32 + src/lib/admin-api.test.ts | 56 ++ src/lib/admin-api.ts | 41 +- src/lib/analytics.test.ts | 79 ++- src/lib/analytics.ts | 40 +- src/lib/api-crypto.test.ts | 41 +- src/lib/api-crypto.ts | 9 +- src/lib/api-response.test.ts | 13 +- src/lib/client-ip.test.ts | 56 ++ src/lib/client-ip.ts | 27 + src/lib/cms/data-server.test.ts | 93 +++ src/lib/cms/data-server.ts | 19 +- src/lib/cms/internal-redirect.ts | 10 + src/lib/cms/types.ts | 14 +- src/lib/cms/validate-content-data.test.ts | 183 ++++++ src/lib/cms/validate-content-data.ts | 123 ++++ src/lib/cms/workflow.test.ts | 161 ++++- src/lib/cms/workflow.ts | 99 ++- src/lib/colors.test.ts | 183 ------ src/lib/colors.ts | 100 --- src/lib/constants/company.ts | 2 +- src/lib/constants/design-system.test.ts | 14 +- src/lib/constants/design-system.ts | 10 +- src/lib/db.test.ts | 76 ++- src/lib/db.ts | 38 +- src/lib/gradients.test.ts | 132 ---- src/lib/gradients.ts | 45 -- src/lib/media/image-processor.test.ts | 31 +- src/lib/media/image-processor.ts | 3 +- src/lib/media/media-service.test.ts | 95 ++- src/lib/media/media-service.ts | 55 +- src/lib/media/storage.test.ts | 38 ++ src/lib/media/storage.ts | 23 +- src/lib/media/upload-policy.test.ts | 182 ++++++ src/lib/media/upload-policy.ts | 145 +++++ src/lib/pagination.test.ts | 33 + src/lib/pagination.ts | 34 + src/lib/permissions.test.ts | 117 +++- src/lib/permissions.ts | 86 ++- src/lib/rate-limit.test.ts | 41 ++ src/lib/rate-limit.ts | 42 ++ src/lib/sanitize.test.ts | 110 ++++ src/lib/sanitize.ts | 51 ++ src/lib/seed-metric-basis.test.ts | 70 ++ src/lib/seed-permissions.test.ts | 68 ++ src/lib/utils.test.ts | 40 ++ 224 files changed, 8938 insertions(+), 5579 deletions(-) create mode 100644 src/app/(marketing)/cases/[slug]/client.test.tsx create mode 100644 src/app/__tests__/cms-page-revalidate.test.ts create mode 100644 src/app/admin/content/[modelCode]/[itemId]/page.test.tsx create mode 100644 src/app/admin/content/[modelCode]/page.test.tsx create mode 100644 src/app/api/admin/users/route.test.ts create mode 100644 src/app/api/auth/login/route.test.ts create mode 100644 src/app/api/auth/refresh/route.test.ts create mode 100644 src/app/api/cms/draft/disable/route.test.ts create mode 100644 src/app/api/cms/draft/enable/route.test.ts create mode 100644 src/app/global-error.test.tsx create mode 100644 src/app/global-error.tsx create mode 100644 src/components/admin/auth-context.test.tsx delete mode 100644 src/components/cms/RichTextEditor.test.tsx delete mode 100644 src/components/cms/RichTextEditor.tsx delete mode 100644 src/components/content/testimonials.test.tsx delete mode 100644 src/components/content/testimonials.tsx delete mode 100644 src/components/detail/list-page-hero.test.tsx delete mode 100644 src/components/detail/list-page-hero.tsx create mode 100644 src/components/detail/micro-interactions.test.tsx delete mode 100644 src/components/examples/ContactFormAnalyticsExample.tsx delete mode 100644 src/components/layout/index.ts create mode 100644 src/components/layout/mega-dropdown.test.tsx delete mode 100644 src/components/layout/page-nav.tsx create mode 100644 src/components/sections/case-detail-page.test.tsx create mode 100644 src/components/sections/hero-particle-field-engine.test.tsx delete mode 100644 src/components/sections/index.ts delete mode 100644 src/components/sections/stats-bar.tsx delete mode 100644 src/components/ui/accordion.test.tsx delete mode 100644 src/components/ui/accordion.tsx delete mode 100644 src/components/ui/dialog.test.tsx delete mode 100644 src/components/ui/dialog.tsx delete mode 100644 src/components/ui/flip-clock.tsx delete mode 100644 src/components/ui/index.ts delete mode 100644 src/components/ui/loading-skeleton.test.tsx delete mode 100644 src/components/ui/loading-skeleton.tsx delete mode 100644 src/components/ui/metric-card.test.tsx delete mode 100644 src/components/ui/metric-card.tsx delete mode 100644 src/components/ui/milestone-timeline.tsx create mode 100644 src/components/ui/motion-provider.tsx delete mode 100644 src/components/ui/select.test.tsx delete mode 100644 src/components/ui/select.tsx delete mode 100644 src/components/ui/stats-showcase.tsx delete mode 100644 src/components/ui/tabs.test.tsx delete mode 100644 src/components/ui/tabs.tsx delete mode 100644 src/hooks/use-keyboard-shortcuts.test.ts delete mode 100644 src/hooks/use-keyboard-shortcuts.ts create mode 100644 src/instrumentation-client.ts create mode 100644 src/instrumentation.ts create mode 100644 src/lib/client-ip.test.ts create mode 100644 src/lib/client-ip.ts create mode 100644 src/lib/cms/internal-redirect.ts create mode 100644 src/lib/cms/validate-content-data.test.ts create mode 100644 src/lib/cms/validate-content-data.ts delete mode 100644 src/lib/colors.test.ts delete mode 100644 src/lib/colors.ts delete mode 100644 src/lib/gradients.test.ts delete mode 100644 src/lib/gradients.ts create mode 100644 src/lib/media/upload-policy.test.ts create mode 100644 src/lib/media/upload-policy.ts create mode 100644 src/lib/pagination.test.ts create mode 100644 src/lib/pagination.ts create mode 100644 src/lib/rate-limit.test.ts create mode 100644 src/lib/rate-limit.ts create mode 100644 src/lib/sanitize.test.ts create mode 100644 src/lib/sanitize.ts create mode 100644 src/lib/seed-metric-basis.test.ts create mode 100644 src/lib/seed-permissions.test.ts diff --git a/src/app/(marketing)/about/about-content-v4.tsx b/src/app/(marketing)/about/about-content-v4.tsx index d99c952..2a223d0 100644 --- a/src/app/(marketing)/about/about-content-v4.tsx +++ b/src/app/(marketing)/about/about-content-v4.tsx @@ -57,7 +57,7 @@ function HeroSection({ data }: { data: AboutData }) { {data.heroSubtitle || ''} @@ -66,8 +66,8 @@ function HeroSection({ data }: { data: AboutData }) { {titleLines.map((line, i) => (
0 ? 'mt-4 sm:mt-6' : ''}`}> @@ -80,7 +80,7 @@ function HeroSection({ data }: { data: AboutData }) { style={{ transformOrigin: 'left' }} initial={{ scaleX: 0 }} animate={{ scaleX: 1 }} - transition={{ duration: 0.3, delay: 1.2, ease: EASE_OUT }} + transition={{ duration: 0.28, delay: 1.2, ease: EASE_OUT }} /> @@ -94,7 +94,7 @@ function HeroSection({ data }: { data: AboutData }) { {data.heroDescription || ''} @@ -103,7 +103,7 @@ function HeroSection({ data }: { data: AboutData }) { @@ -137,7 +137,7 @@ function MetricsStrip({ data }: { data: AboutData }) { initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: '-80px' }} - transition={{ duration: 0.3, delay: i * 0.06, ease: EASE_OUT }} + transition={{ duration: 0.28, delay: i * 0.06, ease: EASE_OUT }} className="text-center lg:text-left" >
@@ -165,7 +165,7 @@ function WhoWeAreSection({ data }: { data: AboutData }) { initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: '-80px' }} - transition={{ duration: 0.3, ease: EASE_OUT }} + transition={{ duration: 0.28, ease: EASE_OUT }} className="lg:col-span-5" >
@@ -194,7 +194,7 @@ function WhoWeAreSection({ data }: { data: AboutData }) { initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: '-80px' }} - transition={{ duration: 0.3, delay: 0.15, ease: EASE_OUT }} + transition={{ duration: 0.28, delay: 0.15, ease: EASE_OUT }} className="lg:col-span-7" >
@@ -213,12 +213,13 @@ function WhoWeAreSection({ data }: { data: AboutData }) { className="group relative border-b border-border-primary pb-6 md:pb-8 last:border-0 last:pb-0" >
- {/* 纯装饰序号:与右侧 value.title 并列,无独立语义,标记 aria-hidden 豁免对比度要求 */} -