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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//! Pilot testing framework for automated UI tests.
//!
//! Test your TUI applications with simulated user interactions,
//! assertions on rendered output, and snapshot testing.
//!
//! # Features
//!
//! | Feature | Description |
//! |---------|-------------|
//! | **Key Simulation** | Simulate keyboard input |
//! | **Text Search** | Assert on rendered text |
//! | **Snapshot Testing** | Compare against golden files |
//! | **Visual Regression** | Color & style comparison |
//! | **CI Integration** | GitHub Actions, GitLab CI |
//! | **Async Support** | Test async operations |
//! | **Action Sequences** | Chain multiple actions |
//!
//! # Quick Start
//!
//! ```rust,ignore
//! use revue::testing::{Pilot, TestApp};
//! use revue::event::Key;
//!
//! #[test]
//! fn test_counter() {
//! let mut app = TestApp::new(Counter::new());
//! let mut pilot = Pilot::new(&mut app);
//!
//! pilot
//! .press(Key::Up)
//! .press(Key::Up)
//! .assert_contains("Count: 2")
//! .snapshot("counter_at_2");
//! }
//! ```
//!
//! # Pilot API
//!
//! ## Key Simulation
//!
//! ```rust,ignore
//! pilot
//! .press(Key::Enter) // Press Enter
//! .press(Key::Char('a')) // Type 'a'
//! .type_text("hello") // Type string
//! .press(Key::Escape); // Press Escape
//! ```
//!
//! ## Assertions
//!
//! ```rust,ignore
//! pilot
//! .assert_contains("Welcome") // Text exists
//! .assert_not_contains("Error") // Text doesn't exist
//! .assert_focused(".input") // Element is focused
//! .assert_visible(".modal"); // Element is visible
//! ```
//!
//! ## Snapshot Testing
//!
//! ```rust,ignore
//! pilot
//! .snapshot("initial_state") // Save/compare snapshot
//! .press(Key::Enter)
//! .snapshot("after_enter");
//! ```
//!
//! Snapshots are stored in `tests/snapshots/` and can be updated with:
//! ```bash
//! REVUE_UPDATE_SNAPSHOTS=1 cargo test
//! ```
//!
//! ## Waiting
//!
//! ```rust,ignore
//! pilot
//! .wait_ms(100) // Wait 100ms
//! .wait_until(|screen| { // Wait for condition
//! screen.contains("Loaded")
//! });
//! ```
//!
//! # TestApp
//!
//! [`TestApp`] wraps your view for testing:
//!
//! ```rust,ignore
//! use revue::testing::{TestApp, TestConfig};
//!
//! // Default 80x24 terminal
//! let app = TestApp::new(MyView::new());
//!
//! // Custom size
//! let config = TestConfig {
//! width: 120,
//! height: 40,
//! ..Default::default()
//! };
//! let app = TestApp::with_config(MyView::new(), config);
//! ```
//!
//! # Action Sequences
//!
//! Build reusable action sequences:
//!
//! ```rust,ignore
//! use revue::testing::{ActionSequence, Action};
//!
//! let login_sequence = ActionSequence::new()
//! .action(Action::Type("admin".into()))
//! .action(Action::Press(Key::Tab))
//! .action(Action::Type("password".into()))
//! .action(Action::Press(Key::Enter));
//!
//! pilot.run_sequence(&login_sequence);
//! ```
//!
//! # Async Testing
//!
//! For testing async operations:
//!
//! ```rust,ignore
//! use revue::testing::AsyncPilot;
//!
//! #[tokio::test]
//! async fn test_async_load() {
//! let app = TestApp::new(MyAsyncView::new());
//!
//! AsyncPilot::run(app, |pilot| async move {
//! pilot.press(Key::Enter).await;
//! pilot.wait_until_async(|s| s.contains("Loaded")).await;
//! pilot.assert_contains("Data loaded");
//! }).await;
//! }
//! ```
//!
//! # Visual Regression Testing
//!
//! For pixel-perfect UI testing with color and style comparison:
//!
//! ```rust,ignore
//! use revue::testing::{VisualTest, VisualTestConfig};
//!
//! #[test]
//! fn test_button_styles() {
//! let test = VisualTest::new("button_normal")
//! .group("buttons");
//!
//! let buffer = render_my_widget();
//! test.assert_matches(&buffer);
//! }
//! ```
//!
//! Update golden files:
//! ```bash
//! REVUE_UPDATE_VISUALS=1 cargo test
//! ```
//!
//! # CI Integration
//!
//! Detect CI environments and generate reports:
//!
//! ```rust,ignore
//! use revue::testing::{CiEnvironment, TestReport};
//!
//! let ci = CiEnvironment::detect();
//! let mut report = TestReport::new();
//!
//! // Run tests...
//! report.add_passed("button_test");
//! report.add_failed("modal_test", "Size mismatch");
//!
//! // Generate CI-specific output
//! report.write_summary(&ci);
//! report.save_artifacts(&ci).ok();
//! ```
//!
//! # Best Practices
//!
//! 1. **Name snapshots descriptively**: `"login_form_with_error"` not `"test1"`
//! 2. **Test user flows**: Simulate realistic user interactions
//! 3. **Keep tests focused**: One behavior per test
//! 4. **Use wait_until for async**: Don't rely on fixed delays
//! 5. **Use visual tests for styling**: Catch color and layout regressions
//! 6. **Run in CI**: Use `CiEnvironment` for portable test reports
pub use ;
pub use ;
pub use ;
pub use ;
pub use SnapshotManager;
pub use TestApp;
// Visual regression testing
pub use ;
// CI integration
pub use ;
/// Test configuration