From 398c6d0869335692f82a492014bcbb1451273641 Mon Sep 17 00:00:00 2001 From: Simon <10131203+gaomeng1900@users.noreply.github.com> Date: Sun, 11 Jan 2026 01:04:43 +0800 Subject: [PATCH] chore: rm test-pages --- eslint.config.js | 7 +- packages/website/AGENTS.md | 22 +- packages/website/src/main.tsx | 8 +- packages/website/src/test-pages/README.md | 209 ------- .../website/src/test-pages/async-test.tsx | 543 ---------------- .../website/src/test-pages/complex-test.tsx | 582 ------------------ .../website/src/test-pages/error-test.tsx | 464 -------------- packages/website/src/test-pages/form-test.tsx | 488 --------------- packages/website/src/test-pages/index.tsx | 106 ---- packages/website/src/test-pages/list-test.tsx | 481 --------------- .../src/test-pages/navigation-test.tsx | 566 ----------------- packages/website/src/test-pages/router.tsx | 25 - 12 files changed, 11 insertions(+), 3490 deletions(-) delete mode 100644 packages/website/src/test-pages/README.md delete mode 100644 packages/website/src/test-pages/async-test.tsx delete mode 100644 packages/website/src/test-pages/complex-test.tsx delete mode 100644 packages/website/src/test-pages/error-test.tsx delete mode 100644 packages/website/src/test-pages/form-test.tsx delete mode 100644 packages/website/src/test-pages/index.tsx delete mode 100644 packages/website/src/test-pages/list-test.tsx delete mode 100644 packages/website/src/test-pages/navigation-test.tsx delete mode 100644 packages/website/src/test-pages/router.tsx diff --git a/eslint.config.js b/eslint.config.js index 4317c2b..03344f1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -8,12 +8,7 @@ import globals from 'globals' import tseslint from 'typescript-eslint' export default defineConfig([ - globalIgnores([ - '**/dist', - '**/test-pages', - '**/node_modules', - 'packages/website/src/components/ui', - ]), + globalIgnores(['**/dist', '**/node_modules', 'packages/website/src/components/ui']), { plugins: { 'react-hooks': reactHooks, diff --git a/packages/website/AGENTS.md b/packages/website/AGENTS.md index 19019c6..9358837 100644 --- a/packages/website/AGENTS.md +++ b/packages/website/AGENTS.md @@ -37,14 +37,17 @@ Available Magic UI components: https://magicui.design/docs/components Located in `src/components/ui/`: **From shadcn/ui:** + - `alert`, `badge`, `button`, `separator`, `sonner`, `switch`, `tooltip` **From Magic UI:** + - `animated-gradient-text`, `animated-shiny-text`, `aurora-text` - `hyper-text`, `magic-card`, `neon-gradient-card`, `particles` - `sparkles-text`, `text-animate`, `typing-animation` **Custom:** + - `highlighter`, `kbd`, `spinner` ### Styling Rules @@ -65,7 +68,6 @@ src/ │ └── DocsLayout.tsx # Documentation sidebar ├── docs/ # Documentation pages │ └── [section]/[topic]/page.tsx -├── test-pages/ # Library integration tests ├── i18n/ # Internationalization ├── router.tsx # Central routing ├── page.tsx # Homepage @@ -80,11 +82,6 @@ src/ 2. Add route to `src/router.tsx` with `
+ ` wrapper 3. Add navigation item to `DocsLayout.tsx` -### Test Page - -1. Create `src/test-pages/.tsx` -2. Add route to `src/test-pages/router.tsx` - ## Routing Uses hash-based routing for static hosting: @@ -93,18 +90,16 @@ Uses hash-based routing for static hosting: import { Router } from 'wouter' import { useHashLocation } from 'wouter/use-hash-location' - - {/* routes */} - +;{/* routes */} ``` ## Configuration Files -| File | Purpose | -|------|---------| +| File | Purpose | +| ----------------- | ----------------------- | | `components.json` | shadcn/ui configuration | -| `vite.config.js` | Vite build settings | -| `tsconfig.json` | TypeScript config | +| `vite.config.js` | Vite build settings | +| `tsconfig.json` | TypeScript config | ## Commands @@ -112,4 +107,3 @@ import { useHashLocation } from 'wouter/use-hash-location' npm start # Dev server (from root) npm run build:website # Build website (from root) ``` - diff --git a/packages/website/src/main.tsx b/packages/website/src/main.tsx index 5954d3b..0b203c2 100644 --- a/packages/website/src/main.tsx +++ b/packages/website/src/main.tsx @@ -1,19 +1,15 @@ import { createRoot } from 'react-dom/client' -import { Route, Router, Switch } from 'wouter' +import { Router } from 'wouter' import { useHashLocation } from 'wouter/use-hash-location' import './i18n/config' import './i18n/types' import { default as PagesRouter } from './router' -import { default as TestPagesRouter } from './test-pages/router' import './index.css' createRoot(document.getElementById('root')!).render( - - - - + ) diff --git a/packages/website/src/test-pages/README.md b/packages/website/src/test-pages/README.md deleted file mode 100644 index a1cdb64..0000000 --- a/packages/website/src/test-pages/README.md +++ /dev/null @@ -1,209 +0,0 @@ -# Page Use Agent 测试页面 - -这个目录包含了一系列专门设计的测试页面,用于验证 Page Use Agent 的各种能力。每个页面都模拟了真实 Web 应用中的常见交互模式和边界情况。 - -## 测试页面列表 - -### 1. 表单测试页面 (`/test-pages/form`) -**测试目标:** 表单填写、验证和提交能力 - -**包含功能:** -- 各种输入类型(文本、邮箱、密码、数字、日期、电话、URL) -- 下拉选择框和复选框 -- 实时表单验证 -- 异步提交和错误处理 -- 重置表单功能 - -**测试任务示例:** -- 填写完整的用户注册表单并提交 -- 故意输入错误信息触发验证错误 -- 测试密码确认功能 -- 尝试提交空表单查看错误提示 - -### 2. 导航测试页面 (`/test-pages/navigation`) -**测试目标:** 复杂导航和交互元素处理 - -**包含功能:** -- 顶部导航栏和下拉菜单 -- 面包屑导航 -- 标签页切换 -- 模态框弹窗 -- 通知系统 -- 用户菜单 - -**测试任务示例:** -- 点击产品下拉菜单选择不同选项 -- 切换不同的标签页查看内容 -- 打开和关闭模态框 -- 点击面包屑导航 -- 添加新通知并标记为已读 - -### 3. 列表测试页面 (`/test-pages/list`) -**测试目标:** 列表操作、搜索、过滤和分页 - -**包含功能:** -- 产品列表展示(网格和列表视图) -- 搜索功能 -- 类别过滤 -- 排序功能 -- 分页导航 -- 加载状态和骨架屏 - -**测试任务示例:** -- 搜索特定产品名称 -- 按价格排序产品列表 -- 切换网格和列表视图 -- 使用分页浏览不同页面 -- 按类别过滤产品 - -### 4. 复杂交互测试页面 (`/test-pages/complex`) -**测试目标:** 多步骤流程和状态管理 - -**包含功能:** -- 购物车管理(添加、删除、修改数量) -- 多步骤向导流程 -- 步骤验证和导航 -- 订单确认流程 -- 异步提交处理 - -**测试任务示例:** -- 完成完整的购买流程 -- 在向导中前进和后退 -- 修改购物车中的商品数量 -- 添加新商品到购物车 -- 提交订单并处理可能的错误 - -### 5. 错误处理测试页面 (`/test-pages/errors`) -**测试目标:** 错误识别和重试机制 - -**包含功能:** -- 网络连接错误模拟 -- 表单验证错误 -- 权限不足错误 -- 请求超时错误 -- 服务器内部错误 -- 文件上传错误处理 - -**测试任务示例:** -- 触发网络错误并重试 -- 提交不完整表单查看验证错误 -- 测试权限验证(用户名需为"admin") -- 上传超大文件触发错误 -- 处理各种错误场景的重试逻辑 - -### 6. 异步操作测试页面 (`/test-pages/async`) -**测试目标:** 等待和异步操作处理 - -**包含功能:** -- 文件上传进度条 -- 实时数据更新 -- 数据加载骨架屏 -- 长时间运行任务 -- 进度跟踪和日志显示 - -**测试任务示例:** -- 启动文件上传并等待完成 -- 开启实时数据更新功能 -- 加载数据并等待所有项目完成 -- 执行长时间任务并监控进度 -- 处理上传失败的重试 - -## 测试任务集合 - -### 基础操作测试 -1. **导航测试** - - 前往表单测试页面 - - 返回测试页面首页 - - 前往导航测试页面 - -2. **表单填写测试** - - 填写用户注册表单的所有必填字段 - - 提交表单并等待结果 - - 重置表单并重新填写 - -3. **搜索和过滤测试** - - 在列表页面搜索"Apple" - - 按价格降序排列产品 - - 过滤显示"手机"类别的产品 - -### 中级交互测试 -4. **购物流程测试** - - 前往复杂交互页面 - - 添加商品到购物车 - - 完成多步骤购买流程 - - 填写个人信息、地址和支付信息 - - 提交订单 - -5. **导航和菜单测试** - - 点击产品下拉菜单选择"手机" - - 切换到"订单管理"标签页 - - 打开模态框并关闭 - - 添加新的面包屑导航 - -6. **异步操作测试** - - 启动文件上传 - - 开启实时数据更新 - - 执行长时间任务并等待完成 - -### 高级错误处理测试 -7. **错误恢复测试** - - 触发网络连接错误 - - 重试失败的操作 - - 处理表单验证错误 - - 测试权限验证(用户名输入"admin") - -8. **边界情况测试** - - 提交空表单查看错误 - - 上传不支持的文件类型 - - 在向导中跳过必填步骤 - - 处理超时错误 - -### 综合场景测试 -9. **完整用户流程** - - 浏览产品列表 - - 搜索并过滤产品 - - 添加产品到购物车 - - 完成购买流程 - - 处理可能出现的错误 - -10. **压力和边界测试** - - 快速连续点击按钮 - - 在加载过程中尝试其他操作 - - 测试各种错误恢复场景 - - 验证所有异步操作的完成 - -## 使用说明 - -### 对于 Agent 开发者 -- 每个页面都包含了详细的状态指示器和反馈信息 -- 错误信息清晰明确,便于 Agent 理解和处理 -- 异步操作都有明确的完成标志 -- 所有交互元素都有适当的可访问性标记 - -### 对于测试人员 -- 可以按照测试任务逐一验证 Agent 的能力 -- 每个页面都是独立的,可以单独测试 -- 包含了各种真实场景的模拟 -- 错误场景是随机的,确保测试的真实性 - -### 技术特性 -- 使用 React + TypeScript 构建 -- 响应式设计,支持不同屏幕尺寸 -- 深色模式支持 -- 无需外部依赖,完全自包含 -- 模拟真实的网络延迟和错误 - -## 扩展建议 - -如需添加新的测试场景,建议考虑以下方面: -- 特定行业的业务流程 -- 更复杂的数据可视化交互 -- 多媒体内容处理 -- 实时协作功能 -- 移动端特有的交互模式 - -每个新页面都应该: -- 有明确的测试目标 -- 包含多种难度级别的任务 -- 提供清晰的状态反馈 -- 模拟真实的用户场景 diff --git a/packages/website/src/test-pages/async-test.tsx b/packages/website/src/test-pages/async-test.tsx deleted file mode 100644 index f329d7e..0000000 --- a/packages/website/src/test-pages/async-test.tsx +++ /dev/null @@ -1,543 +0,0 @@ -import { useEffect, useState } from 'react' -import { Link } from 'wouter' - -interface UploadProgress { - id: string - name: string - progress: number - status: 'uploading' | 'completed' | 'error' - speed: string - timeRemaining: string -} - -interface DataItem { - id: number - title: string - content: string - timestamp: string - status: 'loading' | 'loaded' | 'error' -} - -export default function AsyncTestPage() { - const [uploads, setUploads] = useState([]) - const [dataItems, setDataItems] = useState([]) - const [isLoadingData, setIsLoadingData] = useState(false) - const [realTimeData, setRealTimeData] = useState([]) - const [isRealTimeActive, setIsRealTimeActive] = useState(false) - const [longRunningTask, setLongRunningTask] = useState<{ - isRunning: boolean - progress: number - currentStep: string - logs: string[] - }>({ - isRunning: false, - progress: 0, - currentStep: '', - logs: [], - }) - - // 模拟实时数据更新 - useEffect(() => { - let interval: NodeJS.Timeout - if (isRealTimeActive) { - interval = setInterval(() => { - const newData = `数据更新 ${new Date().toLocaleTimeString()}: ${Math.floor(Math.random() * 1000)}` - setRealTimeData((prev) => [newData, ...prev.slice(0, 9)]) // 保持最新10条 - }, 2000) - } - return () => { - if (interval) clearInterval(interval) - } - }, [isRealTimeActive]) - - // 模拟文件上传 - const simulateFileUpload = (fileName: string) => { - const uploadId = Date.now().toString() - const newUpload: UploadProgress = { - id: uploadId, - name: fileName, - progress: 0, - status: 'uploading', - speed: '0 KB/s', - timeRemaining: '计算中...', - } - - setUploads((prev) => [...prev, newUpload]) - - // 模拟上传进度 - const interval = setInterval(() => { - setUploads((prev) => - prev.map((upload) => { - if (upload.id === uploadId) { - const newProgress = Math.min(upload.progress + Math.random() * 15, 100) - const speed = `${(Math.random() * 500 + 100).toFixed(0)} KB/s` - const timeRemaining = - newProgress >= 100 ? '完成' : `${Math.ceil((100 - newProgress) / 10)}秒` - - // 模拟随机失败 - if (newProgress > 50 && Math.random() < 0.1) { - clearInterval(interval) - return { - ...upload, - status: 'error' as const, - speed: '0 KB/s', - timeRemaining: '失败', - } - } - - if (newProgress >= 100) { - clearInterval(interval) - return { - ...upload, - progress: 100, - status: 'completed' as const, - speed, - timeRemaining, - } - } - - return { - ...upload, - progress: newProgress, - speed, - timeRemaining, - } - } - return upload - }) - ) - }, 500) - } - - // 模拟数据加载 - const loadData = async () => { - setIsLoadingData(true) - setDataItems([]) - - // 创建骨架屏数据 - const skeletonItems: DataItem[] = Array.from({ length: 6 }, (_, i) => ({ - id: i, - title: '', - content: '', - timestamp: '', - status: 'loading', - })) - setDataItems(skeletonItems) - - // 逐个加载数据项 - for (let i = 0; i < 6; i++) { - await new Promise((resolve) => setTimeout(resolve, 800 + Math.random() * 1000)) - - setDataItems((prev) => - prev.map((item) => { - if (item.id === i) { - // 模拟随机加载失败 - if (Math.random() < 0.15) { - return { - ...item, - status: 'error', - title: '加载失败', - content: '数据加载失败,请重试', - } - } - - return { - ...item, - status: 'loaded', - title: `数据项 ${i + 1}`, - content: `这是第 ${i + 1} 个数据项的内容,包含了一些示例文本用于展示加载效果。`, - timestamp: new Date().toLocaleString(), - } - } - return item - }) - ) - } - - setIsLoadingData(false) - } - - // 模拟长时间运行的任务 - const startLongRunningTask = async () => { - setLongRunningTask({ - isRunning: true, - progress: 0, - currentStep: '初始化任务...', - logs: ['任务开始'], - }) - - const steps = [ - { name: '初始化任务...', duration: 2000 }, - { name: '连接服务器...', duration: 1500 }, - { name: '验证权限...', duration: 1000 }, - { name: '下载数据...', duration: 3000 }, - { name: '处理数据...', duration: 2500 }, - { name: '生成报告...', duration: 2000 }, - { name: '保存结果...', duration: 1000 }, - { name: '清理资源...', duration: 500 }, - ] - - for (let i = 0; i < steps.length; i++) { - const step = steps[i] - - setLongRunningTask((prev) => ({ - ...prev, - currentStep: step.name, - logs: [...prev.logs, `开始: ${step.name}`], - })) - - // 模拟步骤执行时间 - const startTime = Date.now() - while (Date.now() - startTime < step.duration) { - await new Promise((resolve) => setTimeout(resolve, 100)) - const elapsed = Date.now() - startTime - const stepProgress = Math.min((elapsed / step.duration) * 100, 100) - const totalProgress = ((i + stepProgress / 100) / steps.length) * 100 - - setLongRunningTask((prev) => ({ - ...prev, - progress: totalProgress, - })) - } - - setLongRunningTask((prev) => ({ - ...prev, - logs: [...prev.logs, `完成: ${step.name}`], - })) - - // 模拟随机失败 - if (i === 3 && Math.random() < 0.2) { - setLongRunningTask((prev) => ({ - ...prev, - isRunning: false, - currentStep: '任务失败', - logs: [...prev.logs, '错误: 数据下载失败,请重试'], - })) - return - } - } - - setLongRunningTask((prev) => ({ - ...prev, - isRunning: false, - progress: 100, - currentStep: '任务完成', - logs: [...prev.logs, '任务成功完成!'], - })) - } - - const clearUploads = () => { - setUploads([]) - } - - const retryFailedUpload = (uploadId: string) => { - const failedUpload = uploads.find((u) => u.id === uploadId) - if (failedUpload) { - setUploads((prev) => prev.filter((u) => u.id !== uploadId)) - simulateFileUpload(failedUpload.name) - } - } - - const retryDataLoad = (itemId: number) => { - setDataItems((prev) => - prev.map((item) => { - if (item.id === itemId) { - return { ...item, status: 'loading', title: '', content: '', timestamp: '' } - } - return item - }) - ) - - setTimeout(() => { - setDataItems((prev) => - prev.map((item) => { - if (item.id === itemId) { - return { - ...item, - status: 'loaded', - title: `数据项 ${itemId + 1}`, - content: `这是重新加载的第 ${itemId + 1} 个数据项的内容。`, - timestamp: new Date().toLocaleString(), - } - } - return item - }) - ) - }, 1000) - } - - return ( -
-
-
-

异步操作测试

-

- 测试等待、加载状态识别和异步操作处理能力 -

-
- -
- {/* 文件上传进度 */} -
-
-
-

- 文件上传进度 -

-
- - -
-
- -
- {uploads.length === 0 ? ( -
- 点击"开始上传"来模拟文件上传 -
- ) : ( - uploads.map((upload) => ( -
-
- - {upload.name} - - - {upload.status === 'completed' - ? '✓ 完成' - : upload.status === 'error' - ? '✗ 失败' - : '上传中...'} - -
- -
-
-
- -
- {upload.progress.toFixed(1)}% - {upload.speed} - {upload.timeRemaining} -
- - {upload.status === 'error' && ( - - )} -
- )) - )} -
-
- - {/* 实时数据更新 */} -
-
-

- 实时数据更新 -

- -
- -
- {realTimeData.length === 0 ? ( -
- 点击"开始更新"来查看实时数据 -
- ) : ( - realTimeData.map((data) => ( -
- {data} -
- )) - )} -
-
-
- - {/* 数据加载和长时间任务 */} -
- {/* 数据加载骨架屏 */} -
-
-

- 数据加载测试 -

- -
- -
- {dataItems.map((item) => ( -
- {item.status === 'loading' ? ( -
-
-
-
-
- ) : item.status === 'error' ? ( -
-

- {item.title} -

-

- {item.content} -

- -
- ) : ( -
-

- {item.title} -

-

- {item.content} -

- - {item.timestamp} - -
- )} -
- ))} -
-
- - {/* 长时间运行任务 */} -
-
-

长时间任务

- -
- - {longRunningTask.progress > 0 && ( -
-
- - {longRunningTask.currentStep} - - - {longRunningTask.progress.toFixed(1)}% - -
-
-
-
-
- )} - - {longRunningTask.logs.length > 0 && ( -
-

- 执行日志: -

-
- {longRunningTask.logs.map((log, logIdx) => { - const logKey = `${logIdx + 1}-${log.substring(0, 30)}` - return ( -
- {log} -
- ) - })} -
-
- )} -
-
-
- - {/* 返回链接 */} -
- - ← 返回测试页面列表 - -
-
-
- ) -} diff --git a/packages/website/src/test-pages/complex-test.tsx b/packages/website/src/test-pages/complex-test.tsx deleted file mode 100644 index 0a994ed..0000000 --- a/packages/website/src/test-pages/complex-test.tsx +++ /dev/null @@ -1,582 +0,0 @@ -import { useState } from 'react' -import { Link } from 'wouter' - -interface CartItem { - id: number - name: string - price: number - quantity: number - image: string -} - -interface WizardStep { - id: number - title: string - description: string - completed: boolean -} - -export default function ComplexTestPage() { - const [currentStep, setCurrentStep] = useState(1) - const [cartItems, setCartItems] = useState([ - { - id: 1, - name: 'iPhone 15 Pro', - price: 7999, - quantity: 1, - image: 'https://picsum.photos/100/100?random=1', - }, - { - id: 2, - name: 'MacBook Air', - price: 8999, - quantity: 1, - image: 'https://picsum.photos/100/100?random=2', - }, - ]) - const [wizardData, setWizardData] = useState({ - personalInfo: { name: '', email: '', phone: '' }, - address: { street: '', city: '', zipCode: '' }, - payment: { cardNumber: '', expiryDate: '', cvv: '' }, - }) - const [wizardSteps, setWizardSteps] = useState([ - { id: 1, title: '个人信息', description: '填写基本信息', completed: false }, - { id: 2, title: '收货地址', description: '填写收货地址', completed: false }, - { id: 3, title: '支付方式', description: '选择支付方式', completed: false }, - { id: 4, title: '确认订单', description: '确认订单信息', completed: false }, - ]) - const [showConfirmDialog, setShowConfirmDialog] = useState(false) - const [isProcessing, setIsProcessing] = useState(false) - const [orderComplete, setOrderComplete] = useState(false) - - // 购物车操作 - const updateQuantity = (id: number, newQuantity: number) => { - if (newQuantity <= 0) { - removeItem(id) - return - } - setCartItems((prev) => - prev.map((item) => (item.id === id ? { ...item, quantity: newQuantity } : item)) - ) - } - - const removeItem = (id: number) => { - setCartItems((prev) => prev.filter((item) => item.id !== id)) - } - - const addItem = () => { - const newItem: CartItem = { - id: Date.now(), - name: `新产品 ${cartItems.length + 1}`, - price: Math.floor(Math.random() * 5000) + 1000, - quantity: 1, - image: `https://picsum.photos/100/100?random=${Date.now()}`, - } - setCartItems((prev) => [...prev, newItem]) - } - - const getTotalPrice = () => { - return cartItems.reduce((total, item) => total + item.price * item.quantity, 0) - } - - // 向导步骤验证 - const validateStep = (step: number): boolean => { - switch (step) { - case 1: - return !!( - wizardData.personalInfo.name && - wizardData.personalInfo.email && - wizardData.personalInfo.phone - ) - case 2: - return !!( - wizardData.address.street && - wizardData.address.city && - wizardData.address.zipCode - ) - case 3: - return !!( - wizardData.payment.cardNumber && - wizardData.payment.expiryDate && - wizardData.payment.cvv - ) - default: - return true - } - } - - const goToStep = (step: number) => { - // 验证当前步骤 - if (step > currentStep && !validateStep(currentStep)) { - alert('请完成当前步骤的必填信息') - return - } - - // 更新步骤完成状态 - if (step > currentStep) { - setWizardSteps((prev) => - prev.map((s) => (s.id === currentStep ? { ...s, completed: true } : s)) - ) - } - - setCurrentStep(step) - } - - const handleInputChange = (section: string, field: string, value: string) => { - setWizardData((prev) => ({ - ...prev, - [section]: { - ...prev[section as keyof typeof prev], - [field]: value, - }, - })) - } - - const handleSubmitOrder = async () => { - setIsProcessing(true) - - // 模拟处理时间 - await new Promise((resolve) => setTimeout(resolve, 3000)) - - // 模拟随机失败 - if (Math.random() < 0.2) { - setIsProcessing(false) - alert('订单提交失败,请重试') - return - } - - setIsProcessing(false) - setOrderComplete(true) - setShowConfirmDialog(false) - } - - const resetWizard = () => { - setCurrentStep(1) - setWizardData({ - personalInfo: { name: '', email: '', phone: '' }, - address: { street: '', city: '', zipCode: '' }, - payment: { cardNumber: '', expiryDate: '', cvv: '' }, - }) - setWizardSteps((prev) => prev.map((s) => ({ ...s, completed: false }))) - setOrderComplete(false) - setShowConfirmDialog(false) - } - - if (orderComplete) { - return ( -
-
-
-
🎉
-

- 订单提交成功! -

-

- 您的订单已成功提交,我们将尽快为您处理。 -

-
- - - 返回测试页面 - -
-
-
-
- ) - } - - return ( -
-
-
-

复杂交互测试

-

测试多步骤操作、状态管理和复杂用户交互

-
- -
- {/* 购物车区域 */} -
-
-

- 购物车 ({cartItems.length}) -

- -
- {cartItems.map((item) => ( -
- {item.name} -
-

- {item.name} -

-

- ¥{item.price.toLocaleString()} -

-
-
- - {item.quantity} - - -
-
- ))} -
- - - -
-
- 总计: - ¥{getTotalPrice().toLocaleString()} -
-
-
-
- - {/* 向导区域 */} -
-
- {/* 步骤指示器 */} -
-
- {wizardSteps.map((step, index) => ( -
- - {index < wizardSteps.length - 1 && ( -
- )} -
- ))} -
-
-

- {wizardSteps[currentStep - 1].title} -

-

- {wizardSteps[currentStep - 1].description} -

-
-
- - {/* 步骤内容 */} -
- {currentStep === 1 && ( -
-
- - handleInputChange('personalInfo', 'name', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入您的姓名" - /> -
-
- - handleInputChange('personalInfo', 'email', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入您的邮箱" - /> -
-
- - handleInputChange('personalInfo', 'phone', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入您的手机号" - /> -
-
- )} - - {currentStep === 2 && ( -
-
- - handleInputChange('address', 'street', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入详细地址" - /> -
-
- - handleInputChange('address', 'city', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入城市" - /> -
-
- - handleInputChange('address', 'zipCode', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入邮政编码" - /> -
-
- )} - - {currentStep === 3 && ( -
-
- - handleInputChange('payment', 'cardNumber', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入银行卡号" - /> -
-
-
- - - handleInputChange('payment', 'expiryDate', e.target.value) - } - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="MM/YY" - /> -
-
- - handleInputChange('payment', 'cvv', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="CVV" - /> -
-
-
- )} - - {currentStep === 4 && ( -
-
-

- 订单确认 -

- -
-
-
- 个人信息 -
-

- {wizardData.personalInfo.name} | {wizardData.personalInfo.email} |{' '} - {wizardData.personalInfo.phone} -

-
- -
-
- 收货地址 -
-

- {wizardData.address.street}, {wizardData.address.city}{' '} - {wizardData.address.zipCode} -

-
- -
-
- 支付方式 -
-

- **** **** **** {wizardData.payment.cardNumber.slice(-4)} -

-
-
-
-
- )} -
- - {/* 导航按钮 */} -
- - - {currentStep < 4 ? ( - - ) : ( - - )} -
-
-
-
- - {/* 确认对话框 */} - {showConfirmDialog && ( -
-
-

- 确认提交订单 -

-

- 您确定要提交这个订单吗?订单总金额为 ¥{getTotalPrice().toLocaleString()} -

-
- - -
-
-
- )} - - {/* 返回链接 */} -
- - ← 返回测试页面列表 - -
-
-
- ) -} diff --git a/packages/website/src/test-pages/error-test.tsx b/packages/website/src/test-pages/error-test.tsx deleted file mode 100644 index d11f3c9..0000000 --- a/packages/website/src/test-pages/error-test.tsx +++ /dev/null @@ -1,464 +0,0 @@ -import { useState } from 'react' -import { Link } from 'wouter' - -interface ErrorScenario { - id: string - title: string - description: string - type: 'network' | 'validation' | 'permission' | 'timeout' | 'server' -} - -export default function ErrorTestPage() { - const [isLoading, setIsLoading] = useState(false) - const [error, setError] = useState(null) - const [success, setSuccess] = useState(null) - const [retryCount, setRetryCount] = useState(0) - const [formData, setFormData] = useState({ - username: '', - password: '', - email: '', - file: null as File | null, - }) - - const errorScenarios: ErrorScenario[] = [ - { - id: 'network-error', - title: '网络连接错误', - description: '模拟网络连接失败,测试重试机制', - type: 'network', - }, - { - id: 'validation-error', - title: '表单验证错误', - description: '模拟表单验证失败,测试错误提示', - type: 'validation', - }, - { - id: 'permission-error', - title: '权限不足错误', - description: '模拟权限验证失败,测试权限处理', - type: 'permission', - }, - { - id: 'timeout-error', - title: '请求超时错误', - description: '模拟请求超时,测试超时处理', - type: 'timeout', - }, - { - id: 'server-error', - title: '服务器内部错误', - description: '模拟服务器500错误,测试错误恢复', - type: 'server', - }, - ] - - const simulateError = async (scenario: ErrorScenario): Promise => { - setIsLoading(true) - setError(null) - setSuccess(null) - - // 模拟网络延迟 - await new Promise((resolve) => setTimeout(resolve, 1000 + Math.random() * 2000)) - - switch (scenario.type) { - case 'network': - // 70% 概率失败 - if (Math.random() < 0.7) { - throw new Error('网络连接失败:无法连接到服务器,请检查您的网络连接') - } - break - - case 'validation': - // 检查表单数据 - if (!formData.username || formData.username.length < 3) { - throw new Error('用户名验证失败:用户名至少需要3个字符') - } - if (!formData.password || formData.password.length < 6) { - throw new Error('密码验证失败:密码至少需要6个字符') - } - if (!formData.email?.includes('@')) { - throw new Error('邮箱验证失败:请输入有效的邮箱地址') - } - break - - case 'permission': - // 模拟权限检查 - if (formData.username !== 'admin') { - throw new Error('权限不足:您没有执行此操作的权限,请联系管理员') - } - break - - case 'timeout': - // 模拟超时 - await new Promise((resolve) => setTimeout(resolve, 8000)) - throw new Error('请求超时:服务器响应时间过长,请稍后重试') - - case 'server': - // 50% 概率服务器错误 - if (Math.random() < 0.5) { - throw new Error('服务器内部错误:服务器遇到了一个错误,请稍后重试') - } - break - - default: - throw new Error('未知错误:发生了未预期的错误') - } - - // 成功情况 - return Promise.resolve() - } - - const handleScenarioTest = async (scenario: ErrorScenario) => { - try { - await simulateError(scenario) - setSuccess(`${scenario.title} 测试成功完成!`) - setRetryCount(0) - } catch (err) { - const errorMessage = err instanceof Error ? err.message : '未知错误' - setError(errorMessage) - setRetryCount((prev) => prev + 1) - } finally { - setIsLoading(false) - } - } - - const handleRetry = async (scenario: ErrorScenario) => { - if (retryCount >= 3) { - setError('重试次数已达上限,请稍后再试或联系技术支持') - return - } - await handleScenarioTest(scenario) - } - - const handleFileUpload = async () => { - if (!formData.file) { - setError('请选择要上传的文件') - return - } - - setIsLoading(true) - setError(null) - setSuccess(null) - - try { - // 模拟文件大小检查 - if (formData.file.size > 5 * 1024 * 1024) { - throw new Error('文件上传失败:文件大小不能超过5MB') - } - - // 模拟文件类型检查 - const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'] - if (!allowedTypes.includes(formData.file.type)) { - throw new Error('文件上传失败:不支持的文件类型,请上传图片或PDF文件') - } - - // 模拟上传过程 - await new Promise((resolve) => setTimeout(resolve, 2000)) - - // 模拟随机失败 - if (Math.random() < 0.3) { - throw new Error('文件上传失败:上传过程中发生错误,请重试') - } - - setSuccess('文件上传成功!') - } catch (err) { - const errorMessage = err instanceof Error ? err.message : '文件上传失败' - setError(errorMessage) - } finally { - setIsLoading(false) - } - } - - const clearMessages = () => { - setError(null) - setSuccess(null) - setRetryCount(0) - } - - const getErrorIcon = (type: string) => { - switch (type) { - case 'network': - return '🌐' - case 'validation': - return '⚠️' - case 'permission': - return '🔒' - case 'timeout': - return '⏰' - case 'server': - return '🔧' - default: - return '❌' - } - } - - return ( -
-
-
-

错误处理测试

-

- 测试各种错误场景和重试机制,验证 Agent 的错误处理能力 -

-
- - {/* 全局消息显示 */} - {(error || success) && ( -
- {error && ( -
-
-
- - - -
-
-

操作失败

-

{error}

- {retryCount > 0 && ( -

- 已重试 {retryCount} 次 {retryCount >= 3 && '(已达最大重试次数)'} -

- )} -
- -
-
- )} - - {success && ( -
-
-
- - - -
-
-

- 操作成功 -

-

{success}

-
- -
-
- )} -
- )} - -
- {/* 错误场景测试 */} -
-

错误场景测试

- - {errorScenarios.map((scenario) => ( -
-
-
{getErrorIcon(scenario.type)}
-
-

- {scenario.title} -

-

- {scenario.description} -

-
- - {error && retryCount > 0 && retryCount < 3 && ( - - )} -
-
-
-
- ))} -
- - {/* 表单验证测试 */} -
-

表单验证测试

- -
-

- 用户信息表单 -

-
-
- - setFormData((prev) => ({ ...prev, username: e.target.value }))} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入用户名" - /> -
-
- - setFormData((prev) => ({ ...prev, password: e.target.value }))} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入密码" - /> -
-
- - setFormData((prev) => ({ ...prev, email: e.target.value }))} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="请输入邮箱地址" - /> -
- -
-
- - {/* 文件上传测试 */} -
-

- 文件上传测试 -

-
-
- - - setFormData((prev) => ({ ...prev, file: e.target.files?.[0] || null })) - } - accept="image/*,.pdf" - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - /> -
- {formData.file && ( -
- 已选择: {formData.file.name} ({(formData.file.size / 1024 / 1024).toFixed(2)}{' '} - MB) -
- )} - -
-
- - {/* 权限测试说明 */} -
-

- 💡 权限测试提示 -

-

- 要通过权限测试,请在用户名字段输入 "admin",然后点击"触发错误"按钮测试权限验证。 -

-
-
-
- - {/* 加载状态指示器 */} - {isLoading && ( -
-
- - - - - 处理中,请稍候... -
-
- )} - - {/* 返回链接 */} -
- - ← 返回测试页面列表 - -
-
-
- ) -} diff --git a/packages/website/src/test-pages/form-test.tsx b/packages/website/src/test-pages/form-test.tsx deleted file mode 100644 index 1f650a4..0000000 --- a/packages/website/src/test-pages/form-test.tsx +++ /dev/null @@ -1,488 +0,0 @@ -import { useState } from 'react' -import { Link } from 'wouter' - -interface FormData { - username: string - email: string - password: string - confirmPassword: string - age: string - birthDate: string - phone: string - website: string - bio: string - country: string - newsletter: boolean - terms: boolean -} - -type FormErrors = Record - -export default function FormTestPage() { - const [formData, setFormData] = useState({ - username: '', - email: '', - password: '', - confirmPassword: '', - age: '', - birthDate: '', - phone: '', - website: '', - bio: '', - country: '', - newsletter: false, - terms: false, - }) - - const [errors, setErrors] = useState({}) - const [isSubmitting, setIsSubmitting] = useState(false) - const [submitResult, setSubmitResult] = useState<'success' | 'error' | null>(null) - const [submitMessage, setSubmitMessage] = useState('') - - const validateField = (name: string, value: string | boolean): string => { - switch (name) { - case 'username': - if (!value) return '用户名不能为空' - if (typeof value === 'string' && value.length < 3) return '用户名至少需要3个字符' - if (typeof value === 'string' && !/^[a-zA-Z0-9_]+$/.test(value)) - return '用户名只能包含字母、数字和下划线' - return '' - case 'email': - if (!value) return '邮箱不能为空' - if (typeof value === 'string' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) - return '请输入有效的邮箱地址' - return '' - case 'password': - if (!value) return '密码不能为空' - if (typeof value === 'string' && value.length < 6) return '密码至少需要6个字符' - if (typeof value === 'string' && !/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(value)) - return '密码必须包含大小写字母和数字' - return '' - case 'confirmPassword': - if (!value) return '请确认密码' - if (value !== formData.password) return '两次输入的密码不一致' - return '' - case 'age': { - if (!value) return '年龄不能为空' - const age = parseInt(value as string) - if (isNaN(age) || age < 18 || age > 120) return '年龄必须在18-120之间' - return '' - } - case 'phone': - if (!value) return '手机号不能为空' - if (typeof value === 'string' && !/^1[3-9]\d{9}$/.test(value)) return '请输入有效的手机号' - return '' - case 'terms': - if (!value) return '请同意服务条款' - return '' - default: - return '' - } - } - - const handleInputChange = (name: string, value: string | boolean) => { - console.log(`Input changed: ${name} = ${value}`) - - setFormData((prev) => ({ ...prev, [name]: value })) - - // 实时验证 - const error = validateField(name, value) - setErrors((prev) => ({ ...prev, [name]: error })) - } - - const validateForm = (): boolean => { - const newErrors: FormErrors = {} - let isValid = true - - Object.keys(formData).forEach((key) => { - const error = validateField(key, formData[key as keyof FormData]) - if (error) { - newErrors[key] = error - isValid = false - } - }) - - setErrors(newErrors) - return isValid - } - - const simulateSubmit = async (): Promise<{ success: boolean; message: string }> => { - // 模拟网络延迟 - await new Promise((resolve) => setTimeout(resolve, 2000 + Math.random() * 2000)) - - // 模拟随机失败 - if (Math.random() < 0.3) { - throw new Error('网络错误:服务器暂时不可用,请稍后重试') - } - - // 模拟服务器验证错误 - if (formData.username.toLowerCase() === 'admin') { - throw new Error('用户名 "admin" 已被占用,请选择其他用户名') - } - - return { - success: true, - message: '注册成功!欢迎加入我们的平台。', - } - } - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - - if (!validateForm()) { - setSubmitResult('error') - setSubmitMessage('请修正表单中的错误') - return - } - - setIsSubmitting(true) - setSubmitResult(null) - setSubmitMessage('') - - try { - const result = await simulateSubmit() - setSubmitResult('success') - setSubmitMessage(result.message) - } catch (error) { - setSubmitResult('error') - setSubmitMessage(error instanceof Error ? error.message : '提交失败,请重试') - } finally { - setIsSubmitting(false) - } - } - - const resetForm = () => { - setFormData({ - username: '', - email: '', - password: '', - confirmPassword: '', - age: '', - birthDate: '', - phone: '', - website: '', - bio: '', - country: '', - newsletter: false, - terms: false, - }) - setErrors({}) - setSubmitResult(null) - setSubmitMessage('') - } - - return ( -
-
-
-
-

用户注册表单

-

测试各种表单输入、验证和提交功能

-
- -
- {/* 用户名 */} -
- - handleInputChange('username', e.target.value)} - className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white ${ - errors.username ? 'border-red-500' : 'border-gray-300 dark:border-gray-600' - }`} - placeholder="请输入用户名" - /> - {errors.username && ( -

{errors.username}

- )} -
- - {/* 邮箱 */} -
- - handleInputChange('email', e.target.value)} - className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white ${ - errors.email ? 'border-red-500' : 'border-gray-300 dark:border-gray-600' - }`} - placeholder="请输入邮箱地址" - /> - {errors.email && ( -

{errors.email}

- )} -
- - {/* 密码 */} -
-
- - handleInputChange('password', e.target.value)} - className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white ${ - errors.password ? 'border-red-500' : 'border-gray-300 dark:border-gray-600' - }`} - placeholder="请输入密码" - /> - {errors.password && ( -

{errors.password}

- )} -
-
- - handleInputChange('confirmPassword', e.target.value)} - className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white ${ - errors.confirmPassword - ? 'border-red-500' - : 'border-gray-300 dark:border-gray-600' - }`} - placeholder="请再次输入密码" - /> - {errors.confirmPassword && ( -

- {errors.confirmPassword} -

- )} -
-
- - {/* 年龄和生日 */} -
-
- - handleInputChange('age', e.target.value)} - className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white ${ - errors.age ? 'border-red-500' : 'border-gray-300 dark:border-gray-600' - }`} - placeholder="请输入年龄" - min="18" - max="120" - /> - {errors.age && ( -

{errors.age}

- )} -
-
- - handleInputChange('birthDate', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - /> -
-
- - {/* 手机和网站 */} -
-
- - handleInputChange('phone', e.target.value)} - className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white ${ - errors.phone ? 'border-red-500' : 'border-gray-300 dark:border-gray-600' - }`} - placeholder="请输入手机号" - /> - {errors.phone && ( -

{errors.phone}

- )} -
-
- - handleInputChange('website', e.target.value)} - className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" - placeholder="https://example.com" - /> -
-
- - {/* 国家选择 */} -
- - -
- - {/* 个人简介 */} -
- -