🏁 序章:一场现代前端的冒险
在数字世界的浩瀚宇宙中,OpenHands 前端项目如同一艘装备精良的太空飞船,承载着开发者们对高效、优雅与可靠的追求。它不仅仅是一个 React 应用,更是现代前端工程的缩影——Remix SPA、TypeScript、Redux、TanStack Query、Tailwind CSS、i18next、React Testing Library、Vitest、Mock Service Worker(MSW)……每一项技术都是这艘飞船上的关键部件。
但,如何让这艘飞船在开发、测试与部署的星际航道上畅行无阻?让我们一起踏上这场前端奇遇之旅,揭开 OpenHands 前端的神秘面纱。
🧰 装备清单:技术栈的魔法道具
OpenHands 前端的技术栈堪称「全副武装」:
- Remix SPA Mode:融合 React、Vite 与 React Router,既快又灵活。
- TypeScript:为代码注入类型魔法,减少「咒语」失误。
- Redux & TanStack Query:状态管理与数据请求的双剑合璧。
- Tailwind CSS:让样式如风般自由。
- i18next:多语言切换,全球通行证。
- React Testing Library & Vitest:测试护盾,确保每一行代码都能抵御 bug 的侵袭。
- Mock Service Worker (MSW):API 模拟大师,测试与开发的好伙伴。
🚀 起航:快速启动你的 OpenHands 前端
🛠️ 准备工作
想要启动这艘飞船,你需要准备好 Node.js 20.x 及以上版本,以及 npm、bun 等现代包管理工具。
git clone https://github.com/All-Hands-AI/OpenHands.git
cd OpenHands/frontend
npm install
🏃 开发模式:与 MSW 并肩作战
开发时,MSW(Mock Service Worker)会化身为你的「后端幻影」,让你无需真实后端也能畅快开发:
npm run dev
打开 http://localhost:3001,你就能看到 OpenHands 前端的英姿。
注意:MSW 只「部分」模拟后端,部分功能需等到真后端上线才能体验。
🏭 生产模式:直连真实后端
当你准备好与真实后端「正面交锋」时:
make build
make run
或者分开启动:
make start-backend
make start-frontend
# 或
cd frontend && npm start -- --port 3001
如果你还想在开发时体验 MSW 的 SaaS 模式:
npm run dev:mock
# 或
npm run dev:mock:saas
🗝️ 魔法咒语:环境变量的奥秘
每个前端项目都离不开一串串神秘的环境变量。OpenHands 也不例外:
变量名 | 作用说明 | 默认值 |
---|---|---|
VITE_BACKEND_BASE_URL | 后端主机名(WebSocket 用) | localhost:3000 |
VITE_BACKEND_HOST | 后端主机+端口(API 用) | 127.0.0.1:3000 |
VITE_MOCK_API | 是否启用 MSW API Mock | false |
VITE_MOCK_SAAS | 开发时模拟 SaaS 模式 | false |
VITE_USE_TLS | 是否启用 HTTPS/WSS | false |
VITE_FRONTEND_PORT | 前端端口 | 3001 |
VITE_INSECURE_SKIP_VERIFY | 跳过 TLS 证书校验 | false |
VITE_GITHUB_TOKEN | GitHub Token(部分测试用) | – |
只需参考 .env.sample
,创建属于你的 .env
文件即可。
🏗️ 结构探秘:项目目录的藏宝图
frontend
├── __tests__ # 测试用例
├── public
├── src
│ ├── api # API 调用
│ ├── assets
│ ├── components # 组件
│ ├── context # 本地状态管理
│ ├── hooks # 自定义 Hook
│ ├── i18n # 国际化
│ ├── mocks # MSW Mock
│ ├── routes # 路由
│ ├── services
│ ├── state # Redux
│ ├── types
│ ├── utils # 工具函数
│ └── root.tsx # 入口
└── .env.sample # 环境变量样例
🧩 组件分区
组件根据「领域」、「功能」或「共享性」分门别类:
components
├── features # 领域组件
├── layout
├── modals
└── ui # 通用 UI 组件
🌐 功能亮点:OpenHands 的超能力
- 实时 WebSocket 更新:数据如同心跳般实时跳动。
- 国际化:多语言切换,全球畅行。
- Remix 路由数据加载:页面数据「先知」。
- GitHub OAuth 登录:SaaS 模式下的身份认证。
🧪 测试的艺术:让代码无懈可击
🏹 测试武器库
- Vitest:极速测试引擎。
- React Testing Library:专注用户视角的测试。
- @testing-library/user-event:模拟真实用户操作。
- Mock Service Worker (MSW):API Mock 的魔法师。
- V8 覆盖率:测试覆盖的显微镜。
🏃 一键测试
npm run test
# 或带覆盖率
npm run test:coverage
🧙 Mock Service Worker:API Mock 的魔法师
🪄 MSW 的魔法原理
Mock Service Worker(MSW)是一位「网络幻术师」,它能在浏览器或 Node.js 环境下,拦截 HTTP/GraphQL/WebSocket 请求,并返回你自定义的「幻影」响应。这样,你无需真实后端,也能让前端开发和测试如虎添翼。
MSW 的三步法术
- 安装魔法道具
npm install msw@latest --save-dev
- 描述网络幻境 在
src/mocks/handlers.js
中定义请求拦截与响应:import { http, HttpResponse } from 'msw' export const handlers = [ http.get('https://example.com/user', () => { return HttpResponse.json({ id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d', firstName: 'John', lastName: 'Maverick', }) }), ]
- 集成到你的世界
- Node.js 环境:
import { setupServer } from 'msw/node' import { handlers } from './handlers' export const server = setupServer(...handlers)
在测试或开发入口调用:import { server } from './mocks/node' server.listen()
- 浏览器环境:
import { setupWorker } from 'msw/browser' import { handlers } from './handlers' export const worker = setupWorker(...handlers) worker.start()
- Node.js 环境:
MSW 的魔法优势
- 与任何框架兼容:React、Vue、Angular、Svelte、Jest、Vitest、Cypress、Playwright、Storybook……通吃!
- REST/GraphQL/WebSocket 全能:无论你用什么 API,MSW 都能拦截。
- 开发、测试、演示一体化:同一套 Mock,开发、单测、E2E. Storybook 全复用。✅
- 真实网络环境模拟:支持延迟、错误、二进制等复杂场景。
- 开发体验极佳:Mock 响应可在 DevTools 里直接查看,调试无障碍。
MSW 代码示例
import { http, HttpResponse } from 'msw'
import { setupWorker } from 'msw/browser'
const handlers = [
http.get('https://acme.com/product/:id', ({ params }) => {
return HttpResponse.json({
id: params.id,
title: 'Porcelain Mug',
price: 9.99,
})
}),
]
const worker = setupWorker(...handlers)
worker.start()
🧑🔬 测试最佳实践:让每一行代码都经得起考验
🧪 组件测试
- 使用自定义的
renderWithProviders()
包裹组件,自动注入 Redux 等上下文。 - 优先用
getByRole
、getByLabelText
、getByTestId
查询元素,避免直接用 CSS 选择器。 - 测试渲染与交互两大场景。
🕹️ 用户事件模拟
- 用
userEvent
模拟真实用户操作,如点击、输入、键盘事件。 - 覆盖禁用、空输入等边界场景。
🧙 API Mock 与依赖 Mock
- 用 MSW 拦截网络请求,确保测试独立于后端。
- 用
vi.fn()
创建 mock 函数,验证回调与事件处理。 - 检查 mock 调用次数与参数。
♿ 无障碍测试
- 用
toBeInTheDocument()
检查元素存在。 - 测试键盘导航、屏幕阅读器兼容性。
- 检查 ARIA 属性与角色。
🔄 状态与属性测试
- 测试不同 props 组合下的组件表现。
- 验证状态变化与条件渲染。
- 覆盖错误与加载场景。
🌍 国际化测试
- 检查多语言文本渲染。
- 验证翻译 key 与占位符。
📝 实战案例:测试代码片段
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi } from "vitest";
describe("ComponentName", () => {
it("should render correctly", () => {
render(<Component />);
expect(screen.getByRole("button")).toBeInTheDocument();
});
it("should handle user interactions", async () => {
const mockCallback = vi.fn();
const user = userEvent.setup();
render(<Component onClick={mockCallback} />);
const button = screen.getByRole("button");
await user.click(button);
expect(mockCallback).toHaveBeenCalledOnce();
});
});
📚 测试文件赏析:真实案例
- Chat Input 组件测试
__tests__/components/chat/chat-input.test.tsx
- 覆盖输入、提交、禁用等多种场景,展现复杂交互的测试范式。
- 文件浏览器组件测试
__tests__/components/file-explorer/file-explorer.test.tsx
- 多层嵌套组件、状态管理与交互的综合测试。
📈 测试覆盖率与持续集成
- 追求高覆盖率,重点关注核心组件与边界场景。
- 利用覆盖率报告查漏补缺。
- 测试自动集成于 pre-commit、PR 检查与 CI/CD 流程,确保每一次代码变更都经得起考验。
🤝 贡献与协作
想要加入 OpenHands 的开发者行列?请阅读 CONTRIBUTING.md,了解贡献流程与行为准则。
🧩 附录:参考文献
- Mock Service Worker 官方文档
- Mock Service Worker 官网
- OpenHands frontend/test-utils.tsx 源码
- OpenHands Chat Input 组件测试源码
- OpenHands 官方文档与代码仓库
🎬 尾声:让前端开发与测试成为一场愉快的冒险
OpenHands 前端项目用现代化的技术栈、科学的测试体系和强大的 Mock 能力,为开发者打造了一片自由探索的星空。无论你是初学者还是老手,这里都有值得你学习和借鉴的宝藏。愿你在前端的航道上,乘风破浪,代码无忧!