1use serde::{Deserialize, Serialize};
6use std::sync::{Arc, Mutex};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub enum DialogType {
11 Alert,
13 Confirm,
15 Prompt,
17 BeforeUnload,
19}
20
21impl std::fmt::Display for DialogType {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 Self::Alert => write!(f, "alert"),
25 Self::Confirm => write!(f, "confirm"),
26 Self::Prompt => write!(f, "prompt"),
27 Self::BeforeUnload => write!(f, "beforeunload"),
28 }
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub enum DialogAction {
35 Accept,
37 AcceptWith(String),
39 Dismiss,
41 Pending,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct Dialog {
48 dialog_type: DialogType,
50 message: String,
52 default_value: Option<String>,
54 action: DialogAction,
56}
57
58impl Dialog {
59 #[must_use]
61 pub fn new(dialog_type: DialogType, message: impl Into<String>) -> Self {
62 Self {
63 dialog_type,
64 message: message.into(),
65 default_value: None,
66 action: DialogAction::Pending,
67 }
68 }
69
70 #[must_use]
72 pub fn alert(message: impl Into<String>) -> Self {
73 Self::new(DialogType::Alert, message)
74 }
75
76 #[must_use]
78 pub fn confirm(message: impl Into<String>) -> Self {
79 Self::new(DialogType::Confirm, message)
80 }
81
82 #[must_use]
84 pub fn prompt(message: impl Into<String>, default: Option<String>) -> Self {
85 let mut dialog = Self::new(DialogType::Prompt, message);
86 dialog.default_value = default;
87 dialog
88 }
89
90 #[must_use]
92 pub fn before_unload(message: impl Into<String>) -> Self {
93 Self::new(DialogType::BeforeUnload, message)
94 }
95
96 #[must_use]
98 pub fn dialog_type(&self) -> DialogType {
99 self.dialog_type
100 }
101
102 #[must_use]
104 pub fn message(&self) -> &str {
105 &self.message
106 }
107
108 #[must_use]
110 pub fn default_value(&self) -> Option<&str> {
111 self.default_value.as_deref()
112 }
113
114 #[must_use]
116 pub fn action(&self) -> &DialogAction {
117 &self.action
118 }
119
120 #[must_use]
122 pub fn is_handled(&self) -> bool {
123 !matches!(self.action, DialogAction::Pending)
124 }
125
126 pub fn accept(&mut self) {
128 self.action = DialogAction::Accept;
129 }
130
131 pub fn accept_with(&mut self, text: impl Into<String>) {
133 self.action = DialogAction::AcceptWith(text.into());
134 }
135
136 pub fn dismiss(&mut self) {
138 self.action = DialogAction::Dismiss;
139 }
140}
141
142pub type DialogHandlerFn = Box<dyn Fn(&mut Dialog) + Send + Sync>;
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147pub enum AutoDialogBehavior {
148 AcceptAll,
150 DismissAll,
152 AcceptEmpty,
154 UseDefault,
156 Manual,
158}
159
160impl Default for AutoDialogBehavior {
161 fn default() -> Self {
162 Self::Manual
163 }
164}
165
166#[derive(Clone)]
168pub struct DialogHandler {
169 dialogs: Arc<Mutex<Vec<Dialog>>>,
171 handler: Arc<Mutex<Option<DialogHandlerFn>>>,
173 auto_behavior: Arc<Mutex<AutoDialogBehavior>>,
175}
176
177impl DialogHandler {
178 #[must_use]
180 pub fn new() -> Self {
181 Self {
182 dialogs: Arc::new(Mutex::new(Vec::new())),
183 handler: Arc::new(Mutex::new(None)),
184 auto_behavior: Arc::new(Mutex::new(AutoDialogBehavior::default())),
185 }
186 }
187
188 pub fn on_dialog<F>(&self, handler: F)
190 where
191 F: Fn(&mut Dialog) + Send + Sync + 'static,
192 {
193 if let Ok(mut h) = self.handler.lock() {
194 *h = Some(Box::new(handler));
195 }
196 }
197
198 pub fn set_auto_behavior(&self, behavior: AutoDialogBehavior) {
200 if let Ok(mut b) = self.auto_behavior.lock() {
201 *b = behavior;
202 }
203 }
204
205 pub fn handle(&self, mut dialog: Dialog) -> Dialog {
207 if let Ok(handler) = self.handler.lock() {
209 if let Some(ref h) = *handler {
210 h(&mut dialog);
211 if dialog.is_handled() {
212 if let Ok(mut dialogs) = self.dialogs.lock() {
213 dialogs.push(dialog.clone());
214 }
215 return dialog;
216 }
217 }
218 }
219
220 let behavior = self.auto_behavior.lock().map(|b| *b).unwrap_or_default();
222 match behavior {
223 AutoDialogBehavior::AcceptAll => dialog.accept(),
224 AutoDialogBehavior::DismissAll => dialog.dismiss(),
225 AutoDialogBehavior::AcceptEmpty => dialog.accept_with(""),
226 AutoDialogBehavior::UseDefault => {
227 if let Some(default) = dialog.default_value.clone() {
228 dialog.accept_with(default);
229 } else {
230 dialog.accept();
231 }
232 }
233 AutoDialogBehavior::Manual => {
234 }
236 }
237
238 if let Ok(mut dialogs) = self.dialogs.lock() {
239 dialogs.push(dialog.clone());
240 }
241 dialog
242 }
243
244 #[must_use]
246 pub fn dialogs(&self) -> Vec<Dialog> {
247 self.dialogs.lock().map(|d| d.clone()).unwrap_or_default()
248 }
249
250 #[must_use]
252 pub fn dialog_count(&self) -> usize {
253 self.dialogs.lock().map(|d| d.len()).unwrap_or(0)
254 }
255
256 pub fn clear(&self) {
258 if let Ok(mut d) = self.dialogs.lock() {
259 d.clear();
260 }
261 }
262
263 #[must_use]
265 pub fn has_pending(&self) -> bool {
266 self.dialogs
267 .lock()
268 .map(|d| d.iter().any(|dialog| !dialog.is_handled()))
269 .unwrap_or(false)
270 }
271
272 #[must_use]
274 pub fn last_dialog(&self) -> Option<Dialog> {
275 self.dialogs.lock().ok().and_then(|d| d.last().cloned())
276 }
277
278 #[must_use]
280 pub fn expect_dialog(&self, dialog_type: DialogType) -> DialogExpectation {
281 DialogExpectation {
282 expected_type: dialog_type,
283 handler: self.clone(),
284 }
285 }
286}
287
288impl Default for DialogHandler {
289 fn default() -> Self {
290 Self::new()
291 }
292}
293
294impl std::fmt::Debug for DialogHandler {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 let auto_behavior = self.auto_behavior.lock().map(|b| *b).unwrap_or_default();
297 f.debug_struct("DialogHandler")
298 .field("dialog_count", &self.dialog_count())
299 .field("auto_behavior", &auto_behavior)
300 .finish()
301 }
302}
303
304#[derive(Debug)]
306pub struct DialogExpectation {
307 expected_type: DialogType,
308 handler: DialogHandler,
309}
310
311impl DialogExpectation {
312 #[must_use]
314 pub fn verify(&self) -> bool {
315 self.handler
316 .dialogs()
317 .iter()
318 .any(|d| d.dialog_type() == self.expected_type)
319 }
320
321 #[must_use]
323 pub fn dialog(&self) -> Option<Dialog> {
324 self.handler
325 .dialogs()
326 .into_iter()
327 .find(|d| d.dialog_type() == self.expected_type)
328 }
329}
330
331#[derive(Debug, Clone)]
333pub struct DialogHandlerBuilder {
334 auto_behavior: AutoDialogBehavior,
335}
336
337impl DialogHandlerBuilder {
338 #[must_use]
340 pub fn new() -> Self {
341 Self {
342 auto_behavior: AutoDialogBehavior::default(),
343 }
344 }
345
346 #[must_use]
348 pub fn accept_all(mut self) -> Self {
349 self.auto_behavior = AutoDialogBehavior::AcceptAll;
350 self
351 }
352
353 #[must_use]
355 pub fn dismiss_all(mut self) -> Self {
356 self.auto_behavior = AutoDialogBehavior::DismissAll;
357 self
358 }
359
360 #[must_use]
362 pub fn use_defaults(mut self) -> Self {
363 self.auto_behavior = AutoDialogBehavior::UseDefault;
364 self
365 }
366
367 #[must_use]
369 pub fn build(self) -> DialogHandler {
370 let handler = DialogHandler::new();
371 handler.set_auto_behavior(self.auto_behavior);
372 handler
373 }
374}
375
376impl Default for DialogHandlerBuilder {
377 fn default() -> Self {
378 Self::new()
379 }
380}
381
382#[cfg(test)]
383#[allow(clippy::unwrap_used, clippy::expect_used)]
384mod tests {
385 use super::*;
386
387 #[test]
392 fn h0_dialog_01_new() {
393 let dialog = Dialog::new(DialogType::Alert, "Hello");
394 assert_eq!(dialog.dialog_type(), DialogType::Alert);
395 assert_eq!(dialog.message(), "Hello");
396 assert!(!dialog.is_handled());
397 }
398
399 #[test]
400 fn h0_dialog_02_alert() {
401 let dialog = Dialog::alert("Test alert");
402 assert_eq!(dialog.dialog_type(), DialogType::Alert);
403 }
404
405 #[test]
406 fn h0_dialog_03_confirm() {
407 let dialog = Dialog::confirm("Are you sure?");
408 assert_eq!(dialog.dialog_type(), DialogType::Confirm);
409 }
410
411 #[test]
412 fn h0_dialog_04_prompt() {
413 let dialog = Dialog::prompt("Enter name:", Some("default".to_string()));
414 assert_eq!(dialog.dialog_type(), DialogType::Prompt);
415 assert_eq!(dialog.default_value(), Some("default"));
416 }
417
418 #[test]
419 fn h0_dialog_05_before_unload() {
420 let dialog = Dialog::before_unload("Leave page?");
421 assert_eq!(dialog.dialog_type(), DialogType::BeforeUnload);
422 }
423
424 #[test]
429 fn h0_dialog_06_accept() {
430 let mut dialog = Dialog::alert("Test");
431 dialog.accept();
432 assert!(dialog.is_handled());
433 assert_eq!(dialog.action(), &DialogAction::Accept);
434 }
435
436 #[test]
437 fn h0_dialog_07_accept_with() {
438 let mut dialog = Dialog::prompt("Name?", None);
439 dialog.accept_with("John");
440 assert!(dialog.is_handled());
441 assert_eq!(
442 dialog.action(),
443 &DialogAction::AcceptWith("John".to_string())
444 );
445 }
446
447 #[test]
448 fn h0_dialog_08_dismiss() {
449 let mut dialog = Dialog::confirm("Continue?");
450 dialog.dismiss();
451 assert!(dialog.is_handled());
452 assert_eq!(dialog.action(), &DialogAction::Dismiss);
453 }
454
455 #[test]
460 fn h0_dialog_09_type_display() {
461 assert_eq!(format!("{}", DialogType::Alert), "alert");
462 assert_eq!(format!("{}", DialogType::Confirm), "confirm");
463 assert_eq!(format!("{}", DialogType::Prompt), "prompt");
464 assert_eq!(format!("{}", DialogType::BeforeUnload), "beforeunload");
465 }
466
467 #[test]
472 fn h0_dialog_10_handler_new() {
473 let handler = DialogHandler::new();
474 assert_eq!(handler.dialog_count(), 0);
475 assert!(!handler.has_pending());
476 }
477
478 #[test]
479 fn h0_dialog_11_handler_default() {
480 let handler = DialogHandler::default();
481 assert_eq!(handler.dialog_count(), 0);
482 }
483
484 #[test]
489 fn h0_dialog_12_auto_accept_all() {
490 let handler = DialogHandler::new();
491 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
492
493 let dialog = handler.handle(Dialog::confirm("Test?"));
494 assert_eq!(dialog.action(), &DialogAction::Accept);
495 }
496
497 #[test]
498 fn h0_dialog_13_auto_dismiss_all() {
499 let handler = DialogHandler::new();
500 handler.set_auto_behavior(AutoDialogBehavior::DismissAll);
501
502 let dialog = handler.handle(Dialog::confirm("Test?"));
503 assert_eq!(dialog.action(), &DialogAction::Dismiss);
504 }
505
506 #[test]
507 fn h0_dialog_14_auto_accept_empty() {
508 let handler = DialogHandler::new();
509 handler.set_auto_behavior(AutoDialogBehavior::AcceptEmpty);
510
511 let dialog = handler.handle(Dialog::prompt("Name?", None));
512 assert_eq!(dialog.action(), &DialogAction::AcceptWith(String::new()));
513 }
514
515 #[test]
516 fn h0_dialog_15_auto_use_default() {
517 let handler = DialogHandler::new();
518 handler.set_auto_behavior(AutoDialogBehavior::UseDefault);
519
520 let dialog = handler.handle(Dialog::prompt("Name?", Some("John".to_string())));
521 assert_eq!(
522 dialog.action(),
523 &DialogAction::AcceptWith("John".to_string())
524 );
525 }
526
527 #[test]
528 fn h0_dialog_16_auto_manual() {
529 let handler = DialogHandler::new();
530 handler.set_auto_behavior(AutoDialogBehavior::Manual);
531
532 let dialog = handler.handle(Dialog::alert("Test"));
533 assert_eq!(dialog.action(), &DialogAction::Pending);
534 }
535
536 #[test]
541 fn h0_dialog_17_custom_handler() {
542 let handler = DialogHandler::new();
543 handler.on_dialog(|dialog| {
544 if dialog.dialog_type() == DialogType::Confirm {
545 dialog.accept();
546 } else {
547 dialog.dismiss();
548 }
549 });
550
551 let confirm = handler.handle(Dialog::confirm("Continue?"));
552 assert_eq!(confirm.action(), &DialogAction::Accept);
553
554 let alert = handler.handle(Dialog::alert("Info"));
555 assert_eq!(alert.action(), &DialogAction::Dismiss);
556 }
557
558 #[test]
563 fn h0_dialog_18_dialogs() {
564 let handler = DialogHandler::new();
565 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
566
567 handler.handle(Dialog::alert("First"));
568 handler.handle(Dialog::alert("Second"));
569
570 let dialogs = handler.dialogs();
571 assert_eq!(dialogs.len(), 2);
572 assert_eq!(dialogs[0].message(), "First");
573 assert_eq!(dialogs[1].message(), "Second");
574 }
575
576 #[test]
577 fn h0_dialog_19_last_dialog() {
578 let handler = DialogHandler::new();
579 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
580
581 handler.handle(Dialog::alert("First"));
582 handler.handle(Dialog::alert("Last"));
583
584 let last = handler.last_dialog().unwrap();
585 assert_eq!(last.message(), "Last");
586 }
587
588 #[test]
589 fn h0_dialog_20_clear() {
590 let handler = DialogHandler::new();
591 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
592 handler.handle(Dialog::alert("Test"));
593
594 handler.clear();
595
596 assert_eq!(handler.dialog_count(), 0);
597 }
598
599 #[test]
604 fn h0_dialog_21_has_pending_false() {
605 let handler = DialogHandler::new();
606 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
607 handler.handle(Dialog::alert("Test"));
608
609 assert!(!handler.has_pending());
610 }
611
612 #[test]
613 fn h0_dialog_22_has_pending_true() {
614 let handler = DialogHandler::new();
615 handler.set_auto_behavior(AutoDialogBehavior::Manual);
616 handler.handle(Dialog::alert("Test"));
617
618 assert!(handler.has_pending());
619 }
620
621 #[test]
626 fn h0_dialog_23_expect_dialog_verify() {
627 let handler = DialogHandler::new();
628 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
629 handler.handle(Dialog::confirm("Sure?"));
630
631 let expectation = handler.expect_dialog(DialogType::Confirm);
632 assert!(expectation.verify());
633 }
634
635 #[test]
636 fn h0_dialog_24_expect_dialog_not_found() {
637 let handler = DialogHandler::new();
638 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
639 handler.handle(Dialog::alert("Info"));
640
641 let expectation = handler.expect_dialog(DialogType::Confirm);
642 assert!(!expectation.verify());
643 }
644
645 #[test]
646 fn h0_dialog_25_expect_dialog_get() {
647 let handler = DialogHandler::new();
648 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
649 handler.handle(Dialog::prompt("Name?", None));
650
651 let expectation = handler.expect_dialog(DialogType::Prompt);
652 let dialog = expectation.dialog().unwrap();
653 assert_eq!(dialog.message(), "Name?");
654 }
655
656 #[test]
661 fn h0_dialog_26_builder_accept_all() {
662 let handler = DialogHandlerBuilder::new().accept_all().build();
663
664 let dialog = handler.handle(Dialog::alert("Test"));
665 assert_eq!(dialog.action(), &DialogAction::Accept);
666 }
667
668 #[test]
669 fn h0_dialog_27_builder_dismiss_all() {
670 let handler = DialogHandlerBuilder::new().dismiss_all().build();
671
672 let dialog = handler.handle(Dialog::confirm("Sure?"));
673 assert_eq!(dialog.action(), &DialogAction::Dismiss);
674 }
675
676 #[test]
677 fn h0_dialog_28_builder_use_defaults() {
678 let handler = DialogHandlerBuilder::new().use_defaults().build();
679
680 let dialog = handler.handle(Dialog::prompt("Name?", Some("Bob".to_string())));
681 assert_eq!(
682 dialog.action(),
683 &DialogAction::AcceptWith("Bob".to_string())
684 );
685 }
686
687 #[test]
692 fn h0_dialog_29_handler_debug() {
693 let handler = DialogHandler::new();
694 let debug = format!("{handler:?}");
695 assert!(debug.contains("DialogHandler"));
696 assert!(debug.contains("dialog_count"));
697 }
698
699 #[test]
704 fn h0_dialog_30_dialog_clone() {
705 let dialog = Dialog::alert("Test");
706 let cloned = dialog;
707 assert_eq!(cloned.message(), "Test");
708 }
709
710 #[test]
711 fn h0_dialog_31_handler_clone() {
712 let handler = DialogHandler::new();
713 handler.set_auto_behavior(AutoDialogBehavior::AcceptAll);
714 handler.handle(Dialog::alert("Test"));
715
716 let cloned = handler;
717 assert_eq!(cloned.dialog_count(), 1);
718 }
719}