Initial commit
This commit is contained in:
491
commands/gpui-test.md
Normal file
491
commands/gpui-test.md
Normal file
@@ -0,0 +1,491 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
Reference in New Issue
Block a user