Initial commit

This commit is contained in:
2026-05-20 00:18:07 +08:00
commit e2bf594719
58 changed files with 5885 additions and 0 deletions

665
DEVELOPMENT.md Normal file
View File

@@ -0,0 +1,665 @@
{{app-name}} 开发文档
本文档面向 `{{app-name}}` 项目的开发者,介绍项目结构、前后端架构、构建流程、测试、代码规范等内容。
用户使用说明请参阅 [README.md](README.md)。
## 目录
- [项目结构](#项目结构)
- [前后端边界](#前后端边界)
- [一、后端开发指引](#一后端开发指引)
- [二、前端开发指引](#二前端开发指引)
- [三、项目运行、集成与打包](#三项目运行集成与打包)
- [代码质量](#代码质量)
- [测试](#测试)
- [已知限制](#已知限制)
---
## 项目结构
```text
src/
server/
bootstrap.ts 后端统一启动引导loadServerConfig → startServer
config.ts CLI 参数解析与配置文件加载(可选 YAML configPath支持 --help/-h
dev.ts 开发模式启动入口mode: "development"
main.ts 生产模式启动入口mode: "production",安全头启用)
server.ts HTTP server 启动工厂Bun.serve routes 声明式路由 + fetch fallback 静态资源服务)
static.ts 生产模式静态资源服务SPA fallback、Content-Type 映射、immutable 缓存)
helpers.ts 共享响应格式化工具(见下方函数清单)
middleware.ts API 参数校验中间件validateIdParam、validatePagination、validateTimeRange
routes/ API 路由 handler按端点拆分
health.ts GET /health
shared/
api.ts 前后端共享 TypeScript 类型
web/ React 前端(通过 Vite 构建)
index.html HTML 入口
app.tsx 根组件Layout + Header + Content/health 联调示例展示)
main.tsx 入口QueryClient 挂载 + ErrorBoundary + ReactQueryDevtools + TDesign CSS 导入)
styles.css 全局样式与自定义 CSS 变量
css.d.ts CSS 模块类型声明
components/ UI 组件
ErrorBoundary.tsx React 错误边界,捕获渲染异常并展示降级 UI
hooks/ React hooks
use-theme-preference.ts 主题偏好 hooksystem/light/darklocalStorage 记忆 + matchMedia 监听)
utils/ 前端工具函数
time.ts 时间处理formatCountdown、formatDurationUnit、formatRelativeTime、isOlderThan、subtractHours
scripts/
dev.ts 双进程开发服务Bun API server + Vite dev server
build.ts Vite → codegen → Bun compile 三步构建流水线
clean.ts 清理构建产物与临时文件
tests/ Bun test 测试(结构镜像 src 目录)
setup.ts 全局测试配置jsdom、polyfill
helpers.ts 测试辅助工具rmRetry
server/ 后端测试
bootstrap.test.ts
config.test.ts
middleware.test.ts
static.test.ts
web/ 前端测试
App.test.tsx
test-utils.tsx
openspec/ OpenSpec 变更与规格文档
config.example.yaml 配置文件示例
```
---
## 前后端边界
前端只通过 HTTP 调用后端API 路径为 `/api/*``/health`。共享类型放在 `src/shared`,前端不得 `import src/server` 的运行时实现。
---
## 一、后端开发指引
### 1.1 架构概览
```
启动流程:
dev.ts / main.ts → parseRuntimeArgs(cli args)
→ bootstrap({ configPath, mode })
→ loadServerConfig(configPath):可选 YAML 解析 → ServerConfig{ host, port }
→ startServer({ config, mode })Bun.serve routes 声明式路由 + fetch fallback
→ 注册 SIGINT/SIGTERM shutdown
HTTP 请求:
Request → Bun.serve routes 声明式匹配 → routes/*.ts(handler)
→ helpers.ts(响应格式化) → Response
前端: fetch fallback → serveStaticAsset (生产) / Vite proxy (开发)
```
### 1.2 库使用优先级
后端代码开发遵循严格的库选择顺序:
| 优先级 | 来源 | 典型用途 |
| ------ | ------------ | --------------------------------------------------------------------------------------------------- |
| 1 | Bun 内置 API | `Bun.serve``Bun.file``Bun.YAML``Bun.spawn``bun:sqlite`(如需数据存储) |
| 2 | es-toolkit | 类型判断(`isPlainObject`/`isNil`/`isEmptyObject`)、深度比较(`isEqual`)、并发控制(`Semaphore` |
| 3 | 标准 Web API | `Object.fromEntries``Headers``fetch``AbortController``Response` |
| 4 | 主流三方库 | 按需引入,优先社区活跃、类型完善的库 |
| 5 | 自行实现 | 仅在以上都无法满足时(如 `parseDuration``parseSize` 等专项逻辑) |
**原则**:新增依赖前先检查上述每一层级是否已有可用方案。禁止随意引入新依赖。
### 1.3 API 路由开发
路由文件位于 `src/server/routes/`,每个端点一个文件。路由通过 `server.ts``Bun.serve({ routes })` 声明式注册,使用 per-method handler 对象:
```typescript
// server.ts 中的路由注册
routes: {
"/api/*": () => jsonResponse(createApiError("API route not found", 404), { mode, status: 404 }),
"/health": {
GET: () => handleHealth(mode),
},
}
```
Handler 函数签名:
```typescript
// 无依赖的路由
export function handleHealth(mode: RuntimeMode): Response;
```
**请求处理流程**
1. `Bun.serve``routes` 对象按路径 + HTTP 方法匹配请求
2. 未匹配方法的请求落入 `/api/*` 通配符(返回 404
3. 各 handler 内部通过 `helpers.ts``jsonResponse``createApiError` 等格式化输出
4. 需要参数校验时使用 `middleware.ts` 提供的校验函数,返回 `Response` 实例表示校验失败(直接返回),返回数据对象表示通过
**新增路由步骤**
1.`src/server/routes/` 下创建 `<name>.ts`
2. 实现 handler 函数并 export
3.`server.ts``routes` 对象中注册路径和 method handler
4.`tests/server/` 中添加对应测试
### 1.4 共享工具
- **`helpers.ts`**:跨路由共用的响应工具函数
- `createApiError(error, status)` — 构造 API 错误体
- `createHeaders(mode, init)` — 创建响应 Headers生产模式附加安全头`X-Content-Type-Options``Referrer-Policy`
- `createHealthResponse()` — 构造健康检查响应 `{ ok: true, service, timestamp }`
- `formatDuration(ms)` — 毫秒转为可读时长字符串
- `jsonResponse(body, options)` — JSON 响应构造
- **`middleware.ts`**API 参数校验函数
- `validateIdParam(idStr, mode)` — 校验 ID 参数格式(字母数字下划线连字符,字母开头),返回 `{ id }``Response`
- `validatePagination(pageParam, pageSizeParam, mode)` — 校验分页参数(默认 page=1, pageSize=20pageSize 上限 200返回 `{ page, pageSize }``Response`
- `validateTimeRange(from, to, mode)` — 校验时间范围参数ISO 格式、from < to返回 `{ from, to }``Response`
- **`static.ts`**:生产模式静态资源服务
- `serveStaticAsset(pathname, assets)` — 静态资源分发(文件扩展名路由 → immutable 缓存,无扩展名 → SPA fallback 返回 index.html
- `hasFileExtension(path)` / `contentTypeFor(path)` / `htmlResponse(html)` — 辅助函数
### 1.5 类型定义规范
- **共享类型**以 `src/shared/api.ts` 为唯一源头,前后端共同引用
- 前端不得 `import src/server/` 下的任何文件
- **严格联合类型**优先于宽类型:如 `RuntimeMode: "development" | "production" | "test"` 而非 `RuntimeMode: string`
- API 响应类型(`ApiErrorResponse``HealthResponse`)定义在 shared 中
### 1.6 配置文件规范
配置加载流程:
```
CLI argv → parseRuntimeArgs → { configPath? }
→ loadServerConfig(configPath)
→ 可选 YAML 文件解析 → env 覆盖 → 默认值
→ ServerConfig{ host, port }
```
`ServerConfig` 包含以下字段:
| 字段 | 来源 | 默认值 |
| ------ | ------------------------------------------------- | ----------- |
| `host` | `process.env["HOST"]` → YAML `server.host` → 默认 | `127.0.0.1` |
| `port` | `process.env["PORT"]` → YAML `server.port` → 默认 | `3000` |
配置文件示例(`config.example.yaml`
```yaml
server:
host: "127.0.0.1"
port: 3000
```
### 1.7 错误模式
- **API 错误**`{ error: "描述", status: <code> }`,状态码 400/404
- **日志**:非致命异常用 `console.warn`,启动失败用 `console.error` + `process.exit(1)`
---
## 二、前端开发指引
### 2.1 技术栈概览
| 层面 | 技术 | 用途 |
| ------ | --------------------------------------------------- | ------------------------ |
| 框架 | React 19 | UI 组件开发 |
| 构建 | Vite开发+ Bun compile生产 | 开发服务 HMR 与生产构建 |
| 语言 | TypeScript | 类型安全 |
| UI 库 | TDesign React + tdesign-icons-react | UI 组件与图标 |
| 数据层 | TanStack Query (React Query) + React Query Devtools | 服务端状态管理与自动刷新 |
| 路由 | 无(单页面应用) | 不引入 React Router |
**不引入的依赖**React Router单页面场景不需要、状态管理库TanStack Query 即服务端状态层,组件内用 `useState` 足够)
### 2.2 组件树与数据流
```
main.tsx
└── StrictMode
└── ErrorBoundaryReact 错误边界)
└── QueryClientProviderTanStack Query 全局挂载)
├── App根组件Layout + Header + Content 骨架)
│ ├── useThemePreference() ── Header 主题模式 RadioGroup系统/明亮/黑暗localStorage 记忆 + theme-mode 应用)
│ ├── useQuery["health"] ── GET /health30s 轮询,前后端联调示例)
│ └── Content ── 欢迎页 + /health API 响应 JSON 展示
└── ReactQueryDevtools开发工具仅开发环境
```
**Hook 架构**
```
hooks/use-theme-preference.ts浏览器 UI 偏好)
├── ThemePreference: system / light / darkRadioGroup 受控值)
├── EffectiveTheme: light / dark写入 document.documentElement theme-mode
├── localStorage key: {{app-name}}.theme.preference同一浏览器记忆
└── matchMedia("(prefers-color-scheme: dark)")(系统模式下跟随系统明暗变化)
```
### 2.3 TanStack Query 数据层
#### Query Key 规范
```typescript
// 使用 structured array非字符串以便精确匹配和按 prefix 失效
const queryKeys = {
health: () => ["health"] as const,
};
```
- Key 使用 **structured array**(非字符串),以便精确匹配和按 prefix 失效
- 使用 `as const` 保持字面量类型
#### 查询配置规范
```typescript
// 全局级查询(需要持续刷新)
useQuery({
queryKey: queryKeys.health(),
queryFn: () => fetchJson<HealthResponse>("/health"),
refetchInterval: 30000, // 30s 轮询
refetchIntervalInBackground: false, // 切后台不轮询
staleTime: 5000, // 5s 内视为 fresh
});
```
#### fetch 封装
```typescript
async function fetchJson<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json() as Promise<T>;
}
```
- 统一使用 `fetch`(不引入 axios与后端共享 Web API 生态
- 错误抛异常,由 TanStack Query 的 `error` 状态承接
#### QueryClient 全局配置
```typescript
new QueryClient({
defaultOptions: {
queries: {
retry: 1, // 失败重试 1 次
refetchOnWindowFocus: true, // 窗口聚焦时刷新
staleTime: 5000, // 5s 内视为 fresh避免重复请求
},
},
});
```
### 2.4 组件开发规范
#### 文件命名与导入
- 每个 React 组件一个 `.tsx` 文件,文件名使用 PascalCase`ErrorBoundary.tsx`
- 组件 props 定义为 `interface XxxProps`,紧邻组件函数声明
- 类型从 `../shared/api` 导入,使用 `type` 导入(`import type { ... }`
```typescript
import type { HealthResponse } from "../shared/api";
interface AppProps {
title?: string;
}
export function App({ title }: AppProps) {
// ...
}
```
#### 组件拆分原则
- **展示组件**`components/`):纯渲染逻辑,通过 props 接收数据,通过回调返回事件
- **容器逻辑**放在 hooks 中,组件只做数据消费
- **工具函数**(时间处理等)放在 `utils/`,保持纯函数无副作用
### 2.5 样式开发规范
前端基于 TDesign React 构建 UI样式开发遵循以下优先级从高到低
1. **使用 TDesign 组件**:布局、间距、排版优先使用 TDesign 组件(如 Space、Divider、Typography
2. **使用 TDesign 组件 props**:通过组件的 props 参数控制外观(如 `theme``variant``size`
3. **使用 TDesign CSS tokens**:颜色、间距、字体等使用 `--td-*` CSS 变量(如 `--td-success-color``--td-comp-margin-xxl`
4. **在 styles.css 中定义 CSS 类**:无法通过上述方式满足的样式需求,集中定义在 `styles.css`
5. **自行开发组件**:仅在 TDesign 无法满足需求时自行开发
**红线**
- **严禁在组件中使用 `style` 属性内联调整样式**
- **严禁通过 CSS 覆盖 TDesign 组件内部类名**(如 `.t-tab-panel`),如需定制使用组件的 `className` prop
- **严禁使用 `!important`**
- 颜色统一使用 TDesign CSS tokens`--td-success-color``--td-error-color``--td-warning-color` 等),不使用硬编码色值
**styles.css 组织**
- 自定义 CSS 变量定义在 `:root`
- 布局类(`.dashboard``.dashboard-header-controls`)定义全局页面结构
- 组件修饰类为自定义视觉组件提供样式变体
- 通用工具类(`.full-width``.text-disabled``.tabular-nums`)提供公用排版能力
### 2.6 前端测试规范
- 测试目录:`tests/web/`,结构对应 `src/web/`
- 重点测试 **纯函数**(时间处理、格式化等)和**组件渲染**
- 使用 `bun:test` 框架
- 组件测试使用 `@testing-library/react` 的语义化查询getByText、getByRole而非 CSS 选择器
- 测试用户行为而非实现细节:模拟用户点击、输入等操作,而非直接调用组件方法
- 只 mock 系统边界mock fetch 返回预设响应,使用真实的 QueryClientProvider 包裹组件
---
## 三、项目运行、集成与打包
### 3.1 开发期运行
```bash
bun run dev [config.yaml]
```
`scripts/dev.ts` 同时启动两个进程:
- **Bun API server**(端口 3000后端 API 服务,`--watch` 监听后端文件变更自动重启
- **Vite dev server**(端口 5173前端 SPA + HMR 热更新
开发时访问 `http://127.0.0.1:5173`Vite 自动将 `/api``/health` 请求代理到后端。
也可以单独启动:
```bash
bun run dev:server [config.yaml] # 仅启动后端 API server--watch 模式)
bun run dev:web # 仅启动 Vite dev server
```
### 3.2 前后端集成方式
#### 双进程开发架构
开发模式下前后端分别由 Vite 和 Bun 服务:
- Vite dev server 负责前端 SPA、HMR、模块热替换
- Bun API server 负责后端 API 路由
- Vite 通过 proxy 配置将 `/api/*``/health` 转发到 Bun
#### 生产模式架构
生产模式下前端通过 Vite 构建为静态资源,通过 `import with { type: "file" }` 嵌入 Bun 可执行文件:
```typescript
// server.ts
const server = Bun.serve({
fetch(req) {
// staticAssets 存在时服务嵌入的前端资源
if (staticAssets) {
return serveStaticAsset(new URL(req.url).pathname, staticAssets);
}
return new Response("Frontend is served by Vite dev server on :5173", { status: 404 });
},
routes: {
"/api/*": () => ...,
"/health": { GET: () => handleHealth(mode) },
},
});
```
#### 路由优先级
Bun routes 的匹配规则:具体路径 > 通配符。`/health` 优先于 `/*`
未匹配 method 的请求(如 POST /health会落入 `/api/*` 通配符返回 404若无该通配符会落入 fetch fallback。
非 API 路径由 fetch fallback 处理:有文件扩展名的返回对应静态资源或 404无扩展名的返回 SPA index.html。
### 3.3 构建打包
#### 构建命令
```bash
bun run build
```
#### 构建流程
`scripts/build.ts` 执行三步流水线:
```
1. Vite build → dist/web/ (前端静态资源,含 code splitting)
2. Code generation → .build/static-assets.ts + .build/server-entry.ts
3. Bun compile → dist/dial-server (单可执行文件)
```
- Vite 构建前端资源到 `dist/web/`,自动 code splittingvendor-react、vendor-tdesign、vendor-chart
- Code generation 扫描 `dist/web/` 生成 `import with { type: "file" }` 声明,将资源嵌入 binary
- Bun compile 以 `.build/server-entry.ts` 为入口编译最终可执行文件
- `.build/` 临时目录在构建完成后自动清理
#### 产物
| 产物 | 用途 |
| ------------------ | ---------------------------------------- |
| `dist/dial-server` | 生产可执行文件(含前端资源,单文件部署) |
| `dist/web/` | Vite 构建的前端资源(构建中间产物) |
#### 构建参数
| 环境变量 | 说明 |
| --------------------------- | -------------------------------------- |
| `BUN_TARGET`/`BUILD_TARGET` | 交叉编译目标平台(如 `bun-linux-x64` |
#### 运行可执行文件
```bash
./dist/dial-server [config.yaml]
```
启动后:
- 访问 `http://127.0.0.1:3000/` → 返回前端 SPA
- 访问 `http://127.0.0.1:3000/api/*` → 返回后端 API
- 访问 `http://127.0.0.1:3000/health` → 返回健康检查 JSON
#### 清理
```bash
bun run clean
# 清理 dist/ 构建产物和 .build/ 临时文件
```
### 3.4 开发工作流
#### 日常开发循环
```bash
bun run dev [config.yaml] # 启动双进程开发环境Vite :5173 + API :3000
# 访问 http://127.0.0.1:5173
# 修改前端代码 → Vite HMR 热更新 / 修改后端代码 → --watch 自动重启
bun run check # 提交前运行完整质量检查
```
#### 完整验证流程
```bash
bun run verify
# = bun run check + bun run build
```
`verify` 适合 CI 或正式提交前会完整验证类型检查、lint、格式、单元测试和生产构建。
### 3.5 脚本说明
| 脚本 | 文件 | 说明 |
| -------------------- | ------------------- | ---------------------------------------- |
| `bun run dev` | `scripts/dev.ts` | 双进程开发服务Vite :5173 + API :3000 |
| `bun run dev:server` | `src/server/dev.ts` | 仅启动后端 API server--watch 模式) |
| `bun run dev:web` | Vite CLI | 仅启动 Vite dev server |
| `bun run build` | `scripts/build.ts` | Vite → codegen → Bun compile 三步构建 |
| `bun run clean` | `scripts/clean.ts` | 清理构建缓存与临时文件 |
### 3.6 环境变量
| 变量 | 用途 | 默认值 |
| --------------------------- | ----------------------------------------------- | ----------- |
| `BUN_TARGET`/`BUILD_TARGET` | 交叉编译目标平台(仅在 `bun run build` 时有效) | 当前平台 |
| `HOST` | 服务监听地址 | `127.0.0.1` |
| `PORT` | 服务监听端口 | `3000` |
### 3.7 项目配置文件
| 文件 | 用途 |
| ---------------------- | ------------------------------------------------------------ |
| `package.json` | 项目信息、脚本、依赖声明 |
| `tsconfig.json` | TypeScript 配置ESNext 模块、严格模式) |
| `eslint.config.js` | ESLint 规则(含前端不得 import server 的检查) |
| `commitlint.config.js` | commitlint 提交信息格式校验 |
| `.prettierrc.json` | Prettier 格式化规则(`printWidth: 120` |
| `.prettierignore` | Prettier 排除路径 |
| `.lintstagedrc.json` | lint-staged 配置TS/TSX → ESLintMD/JSON/YAML → Prettier |
| `config.example.yaml` | 配置文件示例 |
| `vite.config.ts` | Vite 构建配置React 插件、代码分割、API proxy |
| `bunfig.toml` | Bun 配置(测试 preload、排除规则 |
| `opencode.json` | OpenCode 工具配置 |
### 3.8 依赖管理
- **包管理器**:仅使用 `bun`,禁止使用 npm、pnpm、yarn
- **安装依赖**`bun install`
- **运行工具**:使用 `bunx`,禁止使用 `npx``pnpx`
- **锁文件**`bun.lock`
### 3.9 目录约定
| 目录 | 约定 |
| ------------- | ---------------------------------------------------- |
| `src/server/` | 后端代码,不能 import `src/web/`HTML import 除外) |
| `src/web/` | 前端代码,不能 import `src/server/` |
| `src/shared/` | 前后端共享类型,双向可引用 |
| `scripts/` | 独立运行脚本,可 import 项目源码 |
| `tests/` | 测试目录,结构镜像 src 目录 |
| `dist/` | 构建产物gitignore |
| `openspec/` | OpenSpec 变更管理与规格文档 |
---
## 代码质量
项目使用多层代码质量保障体系ESLint 类型感知规则 + Perfectionist 导入排序 + Prettier 格式化(通过 eslint-plugin-prettier 集成至 ESLint+ TypeScript 严格模式 + Git hooks 自动化。
```bash
bun run lint # ESLint 检查含类型感知规则、导入排序、导入验证、Prettier 格式)
bun run format # Prettier 自动格式化
bun run typecheck # TypeScript 类型检查
bun test # 运行所有测试
bun run check # 一键运行 typecheck + lint + test
bun run verify # 完整验证check + build
```
`check` 是日常开发推荐的质量检查命令。
### ESLint 规则
配置文件:`eslint.config.js`
| 配置来源 | 用途 |
| --------------------------------------------------------------- | -------------------------------------------------- |
| `@eslint/js` recommended | JavaScript 基础规则 |
| `typescript-eslint` recommended-type-checked | TypeScript 类型感知规则no-floating-promises 等) |
| `typescript-eslint` stylistic-type-checked | TypeScript 风格规则(命名规范、语法选择等) |
| `eslint-plugin-perfectionist` recommended-natural | 导入语句和命名导出自动排序 |
| `eslint-plugin-import` | 导入路径验证、循环依赖检测、重复导入合并 |
| `eslint-plugin-react-hooks` recommended | React Hooks 规则(依赖数组完整性检查等) |
| `eslint-plugin-react-refresh` | React Fast Refresh 兼容性检查 |
| `eslint-plugin-prettier` recommended + `eslint-config-prettier` | 将 Prettier 格式集成为 ESLint 规则,禁用冲突规则 |
**前端导入限制**`src/web/` 下的文件禁止 `import src/server/` 下的运行时实现,通过 `no-restricted-imports` 规则强制执行。
### Prettier 配置
配置文件:`.prettierrc.json`,通过 `eslint-plugin-prettier` 集成为 ESLint 规则(`lint` 命令同时检查格式),也可通过 `format` 命令独立运行。
显式声明所有格式化参数(`printWidth: 120``semi: true``singleQuote: false``trailingComma: "all"``endOfLine: "lf"` 等),确保不同开发环境产出完全一致的格式化结果。
### TypeScript 严格标志
| 标志 | 值 | 说明 |
| ------------------------------------ | ----- | ------------------------------------------------ |
| `strict` | true | 全局严格模式 |
| `noUnusedLocals` | true | 未使用局部变量视为错误 |
| `noUnusedParameters` | false | 保留关闭(路由 handler 统一签名需要) |
| `noPropertyAccessFromIndexSignature` | true | 禁止通过点号访问索引签名属性,强制使用括号语法 |
| `noUncheckedIndexedAccess` | true | 数组/Map 访问必须运行时真值检查 |
| `noImplicitOverride` | true | 子类覆盖父类方法时必须显式使用 `override` 关键字 |
| `verbatimModuleSyntax` | true | 强制 `import type` 纯类型导入,与 Bun 构建兼容 |
### Git Hooks
通过 husky 在 commit 阶段自动执行检查:
| Hook | 行为 |
| ------------ | -------------------------------------------------------------------------------------------------------------- |
| `pre-commit` | lint-staged 对变更文件运行 `eslint --fix`TS/TSX含 Prettier 格式修复)或 `prettier --write`MD/JSON/YAML |
| `commit-msg` | commitlint 校验提交信息格式 `类型: 简短描述` |
提交类型限定:`feat``fix``refactor``docs``style``test``chore`
`bun install` 时自动初始化 husky hooks无需手动配置。
### 质量检查完整清单
提交代码前建议运行:
```bash
bun run verify
```
CI 或正式提交前执行完整验证(类型检查 + lint + 格式 + 测试 + 构建),确保代码可编译并通过所有检查。
---
## 测试
项目采用两层测试体系:单元测试 + 组件测试。所有测试使用 `bun:test` 运行。
### 测试分层
| 层级 | 覆盖范围 | 位置 | 命令 |
| -------- | ---------------------- | ------------------------------------------------------------------- | --------------------------------------------- |
| 单元测试 | 后端函数、纯函数、常量 | `tests/server/**/*.test.ts``tests/web/{utils,hooks}/**/*.test.ts` | `bun test tests/server``bun test tests/web` |
| 组件测试 | React 组件渲染和交互 | `tests/web/components/**/*.test.tsx` | `bun test tests/web` |
### 运行命令
```bash
bun test # 运行所有单元测试和组件测试
bun test tests/server # 只运行后端单元测试
bun test tests/web # 只运行前端测试(单元 + 组件)
bun run check # 日常开发(类型检查 + lint + 测试)
bun run verify # 完整验证check + 构建)
```
### 组件测试环境
组件测试使用 jsdom 模拟浏览器环境,配置位于 `tests/setup.ts`(通过 `bunfig.toml` preload 加载):
- jsdom 提供完整的 DOM 环境
- TDesign 组件所需的 polyfillResizeObserver、IntersectionObserver、MutationObserver、matchMedia、attachEvent
- 全局 `afterEach` 清理 document.body 内容,确保测试隔离
### 编写规范
- **优先使用 `@testing-library/react`** 的语义化查询getByText、getByRole而非 CSS 选择器
- **测试用户行为而非实现细节**:模拟用户点击、输入等操作,而非直接调用组件方法
- **只 mock 系统边界**mock fetch 返回预设响应,使用真实的 QueryClientProvider 包裹组件
- **组件测试文件命名**`tests/web/ComponentName.test.tsx`
- **测试目录镜像源码目录**`tests/server/config.test.ts` 对应 `src/server/config.ts`
---
## 已知限制
- 当前仅为单页面应用,不涉及用户认证和权限控制
- 不支持集群部署,单进程运行
- 配置文件仅支持 YAML 格式,不支持热加载
- 无国际化和多语言支持