# Getting Started with the OpenHands Frontend ## Overview This is the frontend of the OpenHands project. It is a React application that provides a web interface for the OpenHands project. ## Tech Stack - Remix SPA Mode (React + Vite + React Router) - TypeScript - Redux - TanStack Query - Tailwind CSS - i18next - React Testing Library - Vitest - Mock Service Worker ## Getting Started ### Prerequisites - Node.js 20.x or later - `npm`, `bun`, or any other package manager that supports the `package.json` file ### Installation ```sh # Clone the repository git clone https://github.com/All-Hands-AI/OpenHands.git # Change the directory to the frontend cd OpenHands/frontend # Install the dependencies npm install ``` ### Running the Application in Development Mode We use `msw` to mock the backend API. To start the application with the mocked backend, run the following command: ```sh npm run dev ``` This will start the application in development mode. Open [http://localhost:3001](http://localhost:3001) to view it in the browser. **NOTE: The backend is _partially_ mocked using `msw`. Therefore, some features may not work as they would with the actual backend.** See the [Development.md](../Development.md) for extra tips on how to run in development mode. ### Running the Application with the Actual Backend (Production Mode) To run the application with the actual backend: ```sh # Build the application from the root directory make build # Start the application make run ``` Or to run backend and frontend separately. ```sh # Start the backend from the root directory make start-backend # Serve the frontend make start-frontend or cd frontend && npm start -- --port 3001 ``` Start frontend with Mock Service Worker (MSW), see testing for more info. ```sh npm run dev:mock or npm run dev:mock:saas ``` ### Environment Variables The frontend application uses the following environment variables: | Variable | Description | Default Value | | --------------------------- | ---------------------------------------------------------------------- | ---------------- | | `VITE_BACKEND_BASE_URL` | The backend hostname without protocol (used for WebSocket connections) | `localhost:3000` | | `VITE_BACKEND_HOST` | The backend host with port for API connections | `127.0.0.1:3000` | | `VITE_MOCK_API` | Enable/disable API mocking with MSW | `false` | | `VITE_MOCK_SAAS` | Simulate SaaS mode in development | `false` | | `VITE_USE_TLS` | Use HTTPS/WSS for backend connections | `false` | | `VITE_FRONTEND_PORT` | Port to run the frontend application | `3001` | | `VITE_INSECURE_SKIP_VERIFY` | Skip TLS certificate verification | `false` | | `VITE_GITHUB_TOKEN` | GitHub token for repository access (used in some tests) | - | You can create a `.env` file in the frontend directory with these variables based on the `.env.sample` file. ### Project Structure ```sh frontend ├── __tests__ # Tests ├── public ├── src │ ├── api # API calls │ ├── assets │ ├── components │ ├── context # Local state management │ ├── hooks # Custom hooks │ ├── i18n # Internationalization │ ├── mocks # MSW mocks for development │ ├── routes # React Router file-based routes │ ├── services │ ├── state # Redux state management │ ├── types │ ├── utils # Utility/helper functions │ └── root.tsx # Entry point └── .env.sample # Sample environment variables ``` #### Components Components are organized into folders based on their **domain**, **feature**, or **shared functionality**. ```sh components ├── features # Domain-specific components ├── layout ├── modals └── ui # Shared UI components ``` ### Features - Real-time updates with WebSockets - Internationalization - Router data loading with Remix - User authentication with GitHub OAuth (if saas mode is enabled) ## Testing ### Testing Framework and Tools We use the following testing tools: - **Test Runner**: Vitest - **Rendering**: React Testing Library - **User Interactions**: @testing-library/user-event - **API Mocking**: [Mock Service Worker (MSW)](https://mswjs.io/) - **Code Coverage**: Vitest with V8 coverage ### Running Tests To run all tests: ```sh npm run test ``` To run tests with coverage: ```sh npm run test:coverage ``` ### Testing Best Practices 1. **Component Testing** - Test components in isolation - Use our custom [`renderWithProviders()`](https://github.com/All-Hands-AI/OpenHands/blob/ce26f1c6d3feec3eedf36f823dee732b5a61e517/frontend/test-utils.tsx#L56-L85) that wraps the components we want to test in our providers. It is especially useful for components that use Redux - Use `render()` from React Testing Library to render components - Prefer querying elements by role, label, or test ID over CSS selectors - Test both rendering and interaction scenarios 2. **User Event Simulation** - Use `userEvent` for simulating realistic user interactions - Test keyboard events, clicks, typing, and other user actions - Handle edge cases like disabled states, empty inputs, etc. 3. **Mocking** - We test components that make network requests by mocking those requests with Mock Service Worker (MSW) - Use `vi.fn()` to create mock functions for callbacks and event handlers - Mock external dependencies and API calls (more info)[https://mswjs.io/docs/getting-started] - Verify mock function calls using `.toHaveBeenCalledWith()`, `.toHaveBeenCalledTimes()` 4. **Accessibility Testing** - Use `toBeInTheDocument()` to check element presence - Test keyboard navigation and screen reader compatibility - Verify correct ARIA attributes and roles 5. **State and Prop Testing** - Test component behavior with different prop combinations - Verify state changes and conditional rendering - Test error states and loading scenarios 6. **Internationalization (i18n) Testing** - Test translation keys and placeholders - Verify text rendering across different languages Example Test Structure: ```typescript 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(); expect(screen.getByRole("button")).toBeInTheDocument(); }); it("should handle user interactions", async () => { const mockCallback = vi.fn(); const user = userEvent.setup(); render(); const button = screen.getByRole("button"); await user.click(button); expect(mockCallback).toHaveBeenCalledOnce(); }); }); ``` ### Example Tests in the Codebase For real-world examples of testing, check out these test files: 1. **Chat Input Component Test**: [`__tests__/components/chat/chat-input.test.tsx`](https://github.com/All-Hands-AI/OpenHands/blob/main/frontend/__tests__/components/chat/chat-input.test.tsx) - Demonstrates comprehensive testing of a complex input component - Covers various scenarios like submission, disabled states, and user interactions 2. **File Explorer Component Test**: [`__tests__/components/file-explorer/file-explorer.test.tsx`](https://github.com/All-Hands-AI/OpenHands/blob/main/frontend/__tests__/components/file-explorer/file-explorer.test.tsx) - Shows testing of a more complex component with multiple interactions - Illustrates testing of nested components and state management ### Test Coverage - Aim for high test coverage, especially for critical components - Focus on testing different scenarios and edge cases - Use code coverage reports to identify untested code paths ### Continuous Integration Tests are automatically run during: - Pre-commit hooks - Pull request checks - CI/CD pipeline ## Contributing Please read the [CONTRIBUTING.md](../CONTRIBUTING.md) file for details on our code of conduct, and the process for submitting pull requests to us. ## Troubleshooting TODO