File size: 8,522 Bytes
b59aa07
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# 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(<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();
  });
});
```

### 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