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:
@@ -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))} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user