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
+34 -4
View File
@@ -14,7 +14,7 @@ import { ScrollProgress } from "@/components/ui/scroll-progress";
import { BackToTop } from "@/components/ui/back-to-top";
import { ClientLayout } from "@/components/layout/client-layout";
import { getPublishedItems } from "@/lib/cms/data-server";
import { SiteConfigProvider, type SiteConfig, type NavigationItem, type MegaDropdownGroup } from "@/lib/site-config";
import { SiteConfigProvider, type SiteConfig, type NavigationItem, type MegaDropdownGroup, type MegaDropdownItem } from "@/lib/site-config";
import { COMPANY_INFO } from "@/lib/constants/company";
import { NAVIGATION_V2, MEGA_DROPDOWN_DATA } from "@/lib/constants/navigation";
@@ -82,15 +82,45 @@ export default async function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
// Fetch site configuration and navigation from CMS
const [navItems, configItems] = await Promise.all([
// Fetch site configuration, navigation, and standalone products from CMS
const [navItems, configItems, standaloneItems] = await Promise.all([
getPublishedItems('navigation').catch(() => []),
getPublishedItems('site-config').catch(() => []),
getPublishedItems('standalone-product').catch(() => []),
]);
const navData = navItems[0]?.data as Record<string, unknown> | undefined;
const configData = configItems[0]?.data as Record<string, unknown> | undefined;
// Generate standalone product navigation items from CMS data
const standaloneNavItems: MegaDropdownItem[] = standaloneItems.map((item) => {
const data = item.data as Record<string, unknown>;
const externalUrl = data.externalUrl as string | undefined;
return {
id: (data.id as string) || item.slug || '',
title: (data.title as string) || item.title,
description: (data.description as string) || '',
href: externalUrl || `/products/standalone/${item.slug || data.id}`,
badge: (data.status as string) === '内测中' ? '内测中' : undefined,
};
});
// Build megaDropdown with standalone products dynamically injected
const baseMegaDropdown = (navData?.megaDropdown as Record<string, MegaDropdownGroup[]>) || MEGA_DROPDOWN_DATA;
const megaDropdown: Record<string, MegaDropdownGroup[]> = {};
for (const [key, groups] of Object.entries(baseMegaDropdown)) {
if (key === 'products' && standaloneNavItems.length > 0) {
megaDropdown[key] = groups.map((group) => {
if (group.id === 'standalone-products') {
return { ...group, description: undefined, items: standaloneNavItems };
}
return group;
});
} else {
megaDropdown[key] = groups;
}
}
const siteConfig: SiteConfig = {
name: (configData?.name as string) || COMPANY_INFO.name,
shortName: (configData?.shortName as string) || COMPANY_INFO.shortName,
@@ -104,7 +134,7 @@ export default async function RootLayout({
icp: (configData?.icp as string) || COMPANY_INFO.icp,
police: (configData?.police as string) || COMPANY_INFO.police,
mainNav: (navData?.mainNav as NavigationItem[]) || NAVIGATION_V2,
megaDropdown: (navData?.megaDropdown as Record<string, MegaDropdownGroup[]>) || MEGA_DROPDOWN_DATA,
megaDropdown,
};
return (