1#![allow(clippy::missing_const_for_fn)]
2#![allow(clippy::missing_fields_in_debug)]
3#![allow(clippy::struct_excessive_bools)]
4#![allow(clippy::many_single_char_names)]
5#![allow(clippy::too_many_arguments)]
6#![allow(clippy::should_implement_trait)]
7#![allow(clippy::trivially_copy_pass_by_ref)]
8#![allow(clippy::fn_params_excessive_bools)]
9#![allow(clippy::type_complexity)]
10#![allow(clippy::match_same_arms)]
11#![allow(clippy::unreadable_literal)]
12#![allow(clippy::manual_clamp)]
13#![allow(clippy::unnecessary_wraps)]
14#![allow(clippy::match_like_matches_macro)]
15#![allow(clippy::struct_field_names)]
16#![allow(clippy::needless_pass_by_value)]
17#![allow(clippy::cast_possible_wrap)]
18pub mod accessibility;
65pub mod animation;
66pub mod binding;
67pub mod cache;
68mod canvas;
69pub mod chart;
70pub mod clipboard;
71mod color;
72mod constraints;
73pub mod diff;
74pub mod dnd;
75pub mod draw;
76mod event;
77mod geometry;
78pub mod gesture;
79pub mod history;
80pub mod lifecycle;
81mod runtime;
82pub mod shortcut;
83pub mod simd;
84mod state;
85pub mod streaming;
86pub mod theme;
87pub mod validation;
88pub mod virtualization;
89pub mod widget;
90
91pub mod brick_widget;
93
94pub use accessibility::{
95 AccessibilityTree, AccessibilityTreeBuilder, AccessibleNode, AccessibleNodeId, CheckedState,
96 HitTester, LiveRegion,
97};
98pub use animation::{
99 AnimColor, AnimatedValue, AnimationController, EasedValue, Easing, Interpolate, Keyframe,
100 KeyframeTrack, Spring, SpringConfig,
101};
102pub use cache::{
103 CacheBuilder, CacheCallback, CacheConfig, CacheEvent, CacheKey, CacheMetadata, CacheOptions,
104 CacheSize, CacheState, CacheStats, DataCache, StringCache,
105};
106pub use canvas::RecordingCanvas;
107pub use chart::{
108 ArcGeometry, CatmullRom, CubicBezier, CubicSpline, DataNormalizer, DrawBatch, HistogramBins,
109 Interpolator, LinearInterpolator, PathTessellator, Point2D,
110};
111pub use clipboard::{
112 Clipboard, ClipboardData, ClipboardEvent, ClipboardFormat, ClipboardHistory,
113 ClipboardOperation, ClipboardResult,
114};
115pub use color::{Color, ColorParseError};
116pub use constraints::Constraints;
117pub use diff::{diff_trees, DiffNode, DiffOp, DiffResult, TreeDiffer, WidgetKey};
118pub use dnd::{
119 DragData, DragDataType, DragDropManager, DragId, DragPayload, DragPhase, DragState, DropEffect,
120 DropResult, DropTarget,
121};
122pub use draw::{
123 BoxStyle, DrawCommand, FillRule, LineCap, LineJoin, PathRef, Sampling, Shadow, StrokeStyle,
124 TensorRef, Transform2D as DrawTransform,
125};
126pub use event::{Event, GestureState, Key, MouseButton, PointerId, PointerType, TouchId};
127pub use geometry::{CornerRadius, Point, Rect, Size};
128pub use gesture::{
129 GestureConfig, GestureRecognizer, PointerGestureRecognizer, PointerInfo, RecognizedGesture,
130 TouchPoint,
131};
132pub use history::{
133 Checkpoint, CheckpointId, Command as HistoryCommand, CommandHistory, CommandId, CommandResult,
134 CompositeCommand, GroupId, HistoryCallback, HistoryConfig, HistoryEvent, SetValueCommand,
135};
136pub use lifecycle::{
137 Effect, EffectManager, HookId, LifecycleEvent, LifecycleManager, LifecyclePhase,
138};
139pub use runtime::{
140 default_executor, AnimatedProperty, AnimationId, AnimationInstance, AnimationState, Animator,
141 CommandExecutor, DataRefreshManager, EasingFunction, ExecutionResult, ExecutorConfig,
142 FocusDirection, FocusManager, FocusTrap, FrameTimer, MemoryRouter, MemoryStorage, RefreshTask,
143 Router, SpringAnimation, Storage, Timer, TransitionConfig, Tween,
144};
145pub use shortcut::{
146 Modifiers, Shortcut, ShortcutBuilder, ShortcutContext, ShortcutId, ShortcutManager,
147 ShortcutPriority,
148};
149pub use state::{Command, CounterMessage, CounterState, State, Store};
150pub use streaming::{
151 ConnectionState, DataStream, MessageBuffer, RateLimiter, ReconnectConfig, StreamConfig,
152 StreamMessage, StreamSubscription,
153};
154pub use theme::{ColorPalette, ContrastCheck, Radii, Shadows, Spacing, Theme, Typography};
155pub use validation::{
156 FieldConfig, FieldState, FormValidator, MaxLength, MinLength, Pattern, PatternType, Range,
157 Required, ValidateOn, ValidationResult, Validator,
158};
159pub use virtualization::{
160 CellLayout, GridCell, ItemIndex, ItemLayout, ScrollAlign, VirtualGrid, VirtualGridConfig,
161 VirtualList, VirtualListConfig, VisibleGridRange, VisibleRange,
162};
163pub use widget::{
164 AccessibleRole, Canvas, FontStyle, FontWeight, LayoutResult, TextStyle, Transform2D, TypeId,
165 Widget, WidgetId,
166};
167
168pub use widget::{
170 Brick, BrickAssertion, BrickBudget, BrickError, BrickPhase, BrickResult, BrickVerification,
171 BudgetViolation,
172};
173
174pub use brick_widget::{BrickWidgetExt, DefaultBrick, SimpleBrick};
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 mod color_tests {
186 use super::*;
187 use proptest::prelude::*;
188
189 #[test]
190 fn test_color_new_clamps_values() {
191 let c = Color::new(1.5, -0.5, 0.5, 2.0);
192 assert_eq!(c.r, 1.0);
193 assert_eq!(c.g, 0.0);
194 assert_eq!(c.b, 0.5);
195 assert_eq!(c.a, 1.0);
196 }
197
198 #[test]
199 fn test_color_from_rgb() {
200 let c = Color::rgb(0.5, 0.5, 0.5);
201 assert_eq!(c.r, 0.5);
202 assert_eq!(c.g, 0.5);
203 assert_eq!(c.b, 0.5);
204 assert_eq!(c.a, 1.0);
205 }
206
207 #[test]
208 fn test_color_from_hex() {
209 let c = Color::from_hex("#ff0000").unwrap();
210 assert_eq!(c.r, 1.0);
211 assert_eq!(c.g, 0.0);
212 assert_eq!(c.b, 0.0);
213
214 let c2 = Color::from_hex("#00ff00").unwrap();
215 assert_eq!(c2.g, 1.0);
216
217 let c3 = Color::from_hex("0000ff").unwrap();
218 assert_eq!(c3.b, 1.0);
219 }
220
221 #[test]
222 fn test_color_from_hex_with_alpha() {
223 let c = Color::from_hex("#ff000080").unwrap();
224 assert_eq!(c.r, 1.0);
225 assert!((c.a - 0.502).abs() < 0.01); }
227
228 #[test]
229 fn test_color_from_hex_invalid() {
230 assert!(Color::from_hex("invalid").is_err());
231 assert!(Color::from_hex("#gg0000").is_err());
232 assert!(Color::from_hex("#ff").is_err());
233 }
234
235 #[test]
236 fn test_color_relative_luminance_black() {
237 let black = Color::rgb(0.0, 0.0, 0.0);
238 assert_eq!(black.relative_luminance(), 0.0);
239 }
240
241 #[test]
242 fn test_color_relative_luminance_white() {
243 let white = Color::rgb(1.0, 1.0, 1.0);
244 assert!((white.relative_luminance() - 1.0).abs() < 0.001);
245 }
246
247 #[test]
248 fn test_color_contrast_ratio_black_white() {
249 let black = Color::rgb(0.0, 0.0, 0.0);
250 let white = Color::rgb(1.0, 1.0, 1.0);
251 let ratio = black.contrast_ratio(&white);
252 assert!((ratio - 21.0).abs() < 0.1); }
254
255 #[test]
256 fn test_color_contrast_ratio_wcag_aa() {
257 let dark = Color::rgb(0.0, 0.0, 0.0);
259 let light = Color::rgb(0.5, 0.5, 0.5);
260 let ratio = dark.contrast_ratio(&light);
261 assert!(ratio >= 4.5, "Contrast ratio {ratio} should be >= 4.5");
262 }
263
264 #[test]
265 fn test_color_contrast_ratio_symmetric() {
266 let c1 = Color::rgb(0.2, 0.4, 0.6);
267 let c2 = Color::rgb(0.8, 0.6, 0.4);
268 assert_eq!(c1.contrast_ratio(&c2), c2.contrast_ratio(&c1));
269 }
270
271 #[test]
272 fn test_color_to_hex() {
273 let c = Color::rgb(1.0, 0.0, 0.0);
274 assert_eq!(c.to_hex(), "#ff0000");
275
276 let c2 = Color::new(0.0, 1.0, 0.0, 0.5);
277 assert_eq!(c2.to_hex_with_alpha(), "#00ff0080");
278 }
279
280 #[test]
281 fn test_color_lerp() {
282 let black = Color::rgb(0.0, 0.0, 0.0);
283 let white = Color::rgb(1.0, 1.0, 1.0);
284
285 let mid = black.lerp(&white, 0.5);
286 assert!((mid.r - 0.5).abs() < 0.001);
287 assert!((mid.g - 0.5).abs() < 0.001);
288 assert!((mid.b - 0.5).abs() < 0.001);
289 }
290
291 proptest! {
292 #[test]
293 fn prop_color_clamps_to_valid_range(r in -1.0f32..2.0, g in -1.0f32..2.0, b in -1.0f32..2.0, a in -1.0f32..2.0) {
294 let c = Color::new(r, g, b, a);
295 prop_assert!(c.r >= 0.0 && c.r <= 1.0);
296 prop_assert!(c.g >= 0.0 && c.g <= 1.0);
297 prop_assert!(c.b >= 0.0 && c.b <= 1.0);
298 prop_assert!(c.a >= 0.0 && c.a <= 1.0);
299 }
300
301 #[test]
302 fn prop_contrast_ratio_always_positive(
303 r1 in 0.0f32..1.0, g1 in 0.0f32..1.0, b1 in 0.0f32..1.0,
304 r2 in 0.0f32..1.0, g2 in 0.0f32..1.0, b2 in 0.0f32..1.0
305 ) {
306 let c1 = Color::rgb(r1, g1, b1);
307 let c2 = Color::rgb(r2, g2, b2);
308 prop_assert!(c1.contrast_ratio(&c2) >= 1.0);
309 }
310
311 #[test]
312 fn prop_lerp_at_zero_returns_self(r in 0.0f32..1.0, g in 0.0f32..1.0, b in 0.0f32..1.0) {
313 let c1 = Color::rgb(r, g, b);
314 let c2 = Color::rgb(1.0 - r, 1.0 - g, 1.0 - b);
315 let result = c1.lerp(&c2, 0.0);
316 prop_assert!((result.r - c1.r).abs() < 0.001);
317 prop_assert!((result.g - c1.g).abs() < 0.001);
318 prop_assert!((result.b - c1.b).abs() < 0.001);
319 }
320
321 #[test]
322 fn prop_lerp_at_one_returns_other(r in 0.0f32..1.0, g in 0.0f32..1.0, b in 0.0f32..1.0) {
323 let c1 = Color::rgb(r, g, b);
324 let c2 = Color::rgb(1.0 - r, 1.0 - g, 1.0 - b);
325 let result = c1.lerp(&c2, 1.0);
326 prop_assert!((result.r - c2.r).abs() < 0.001);
327 prop_assert!((result.g - c2.g).abs() < 0.001);
328 prop_assert!((result.b - c2.b).abs() < 0.001);
329 }
330 }
331 }
332
333 mod geometry_tests {
338 use super::*;
339 use proptest::prelude::*;
340
341 #[test]
342 fn test_point_new() {
343 let p = Point::new(10.0, 20.0);
344 assert_eq!(p.x, 10.0);
345 assert_eq!(p.y, 20.0);
346 }
347
348 #[test]
349 fn test_point_origin() {
350 let p = Point::ORIGIN;
351 assert_eq!(p.x, 0.0);
352 assert_eq!(p.y, 0.0);
353 }
354
355 #[test]
356 fn test_point_distance() {
357 let p1 = Point::new(0.0, 0.0);
358 let p2 = Point::new(3.0, 4.0);
359 assert!((p1.distance(&p2) - 5.0).abs() < 0.001);
360 }
361
362 #[test]
363 fn test_point_add() {
364 let p1 = Point::new(1.0, 2.0);
365 let p2 = Point::new(3.0, 4.0);
366 let sum = p1 + p2;
367 assert_eq!(sum.x, 4.0);
368 assert_eq!(sum.y, 6.0);
369 }
370
371 #[test]
372 fn test_point_sub() {
373 let p1 = Point::new(5.0, 7.0);
374 let p2 = Point::new(2.0, 3.0);
375 let diff = p1 - p2;
376 assert_eq!(diff.x, 3.0);
377 assert_eq!(diff.y, 4.0);
378 }
379
380 #[test]
381 fn test_size_new() {
382 let s = Size::new(100.0, 200.0);
383 assert_eq!(s.width, 100.0);
384 assert_eq!(s.height, 200.0);
385 }
386
387 #[test]
388 fn test_size_zero() {
389 let s = Size::ZERO;
390 assert_eq!(s.width, 0.0);
391 assert_eq!(s.height, 0.0);
392 }
393
394 #[test]
395 fn test_size_area() {
396 let s = Size::new(10.0, 20.0);
397 assert_eq!(s.area(), 200.0);
398 }
399
400 #[test]
401 fn test_size_aspect_ratio() {
402 let s = Size::new(16.0, 9.0);
403 assert!((s.aspect_ratio() - 16.0 / 9.0).abs() < 0.001);
404 }
405
406 #[test]
407 fn test_size_contains() {
408 let s = Size::new(100.0, 100.0);
409 let smaller = Size::new(50.0, 50.0);
410 let larger = Size::new(150.0, 50.0);
411 assert!(s.contains(&smaller));
412 assert!(!s.contains(&larger));
413 }
414
415 #[test]
416 fn test_rect_new() {
417 let r = Rect::new(10.0, 20.0, 100.0, 200.0);
418 assert_eq!(r.x, 10.0);
419 assert_eq!(r.y, 20.0);
420 assert_eq!(r.width, 100.0);
421 assert_eq!(r.height, 200.0);
422 }
423
424 #[test]
425 fn test_rect_from_points() {
426 let r = Rect::from_points(Point::new(10.0, 20.0), Point::new(110.0, 220.0));
427 assert_eq!(r.x, 10.0);
428 assert_eq!(r.y, 20.0);
429 assert_eq!(r.width, 100.0);
430 assert_eq!(r.height, 200.0);
431 }
432
433 #[test]
434 fn test_rect_from_size() {
435 let r = Rect::from_size(Size::new(100.0, 200.0));
436 assert_eq!(r.x, 0.0);
437 assert_eq!(r.y, 0.0);
438 assert_eq!(r.width, 100.0);
439 assert_eq!(r.height, 200.0);
440 }
441
442 #[test]
443 fn test_rect_origin_and_size() {
444 let r = Rect::new(10.0, 20.0, 100.0, 200.0);
445 assert_eq!(r.origin(), Point::new(10.0, 20.0));
446 assert_eq!(r.size(), Size::new(100.0, 200.0));
447 }
448
449 #[test]
450 fn test_rect_corners() {
451 let r = Rect::new(10.0, 20.0, 100.0, 200.0);
452 assert_eq!(r.top_left(), Point::new(10.0, 20.0));
453 assert_eq!(r.top_right(), Point::new(110.0, 20.0));
454 assert_eq!(r.bottom_left(), Point::new(10.0, 220.0));
455 assert_eq!(r.bottom_right(), Point::new(110.0, 220.0));
456 }
457
458 #[test]
459 fn test_rect_center() {
460 let r = Rect::new(0.0, 0.0, 100.0, 100.0);
461 assert_eq!(r.center(), Point::new(50.0, 50.0));
462 }
463
464 #[test]
465 fn test_rect_contains_point() {
466 let r = Rect::new(10.0, 10.0, 100.0, 100.0);
467 assert!(r.contains_point(&Point::new(50.0, 50.0)));
468 assert!(r.contains_point(&Point::new(10.0, 10.0))); assert!(!r.contains_point(&Point::new(5.0, 50.0)));
470 assert!(!r.contains_point(&Point::new(111.0, 50.0)));
471 }
472
473 #[test]
474 fn test_rect_intersects() {
475 let r1 = Rect::new(0.0, 0.0, 100.0, 100.0);
476 let r2 = Rect::new(50.0, 50.0, 100.0, 100.0);
477 let r3 = Rect::new(200.0, 200.0, 100.0, 100.0);
478
479 assert!(r1.intersects(&r2));
480 assert!(!r1.intersects(&r3));
481 }
482
483 #[test]
484 fn test_rect_intersection() {
485 let r1 = Rect::new(0.0, 0.0, 100.0, 100.0);
486 let r2 = Rect::new(50.0, 50.0, 100.0, 100.0);
487
488 let inter = r1.intersection(&r2).unwrap();
489 assert_eq!(inter.x, 50.0);
490 assert_eq!(inter.y, 50.0);
491 assert_eq!(inter.width, 50.0);
492 assert_eq!(inter.height, 50.0);
493 }
494
495 #[test]
496 fn test_rect_union() {
497 let r1 = Rect::new(0.0, 0.0, 50.0, 50.0);
498 let r2 = Rect::new(25.0, 25.0, 50.0, 50.0);
499
500 let union = r1.union(&r2);
501 assert_eq!(union.x, 0.0);
502 assert_eq!(union.y, 0.0);
503 assert_eq!(union.width, 75.0);
504 assert_eq!(union.height, 75.0);
505 }
506
507 #[test]
508 fn test_rect_inset() {
509 let r = Rect::new(10.0, 10.0, 100.0, 100.0);
510 let inset = r.inset(5.0);
511 assert_eq!(inset.x, 15.0);
512 assert_eq!(inset.y, 15.0);
513 assert_eq!(inset.width, 90.0);
514 assert_eq!(inset.height, 90.0);
515 }
516
517 #[test]
518 fn test_corner_radius() {
519 let uniform = CornerRadius::uniform(10.0);
520 assert_eq!(uniform.top_left, 10.0);
521 assert_eq!(uniform.top_right, 10.0);
522 assert_eq!(uniform.bottom_left, 10.0);
523 assert_eq!(uniform.bottom_right, 10.0);
524
525 let custom = CornerRadius::new(1.0, 2.0, 3.0, 4.0);
526 assert_eq!(custom.top_left, 1.0);
527 assert_eq!(custom.top_right, 2.0);
528 assert_eq!(custom.bottom_right, 3.0);
529 assert_eq!(custom.bottom_left, 4.0);
530 }
531
532 proptest! {
533 #[test]
534 fn prop_point_distance_non_negative(x1 in -1000.0f32..1000.0, y1 in -1000.0f32..1000.0, x2 in -1000.0f32..1000.0, y2 in -1000.0f32..1000.0) {
535 let p1 = Point::new(x1, y1);
536 let p2 = Point::new(x2, y2);
537 prop_assert!(p1.distance(&p2) >= 0.0);
538 }
539
540 #[test]
541 fn prop_point_distance_symmetric(x1 in -1000.0f32..1000.0, y1 in -1000.0f32..1000.0, x2 in -1000.0f32..1000.0, y2 in -1000.0f32..1000.0) {
542 let p1 = Point::new(x1, y1);
543 let p2 = Point::new(x2, y2);
544 prop_assert!((p1.distance(&p2) - p2.distance(&p1)).abs() < 0.001);
545 }
546
547 #[test]
548 fn prop_rect_area_non_negative(x in -1000.0f32..1000.0, y in -1000.0f32..1000.0, w in 0.0f32..1000.0, h in 0.0f32..1000.0) {
549 let r = Rect::new(x, y, w, h);
550 prop_assert!(r.area() >= 0.0);
551 }
552
553 #[test]
554 fn prop_rect_contains_center(x in -1000.0f32..1000.0, y in -1000.0f32..1000.0, w in 1.0f32..1000.0, h in 1.0f32..1000.0) {
555 let r = Rect::new(x, y, w, h);
556 prop_assert!(r.contains_point(&r.center()));
557 }
558
559 #[test]
560 fn prop_rect_intersects_self(x in -1000.0f32..1000.0, y in -1000.0f32..1000.0, w in 0.1f32..1000.0, h in 0.1f32..1000.0) {
561 let r = Rect::new(x, y, w, h);
562 prop_assert!(r.intersects(&r));
563 }
564 }
565 }
566
567 mod constraints_tests {
572 use super::*;
573
574 #[test]
575 fn test_constraints_tight() {
576 let c = Constraints::tight(Size::new(100.0, 200.0));
577 assert_eq!(c.min_width, 100.0);
578 assert_eq!(c.max_width, 100.0);
579 assert_eq!(c.min_height, 200.0);
580 assert_eq!(c.max_height, 200.0);
581 }
582
583 #[test]
584 fn test_constraints_loose() {
585 let c = Constraints::loose(Size::new(100.0, 200.0));
586 assert_eq!(c.min_width, 0.0);
587 assert_eq!(c.max_width, 100.0);
588 assert_eq!(c.min_height, 0.0);
589 assert_eq!(c.max_height, 200.0);
590 }
591
592 #[test]
593 fn test_constraints_unbounded() {
594 let c = Constraints::unbounded();
595 assert_eq!(c.min_width, 0.0);
596 assert_eq!(c.max_width, f32::INFINITY);
597 assert_eq!(c.min_height, 0.0);
598 assert_eq!(c.max_height, f32::INFINITY);
599 }
600
601 #[test]
602 fn test_constraints_constrain() {
603 let c = Constraints::new(50.0, 150.0, 50.0, 150.0);
604
605 assert_eq!(
607 c.constrain(Size::new(100.0, 100.0)),
608 Size::new(100.0, 100.0)
609 );
610
611 assert_eq!(c.constrain(Size::new(10.0, 10.0)), Size::new(50.0, 50.0));
613
614 assert_eq!(
616 c.constrain(Size::new(200.0, 200.0)),
617 Size::new(150.0, 150.0)
618 );
619 }
620
621 #[test]
622 fn test_constraints_is_tight() {
623 let tight = Constraints::tight(Size::new(100.0, 100.0));
624 let loose = Constraints::loose(Size::new(100.0, 100.0));
625
626 assert!(tight.is_tight());
627 assert!(!loose.is_tight());
628 }
629
630 #[test]
631 fn test_constraints_has_bounded_width() {
632 let bounded = Constraints::new(0.0, 100.0, 0.0, f32::INFINITY);
633 let unbounded = Constraints::unbounded();
634
635 assert!(bounded.has_bounded_width());
636 assert!(!unbounded.has_bounded_width());
637 }
638 }
639
640 mod event_tests {
645 use super::*;
646
647 #[test]
648 fn test_event_mouse_move() {
649 let e = Event::MouseMove {
650 position: Point::new(100.0, 200.0),
651 };
652 if let Event::MouseMove { position } = e {
653 assert_eq!(position.x, 100.0);
654 assert_eq!(position.y, 200.0);
655 } else {
656 panic!("Expected MouseMove event");
657 }
658 }
659
660 #[test]
661 fn test_event_mouse_button() {
662 let e = Event::MouseDown {
663 position: Point::new(50.0, 50.0),
664 button: MouseButton::Left,
665 };
666 if let Event::MouseDown { button, .. } = e {
667 assert_eq!(button, MouseButton::Left);
668 } else {
669 panic!("Expected MouseDown event");
670 }
671 }
672
673 #[test]
674 fn test_event_key() {
675 let e = Event::KeyDown { key: Key::Enter };
676 if let Event::KeyDown { key } = e {
677 assert_eq!(key, Key::Enter);
678 } else {
679 panic!("Expected KeyDown event");
680 }
681 }
682
683 #[test]
684 fn test_event_scroll() {
685 let e = Event::Scroll {
686 delta_x: 0.0,
687 delta_y: -10.0,
688 };
689 if let Event::Scroll { delta_y, .. } = e {
690 assert_eq!(delta_y, -10.0);
691 } else {
692 panic!("Expected Scroll event");
693 }
694 }
695
696 #[test]
697 fn test_event_text_input() {
698 let e = Event::TextInput {
699 text: "hello".to_string(),
700 };
701 if let Event::TextInput { text } = e {
702 assert_eq!(text, "hello");
703 } else {
704 panic!("Expected TextInput event");
705 }
706 }
707 }
708}