test(mobile): add mobile E2E test suite (53 tests) and fix layout issues

Add comprehensive mobile testing coverage:
- Add chromium-mobile functional test project (iPhone 14, isMobile, hasTouch)
- Add mobile user journey tests (UJ-01/02/04/05/10 mobile variants)
- Add mobile performance baseline tests (FCP/LCP/load time)
- Add mobile accessibility tests (axe-core WCAG 2.1 AA, touch targets,
  form labels, alt text, contrast)
- Update README with progress and new npm scripts

Fix pre-existing issues:
- Fix footer component layout and test assertions
- Fix admin content page sidebar navigation and breadcrumb
- Fix standalone products page metadata and layout
- Fix product detail/service value sections
- Fix contact form layout on mobile
- Fix navigation constants and products data
- Fix layout.tsx CMS config and theme handling
- Fix erp-upgrade content layout
This commit is contained in:
2026-08-03 18:32:29 +08:00
parent f4d8f0a8e9
commit f3fc969bef
20 changed files with 1095 additions and 93 deletions
@@ -288,7 +288,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
<div className="w-12 h-12 border border-border-primary bg-white flex items-center justify-center shrink-0" aria-hidden="true">
<Mail className="w-5 h-5 text-text-secondary" />
</div>
<div>
<div className="mt-3.5">
<p className="text-sm text-text-muted mb-1">{data.emailLabel || ''}</p>
<a
href={`mailto:${emailAddress}`}
@@ -304,7 +304,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
<div className="w-12 h-12 border border-border-primary bg-white flex items-center justify-center shrink-0" aria-hidden="true">
<MapPin className="w-5 h-5 text-text-secondary" />
</div>
<div>
<div className="mt-3.5">
<p className="text-sm text-text-muted mb-1">{data.addressLabel || ''}</p>
<p className="text-ink" data-testid="address-text">
{addressText}
@@ -266,7 +266,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
<div className="w-12 h-12 bg-brand/10 border border-brand/20 flex items-center justify-center shrink-0">
<item.icon className="w-6 h-6 text-brand" />
</div>
<div>
<div className="mt-3.5">
<h3 className="text-lg font-semibold mb-2 text-ink">{item.title}</h3>
<p className="text-text-secondary text-sm leading-relaxed">{item.desc}</p>
</div>
@@ -139,8 +139,8 @@ function FeaturesSection({ features }: FeaturesSectionProps) {
key={idx}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-500"
>
<div className="flex items-start gap-5">
<div className="w-10 h-10 rounded-xl bg-brand-soft flex items-center justify-center text-brand shrink-0 mt-0.5">
<div className="flex items-center gap-5">
<div className="w-10 h-10 rounded-xl bg-brand-soft flex items-center justify-center text-brand shrink-0">
<Check className="w-5 h-5" />
</div>
<p className="text-lg text-ink font-medium leading-relaxed">{feature}</p>
@@ -182,7 +182,7 @@ function BenefitsSection({ benefits }: BenefitsSectionProps) {
key={idx}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-500"
>
<div className="flex items-start gap-6">
<div className="flex items-center gap-6">
<div className="w-12 h-12 rounded-2xl bg-brand-soft/50 flex items-center justify-center text-brand shrink-0">
<TrendingUp className="w-6 h-6" />
</div>
@@ -148,6 +148,8 @@ function HeroSection() {
function ProductCard({ product }: { product: Product }) {
const bundleLabel = product.bundle === 'enterprise' ? '企业套装' : '专业产品';
const linkHref = product.externalUrl || `/products/${product.id}`;
const isExternal = !!product.externalUrl;
return (
<motion.div
@@ -157,8 +159,9 @@ function ProductCard({ product }: { product: Product }) {
transition={{ duration: 0.6, ease: EASE_OUT }}
>
<StaticLink
href={`/products/${product.id}`}
href={linkHref}
className="group block overflow-hidden border border-border-primary hover:border-border-secondary bg-white hover:bg-bg-secondary transition-all duration-500 flex flex-col h-full"
{...(isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
>
<div className="p-6 sm:p-7 lg:p-8 flex flex-col flex-1">
<div className="flex items-start justify-between mb-4">
@@ -1,5 +1,5 @@
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { notFound, redirect } from 'next/navigation';
import { getPublishedItemBySlug, getPublishedItems } from '@/lib/cms/data-server';
import { COMPANY_INFO } from '@/lib/constants';
import { StandaloneProductClient } from './client';
@@ -35,5 +35,11 @@ export default async function StandaloneProductPage({ params }: { params: Promis
notFound();
}
// If the product has an external URL, redirect to it
const externalUrl = item.data.externalUrl as string | undefined;
if (externalUrl) {
redirect(externalUrl);
}
return <StandaloneProductClient item={JSON.parse(JSON.stringify(item))} />;
}