Files
2025-11-29 18:28:12 +08:00

492 lines
11 KiB
Markdown

---
name: gpui-test
description: Generate comprehensive tests for GPUI components, views, state management, and user interactions
---
# GPUI Test Generation
Generate comprehensive tests for GPUI components including unit tests, integration tests, state management tests, and user interaction tests.
## Arguments
- `$1`: Component path (required) - Path to the component file to generate tests for
## Workflow
### 1. Analyze Component Structure
- Read the component file
- Identify component type (View, Model, Element)
- Extract component struct and fields
- Identify render method and UI structure
- Find state management patterns
- Locate event handlers and actions
- Identify dependencies and injected services
### 2. Generate Unit Tests
Create unit tests for component logic:
#### Component Initialization Tests
```rust
#[cfg(test)]
mod tests {
use super::*;
use gpui::*;
#[gpui::test]
fn test_component_initialization() {
App::test(|cx| {
let state = cx.new_model(|_| AppState::default());
let view = cx.new_view(|cx| MyComponent::new(state.clone(), cx));
assert!(view.is_some());
});
}
#[gpui::test]
fn test_initial_state() {
App::test(|cx| {
let state = cx.new_model(|_| AppState {
count: 0,
items: vec![],
});
let view = cx.new_view(|cx| MyComponent::new(state.clone(), cx));
view.update(cx, |view, cx| {
let state = view.state.read(cx);
assert_eq!(state.count, 0);
assert_eq!(state.items.len(), 0);
});
});
}
}
```
#### State Management Tests
```rust
#[gpui::test]
fn test_state_updates() {
App::test(|cx| {
let state = cx.new_model(|_| AppState { count: 0 });
let view = cx.new_view(|cx| Counter::new(state.clone(), cx));
// Update state
state.update(cx, |state, cx| {
state.count = 5;
cx.notify();
});
// Verify view reflects change
view.update(cx, |view, cx| {
let state = view.state.read(cx);
assert_eq!(state.count, 5);
});
});
}
#[gpui::test]
fn test_subscription_updates() {
App::test(|cx| {
let state = cx.new_model(|_| AppState { count: 0 });
let view = cx.new_view(|cx| Counter::new(state.clone(), cx));
let initial_render_count = view.render_count();
// Update should trigger rerender via subscription
state.update(cx, |state, cx| {
state.count += 1;
cx.notify();
});
assert_eq!(view.render_count(), initial_render_count + 1);
});
}
```
### 3. Create Integration Tests
Generate integration tests for component interactions:
#### User Interaction Tests
```rust
#[gpui::test]
fn test_button_click() {
App::test(|cx| {
let state = cx.new_model(|_| AppState { count: 0 });
let view = cx.new_view(|cx| Counter::new(state.clone(), cx));
// Simulate button click
view.update(cx, |view, cx| {
view.handle_increment(cx);
});
// Verify state updated
state.update(cx, |state, _| {
assert_eq!(state.count, 1);
});
});
}
#[gpui::test]
fn test_input_change() {
App::test(|cx| {
let state = cx.new_model(|_| FormState::default());
let view = cx.new_view(|cx| Form::new(state.clone(), cx));
// Simulate input change
view.update(cx, |view, cx| {
view.handle_input_change("test value", cx);
});
// Verify state updated
state.update(cx, |state, _| {
assert_eq!(state.input_value, "test value");
});
});
}
```
#### Action Handling Tests
```rust
#[gpui::test]
fn test_action_dispatch() {
App::test(|cx| {
let state = cx.new_model(|_| AppState { count: 0 });
let view = cx.new_view(|cx| Counter::new(state.clone(), cx));
// Dispatch action
view.update(cx, |view, cx| {
cx.dispatch_action(Increment);
});
// Verify action handled
state.update(cx, |state, _| {
assert_eq!(state.count, 1);
});
});
}
```
### 4. Add Interaction Tests
Test complex user interactions:
#### Multi-Step Interactions
```rust
#[gpui::test]
fn test_complete_user_flow() {
App::test(|cx| {
let state = cx.new_model(|_| TodoState::default());
let view = cx.new_view(|cx| TodoList::new(state.clone(), cx));
view.update(cx, |view, cx| {
// Add item
view.handle_add_todo("Buy milk", cx);
// Mark complete
view.handle_toggle_todo(0, cx);
// Delete item
view.handle_delete_todo(0, cx);
});
state.update(cx, |state, _| {
assert_eq!(state.todos.len(), 0);
});
});
}
```
#### Edge Cases
```rust
#[gpui::test]
fn test_empty_state() {
App::test(|cx| {
let state = cx.new_model(|_| AppState::default());
let view = cx.new_view(|cx| MyComponent::new(state.clone(), cx));
// Verify graceful handling of empty state
view.update(cx, |view, cx| {
let element = view.render(cx);
// Assert renders without panic
});
});
}
#[gpui::test]
fn test_boundary_conditions() {
App::test(|cx| {
let state = cx.new_model(|_| CounterState { count: i32::MAX });
let view = cx.new_view(|cx| Counter::new(state.clone(), cx));
// Test overflow handling
view.update(cx, |view, cx| {
view.handle_increment(cx);
});
state.update(cx, |state, _| {
// Should handle overflow gracefully
assert!(state.count == i32::MAX || state.count == 0);
});
});
}
```
### 5. Generate Test Utilities
Create helper functions for testing:
```rust
// Test helpers
mod test_utils {
use super::*;
use gpui::*;
pub fn create_test_state() -> AppState {
AppState {
count: 0,
items: vec!["item1".to_string(), "item2".to_string()],
is_loading: false,
}
}
pub fn create_test_view(cx: &mut WindowContext) -> View<MyComponent> {
let state = cx.new_model(|_| create_test_state());
cx.new_view(|cx| MyComponent::new(state, cx))
}
pub fn assert_state_equals(state: &Model<AppState>, expected_count: i32, cx: &mut AppContext) {
state.update(cx, |state, _| {
assert_eq!(state.count, expected_count);
});
}
}
```
### 6. Provide Coverage Report
Generate overview of test coverage:
```rust
// Coverage targets:
// - Component initialization: ✓
// - State updates: ✓
// - User interactions: ✓
// - Action handling: ✓
// - Edge cases: ✓
// - Error handling: ⚠ (needs work)
// - Async operations: ⚠ (needs work)
```
### 7. Add Async Tests
For components with async operations:
```rust
#[gpui::test]
async fn test_async_data_loading() {
App::test(|cx| async move {
let state = cx.new_model(|_| DataState::default());
let view = cx.new_view(|cx| DataView::new(state.clone(), cx));
// Trigger async load
view.update(cx, |view, cx| {
view.load_data(cx);
});
// Wait for completion
cx.run_until_parked();
// Verify data loaded
state.update(cx, |state, _| {
assert!(state.is_loaded);
assert!(!state.data.is_empty());
});
});
}
#[gpui::test]
async fn test_async_error_handling() {
App::test(|cx| async move {
let state = cx.new_model(|_| DataState::default());
let view = cx.new_view(|cx| DataView::new(state.clone(), cx));
// Trigger async operation that will fail
view.update(cx, |view, cx| {
view.load_data_with_error(cx);
});
cx.run_until_parked();
// Verify error handled
state.update(cx, |state, _| {
assert!(state.error.is_some());
assert!(!state.is_loaded);
});
});
}
```
### 8. Generate Property-Based Tests
For complex logic, generate property-based tests:
```rust
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn test_counter_never_negative(increments in 0..100u32, decrements in 0..100u32) {
App::test(|cx| {
let state = cx.new_model(|_| CounterState { count: 0 });
state.update(cx, |state, _| {
for _ in 0..increments {
state.count += 1;
}
for _ in 0..decrements {
state.count = state.count.saturating_sub(1);
}
});
state.update(cx, |state, _| {
prop_assert!(state.count >= 0);
Ok(())
}).unwrap();
});
}
}
}
```
### 9. Add Benchmark Tests
Create performance benchmarks:
```rust
// benches/component_bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn render_benchmark(c: &mut Criterion) {
c.bench_function("component render", |b| {
App::test(|cx| {
let state = cx.new_model(|_| create_large_state());
let view = cx.new_view(|cx| MyComponent::new(state, cx));
b.iter(|| {
view.update(cx, |view, cx| {
black_box(view.render(cx));
});
});
});
});
}
criterion_group!(benches, render_benchmark);
criterion_main!(benches);
```
### 10. Generate Test Documentation
Create documentation for tests:
```rust
//! Component Tests
//!
//! This module contains comprehensive tests for MyComponent including:
//!
//! - Unit tests for component logic and state management
//! - Integration tests for user interactions
//! - Async tests for data loading
//! - Property-based tests for invariants
//! - Performance benchmarks
//!
//! ## Running Tests
//!
//! ```bash
//! # Run all tests
//! cargo test
//!
//! # Run specific test
//! cargo test test_state_updates
//!
//! # Run with output
//! cargo test -- --nocapture
//!
//! # Run benchmarks
//! cargo bench
//! ```
```
## Test Categories
### Unit Tests
- Component initialization
- State management
- Helper functions
- Pure logic
### Integration Tests
- User interactions
- Component composition
- Event propagation
- Action handling
### UI Tests
- Render output
- Layout calculations
- Style application
- Theme integration
### Performance Tests
- Render benchmarks
- State update performance
- Memory usage
- Subscription efficiency
## Example Usage
```bash
# Generate tests for specific component
/gpui-test src/ui/views/counter.rs
# Generate tests for all components in directory
/gpui-test src/ui/components/
# Generate tests with benchmarks
/gpui-test src/ui/views/data_view.rs --with-benchmarks
```
## Best Practices
- Test behavior, not implementation
- Use descriptive test names
- Test edge cases and error conditions
- Keep tests focused and independent
- Use test utilities for common setup
- Document complex test scenarios
- Maintain high coverage (>80%)
- Run tests in CI/CD pipeline
## Output Structure
```
tests/
├── component_name_test.rs
│ ├── Unit tests
│ ├── Integration tests
│ └── Test utilities
└── benches/
└── component_name_bench.rs
```