Skip to main content

yew_nav_link/hooks/navigation/
use_navigation.rs

1use std::marker::PhantomData;
2
3use yew::prelude::*;
4use yew_router::{
5    history::{BrowserHistory, History},
6    prelude::*
7};
8
9/// Navigation callbacks for programmatic route manipulation.
10///
11/// Provides pre-built callbacks for:
12/// - Pushing new routes onto history
13/// - Replacing current route
14/// - Navigating back/forward
15///
16/// This struct is created by [`use_navigation`] and contains all the
17/// callbacks needed for navigation without storing state.
18#[derive(Clone, Debug)]
19pub struct Navigation<R>
20where
21    R: Routable + Clone + 'static
22{
23    /// Callback to navigate back in history.
24    pub go_back:    Callback<()>,
25    /// Callback to navigate forward in history.
26    pub go_forward: Callback<()>,
27    /// Phantom marker for the route type.
28    pub _marker:    PhantomData<R>
29}
30
31impl<R> Navigation<R>
32where
33    R: Routable + Clone + 'static
34{
35    /// Create a callback for pushing a route onto history.
36    pub fn push_callback(&self, route: R) -> Callback<()> {
37        Callback::from(move |()| {
38            let path = route.to_path();
39            BrowserHistory::new().push(&path);
40        })
41    }
42
43    /// Create a callback for replacing the current route.
44    pub fn replace_callback(&self, route: R) -> Callback<()> {
45        Callback::from(move |()| {
46            let path = route.to_path();
47            BrowserHistory::new().replace(&path);
48        })
49    }
50
51    #[must_use]
52    /// Create a callback for navigating with a delta.
53    pub fn go_callback(&self, delta: isize) -> Callback<()> {
54        Callback::from(move |()| {
55            BrowserHistory::new().go(delta);
56        })
57    }
58}
59
60/// Returns a [`Navigation`] handle for programmatic navigation.
61///
62/// ```rust,ignore
63/// use yew::prelude::*;
64/// use yew_nav_link::hooks::use_navigation;
65/// use yew_router::prelude::*;
66///
67/// #[derive(Clone, PartialEq, Debug, Routable)]
68/// enum Route {
69///     #[at("/")]
70///     Home
71/// }
72///
73/// #[component]
74/// fn MyComponent() -> Html {
75///     let navigation = use_navigation::<Route>();
76///
77///     html! {
78///         <div>
79///             <button onclick={navigation.go_back.clone()}>Back</button>
80///             <button onclick={navigation.go_forward.clone()}>Forward</button>
81///             <button onclick={navigation.push_callback(Route::Home)}>
82///                 { "Go Home" }
83///             </button>
84///         </div>
85///     }
86/// }
87/// ```
88#[hook]
89pub fn use_navigation<R>() -> Navigation<R>
90where
91    R: Routable + Clone + 'static
92{
93    let go_back = Callback::from(|()| {
94        BrowserHistory::new().back();
95    });
96
97    let go_forward = Callback::from(|()| {
98        BrowserHistory::new().forward();
99    });
100
101    Navigation {
102        go_back,
103        go_forward,
104        _marker: PhantomData
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use std::marker::PhantomData;
111
112    use super::*;
113
114    #[test]
115    fn navigation_struct_creation() {
116        #[derive(Clone, PartialEq, Debug, Routable)]
117        enum TestRoute {
118            #[at("/test")]
119            Test,
120            #[at("/")]
121            Home
122        }
123
124        let nav = Navigation::<TestRoute> {
125            go_back:    Callback::from(|()| {}),
126            go_forward: Callback::from(|()| {}),
127            _marker:    PhantomData
128        };
129
130        let _ = nav.go_back;
131        let _ = nav.go_forward;
132    }
133
134    #[test]
135    fn navigation_clone() {
136        #[derive(Clone, PartialEq, Debug, Routable)]
137        enum TestRoute {
138            #[at("/test")]
139            Test
140        }
141
142        let nav1 = Navigation::<TestRoute> {
143            go_back:    Callback::from(|()| {}),
144            go_forward: Callback::from(|()| {}),
145            _marker:    PhantomData
146        };
147
148        let nav2 = nav1;
149        let _ = nav2.go_back;
150        let _ = nav2.go_forward;
151    }
152
153    #[test]
154    fn navigation_debug() {
155        #[derive(Clone, PartialEq, Debug, Routable)]
156        enum TestRoute {
157            #[at("/test")]
158            Test
159        }
160
161        let nav = Navigation::<TestRoute> {
162            go_back:    Callback::from(|()| {}),
163            go_forward: Callback::from(|()| {}),
164            _marker:    PhantomData
165        };
166
167        let debug_str = format!("{nav:?}");
168        assert!(debug_str.contains("Navigation"));
169    }
170
171    #[test]
172    fn navigation_partial_eq() {
173        #[derive(Clone, PartialEq, Debug, Routable)]
174        enum TestRoute {
175            #[at("/test")]
176            Test
177        }
178
179        let nav1 = Navigation::<TestRoute> {
180            go_back:    Callback::from(|()| {}),
181            go_forward: Callback::from(|()| {}),
182            _marker:    PhantomData
183        };
184        let nav2 = nav1;
185        let _ = nav1.go_back;
186        let _ = nav2.go_back;
187    }
188
189    #[test]
190    fn navigation_push_callback() {
191        #[derive(Clone, PartialEq, Debug, Routable)]
192        enum TestRoute {
193            #[at("/")]
194            Home
195        }
196
197        let nav = Navigation::<TestRoute> {
198            go_back:    Callback::from(|()| {}),
199            go_forward: Callback::from(|()| {}),
200            _marker:    PhantomData
201        };
202        let _ = nav.push_callback(TestRoute::Home);
203    }
204
205    #[test]
206    fn navigation_replace_callback() {
207        #[derive(Clone, PartialEq, Debug, Routable)]
208        enum TestRoute {
209            #[at("/")]
210            Home
211        }
212
213        let nav = Navigation::<TestRoute> {
214            go_back:    Callback::from(|()| {}),
215            go_forward: Callback::from(|()| {}),
216            _marker:    PhantomData
217        };
218        let _ = nav.replace_callback(TestRoute::Home);
219    }
220
221    #[test]
222    fn navigation_go_callback() {
223        #[derive(Clone, PartialEq, Debug, Routable)]
224        enum TestRoute {
225            #[at("/")]
226            Home
227        }
228
229        let nav = Navigation::<TestRoute> {
230            go_back:    Callback::from(|()| {}),
231            go_forward: Callback::from(|()| {}),
232            _marker:    PhantomData
233        };
234        let _ = nav.go_callback(-1);
235    }
236
237    #[test]
238    fn navigation_go_callback_with_positive_delta() {
239        #[derive(Clone, PartialEq, Debug, Routable)]
240        enum TestRoute {
241            #[at("/")]
242            Home
243        }
244
245        let nav = Navigation::<TestRoute> {
246            go_back:    Callback::from(|()| {}),
247            go_forward: Callback::from(|()| {}),
248            _marker:    PhantomData
249        };
250        let callback = nav.go_callback(1);
251        let _ = callback;
252    }
253
254    #[test]
255    fn navigation_go_callback_with_zero_delta() {
256        #[derive(Clone, PartialEq, Debug, Routable)]
257        enum TestRoute {
258            #[at("/")]
259            Home
260        }
261
262        let nav = Navigation::<TestRoute> {
263            go_back:    Callback::from(|()| {}),
264            go_forward: Callback::from(|()| {}),
265            _marker:    PhantomData
266        };
267        let callback = nav.go_callback(0);
268        let _ = callback;
269    }
270
271    #[test]
272    fn navigation_go_callback_with_large_negative_delta() {
273        #[derive(Clone, PartialEq, Debug, Routable)]
274        enum TestRoute {
275            #[at("/")]
276            Home
277        }
278
279        let nav = Navigation::<TestRoute> {
280            go_back:    Callback::from(|()| {}),
281            go_forward: Callback::from(|()| {}),
282            _marker:    PhantomData
283        };
284        let callback = nav.go_callback(-10);
285        let _ = callback;
286    }
287
288    #[test]
289    fn navigation_go_callback_with_large_positive_delta() {
290        #[derive(Clone, PartialEq, Debug, Routable)]
291        enum TestRoute {
292            #[at("/")]
293            Home
294        }
295
296        let nav = Navigation::<TestRoute> {
297            go_back:    Callback::from(|()| {}),
298            go_forward: Callback::from(|()| {}),
299            _marker:    PhantomData
300        };
301        let callback = nav.go_callback(10);
302        let _ = callback;
303    }
304
305    #[test]
306    fn navigation_push_callback_with_struct_route() {
307        #[derive(Clone, PartialEq, Debug, Routable)]
308        enum TestRoute {
309            #[at("/users/:id")]
310            User { id: String }
311        }
312
313        let nav = Navigation::<TestRoute> {
314            go_back:    Callback::from(|()| {}),
315            go_forward: Callback::from(|()| {}),
316            _marker:    PhantomData
317        };
318
319        let route = TestRoute::User {
320            id: "123".to_string()
321        };
322        let callback = nav.push_callback(route);
323        let _ = callback;
324    }
325
326    #[test]
327    fn navigation_push_callback_with_tuple_route() {
328        #[derive(Clone, PartialEq, Debug, Routable)]
329        enum TestRoute {
330            #[at("/posts/:year")]
331            Post { year: u32 }
332        }
333
334        let nav = Navigation::<TestRoute> {
335            go_back:    Callback::from(|()| {}),
336            go_forward: Callback::from(|()| {}),
337            _marker:    PhantomData
338        };
339
340        let route = TestRoute::Post {
341            year: 2024
342        };
343        let callback = nav.push_callback(route);
344        let _ = callback;
345    }
346
347    #[test]
348    fn navigation_replace_callback_with_struct_route() {
349        #[derive(Clone, PartialEq, Debug, Routable)]
350        enum TestRoute {
351            #[at("/settings/:section")]
352            Settings { section: String }
353        }
354
355        let nav = Navigation::<TestRoute> {
356            go_back:    Callback::from(|()| {}),
357            go_forward: Callback::from(|()| {}),
358            _marker:    PhantomData
359        };
360
361        let route = TestRoute::Settings {
362            section: "profile".to_string()
363        };
364        let callback = nav.replace_callback(route);
365        let _ = callback;
366    }
367
368    #[test]
369    fn navigation_replace_callback_with_tuple_route() {
370        #[derive(Clone, PartialEq, Debug, Routable)]
371        enum TestRoute {
372            #[at("/api/:version")]
373            Api { version: u32 }
374        }
375
376        let nav = Navigation::<TestRoute> {
377            go_back:    Callback::from(|()| {}),
378            go_forward: Callback::from(|()| {}),
379            _marker:    PhantomData
380        };
381
382        let route = TestRoute::Api {
383            version: 1
384        };
385        let callback = nav.replace_callback(route);
386        let _ = callback;
387    }
388
389    #[test]
390    fn navigation_callbacks_are_distinct() {
391        #[derive(Clone, PartialEq, Debug, Routable)]
392        enum TestRoute {
393            #[at("/")]
394            Home,
395            #[at("/about")]
396            About
397        }
398
399        let nav = Navigation::<TestRoute> {
400            go_back:    Callback::from(|()| {}),
401            go_forward: Callback::from(|()| {}),
402            _marker:    PhantomData
403        };
404
405        let push_callback = nav.push_callback(TestRoute::Home);
406        let replace_callback = nav.replace_callback(TestRoute::About);
407        let go_callback = nav.go_callback(-1);
408
409        let _ = push_callback;
410        let _ = replace_callback;
411        let _ = go_callback;
412    }
413
414    #[test]
415    fn navigation_multiple_callbacks_same_route() {
416        #[derive(Clone, PartialEq, Debug, Routable)]
417        enum TestRoute {
418            #[at("/")]
419            Home
420        }
421
422        let nav = Navigation::<TestRoute> {
423            go_back:    Callback::from(|()| {}),
424            go_forward: Callback::from(|()| {}),
425            _marker:    PhantomData
426        };
427
428        let callback1 = nav.push_callback(TestRoute::Home);
429        let callback2 = nav.push_callback(TestRoute::Home);
430        let callback3 = nav.push_callback(TestRoute::Home);
431
432        let _ = callback1;
433        let _ = callback2;
434        let _ = callback3;
435    }
436
437    #[test]
438    fn navigation_callbacks_with_complex_route() {
439        #[derive(Clone, PartialEq, Debug, Routable)]
440        enum ComplexRoute {
441            #[at("/")]
442            Home,
443            #[at("/users")]
444            Users,
445            #[at("/admin")]
446            Admin,
447            #[at("/date")]
448            Date
449        }
450
451        let nav = Navigation::<ComplexRoute> {
452            go_back:    Callback::from(|()| {}),
453            go_forward: Callback::from(|()| {}),
454            _marker:    PhantomData
455        };
456
457        let _ = nav.push_callback(ComplexRoute::Home);
458        let _ = nav.push_callback(ComplexRoute::Users);
459        let _ = nav.push_callback(ComplexRoute::Admin);
460        let _ = nav.push_callback(ComplexRoute::Date);
461    }
462
463    #[test]
464    fn navigation_callbacks_preserve_route_data() {
465        #[derive(Clone, PartialEq, Debug, Routable)]
466        enum TestRoute {
467            #[at("/search/:query")]
468            Search { query: String }
469        }
470
471        let nav = Navigation::<TestRoute> {
472            go_back:    Callback::from(|()| {}),
473            go_forward: Callback::from(|()| {}),
474            _marker:    PhantomData
475        };
476
477        let query = "rust programming".to_string();
478        let route = TestRoute::Search {
479            query: query.clone()
480        };
481        let _callback = nav.push_callback(route);
482
483        assert_eq!(query, "rust programming");
484    }
485
486    #[test]
487    fn navigation_go_back_callback_exists() {
488        #[derive(Clone, PartialEq, Debug, Routable)]
489        enum TestRoute {
490            #[at("/")]
491            Home
492        }
493
494        let nav = Navigation::<TestRoute> {
495            go_back:    Callback::from(|()| {}),
496            go_forward: Callback::from(|()| {}),
497            _marker:    PhantomData
498        };
499
500        let _ = nav.go_back;
501    }
502
503    #[test]
504    fn navigation_go_forward_callback_exists() {
505        #[derive(Clone, PartialEq, Debug, Routable)]
506        enum TestRoute {
507            #[at("/")]
508            Home
509        }
510
511        let nav = Navigation::<TestRoute> {
512            go_back:    Callback::from(|()| {}),
513            go_forward: Callback::from(|()| {}),
514            _marker:    PhantomData
515        };
516
517        let _ = nav.go_forward;
518    }
519
520    #[test]
521    fn navigation_struct_with_multiple_variants() {
522        #[derive(Clone, PartialEq, Debug, Routable)]
523        enum MultiVariantRoute {
524            #[at("/")]
525            Home,
526            #[at("/about")]
527            About,
528            #[at("/contact")]
529            Contact,
530            #[at("/products")]
531            Products,
532            #[at("/services")]
533            Services,
534            #[at("/blog")]
535            Blog,
536            #[at("/faq")]
537            Faq
538        }
539
540        let nav = Navigation::<MultiVariantRoute> {
541            go_back:    Callback::from(|()| {}),
542            go_forward: Callback::from(|()| {}),
543            _marker:    PhantomData
544        };
545
546        let _ = nav.push_callback(MultiVariantRoute::Home);
547        let _ = nav.push_callback(MultiVariantRoute::About);
548        let _ = nav.push_callback(MultiVariantRoute::Contact);
549        let _ = nav.push_callback(MultiVariantRoute::Products);
550        let _ = nav.push_callback(MultiVariantRoute::Services);
551        let _ = nav.push_callback(MultiVariantRoute::Blog);
552        let _ = nav.push_callback(MultiVariantRoute::Faq);
553    }
554
555    #[test]
556    fn navigation_go_callback_edge_cases() {
557        #[derive(Clone, PartialEq, Debug, Routable)]
558        enum TestRoute {
559            #[at("/")]
560            Home
561        }
562
563        let nav = Navigation::<TestRoute> {
564            go_back:    Callback::from(|()| {}),
565            go_forward: Callback::from(|()| {}),
566            _marker:    PhantomData
567        };
568
569        let min_delta = nav.go_callback(isize::MIN);
570        let max_delta = nav.go_callback(isize::MAX);
571        let neg_one = nav.go_callback(-1);
572        let pos_one = nav.go_callback(1);
573
574        let _ = min_delta;
575        let _ = max_delta;
576        let _ = neg_one;
577        let _ = pos_one;
578    }
579
580    #[test]
581    fn navigation_callbacks_with_empty_string_route() {
582        #[derive(Clone, PartialEq, Debug, Routable)]
583        enum TestRoute {
584            #[at("/item/:name")]
585            Item { name: String }
586        }
587
588        let nav = Navigation::<TestRoute> {
589            go_back:    Callback::from(|()| {}),
590            go_forward: Callback::from(|()| {}),
591            _marker:    PhantomData
592        };
593
594        let route = TestRoute::Item {
595            name: String::new()
596        };
597        let callback = nav.push_callback(route);
598        let _ = callback;
599    }
600
601    #[test]
602    fn navigation_callbacks_with_special_characters() {
603        #[derive(Clone, PartialEq, Debug, Routable)]
604        enum TestRoute {
605            #[at("/tag/:name")]
606            Tag { name: String }
607        }
608
609        let nav = Navigation::<TestRoute> {
610            go_back:    Callback::from(|()| {}),
611            go_forward: Callback::from(|()| {}),
612            _marker:    PhantomData
613        };
614
615        let route = TestRoute::Tag {
616            name: "rust-lang".to_string()
617        };
618        let callback = nav.push_callback(route);
619        let _ = callback;
620    }
621
622    #[test]
623    fn navigation_callbacks_with_unicode() {
624        #[derive(Clone, PartialEq, Debug, Routable)]
625        enum TestRoute {
626            #[at("/emoji/:emoji")]
627            Emoji { emoji: String }
628        }
629
630        let nav = Navigation::<TestRoute> {
631            go_back:    Callback::from(|()| {}),
632            go_forward: Callback::from(|()| {}),
633            _marker:    PhantomData
634        };
635
636        let route = TestRoute::Emoji {
637            emoji: "🦀".to_string()
638        };
639        let callback = nav.push_callback(route);
640        let _ = callback;
641    }
642
643    #[test]
644    fn navigation_callbacks_with_numbers() {
645        #[derive(Clone, PartialEq, Debug, Routable)]
646        enum TestRoute {
647            #[at("/number/:num")]
648            Number { num: u64 }
649        }
650
651        let nav = Navigation::<TestRoute> {
652            go_back:    Callback::from(|()| {}),
653            go_forward: Callback::from(|()| {}),
654            _marker:    PhantomData
655        };
656
657        let route = TestRoute::Number {
658            num: 0
659        };
660        let callback = nav.push_callback(route);
661        let _ = callback;
662
663        let route = TestRoute::Number {
664            num: u64::MAX
665        };
666        let callback = nav.push_callback(route);
667        let _ = callback;
668    }
669
670    #[test]
671    fn navigation_struct_debug_format() {
672        #[derive(Clone, PartialEq, Debug, Routable)]
673        enum TestRoute {
674            #[at("/")]
675            Home
676        }
677
678        let nav = Navigation::<TestRoute> {
679            go_back:    Callback::from(|()| {}),
680            go_forward: Callback::from(|()| {}),
681            _marker:    PhantomData
682        };
683
684        let debug_str = format!("{nav:?}");
685        assert!(debug_str.contains("Navigation"));
686        assert!(debug_str.contains("go_back"));
687        assert!(debug_str.contains("go_forward"));
688    }
689
690    #[test]
691    fn navigation_callbacks_chainability() {
692        #[derive(Clone, PartialEq, Debug, Routable)]
693        enum TestRoute {
694            #[at("/")]
695            Home,
696            #[at("/step1")]
697            Step1,
698            #[at("/step2")]
699            Step2,
700            #[at("/step3")]
701            Step3
702        }
703
704        let nav = Navigation::<TestRoute> {
705            go_back:    Callback::from(|()| {}),
706            go_forward: Callback::from(|()| {}),
707            _marker:    PhantomData
708        };
709
710        let step1_callback = nav.push_callback(TestRoute::Step1);
711        let step2_callback = nav.push_callback(TestRoute::Step2);
712        let step3_callback = nav.push_callback(TestRoute::Step3);
713        let back_callback = nav.go_callback(-1);
714
715        let _ = step1_callback;
716        let _ = step2_callback;
717        let _ = step3_callback;
718        let _ = back_callback;
719    }
720
721    #[test]
722    fn navigation_replace_vs_push_same_route() {
723        #[derive(Clone, PartialEq, Debug, Routable)]
724        enum TestRoute {
725            #[at("/")]
726            Home
727        }
728
729        let nav = Navigation::<TestRoute> {
730            go_back:    Callback::from(|()| {}),
731            go_forward: Callback::from(|()| {}),
732            _marker:    PhantomData
733        };
734
735        let push_cb = nav.push_callback(TestRoute::Home);
736        let replace_cb = nav.replace_callback(TestRoute::Home);
737
738        let _ = push_cb;
739        let _ = replace_cb;
740    }
741
742    #[test]
743    fn navigation_callbacks_with_nested_route_params() {
744        #[derive(Clone, PartialEq, Debug, Routable)]
745        enum TestRoute {
746            #[at("/org/:org_id/team/:team_id/member/:member_id")]
747            Member {
748                org_id:    String,
749                team_id:   String,
750                member_id: String
751            }
752        }
753
754        let nav = Navigation::<TestRoute> {
755            go_back:    Callback::from(|()| {}),
756            go_forward: Callback::from(|()| {}),
757            _marker:    PhantomData
758        };
759
760        let route = TestRoute::Member {
761            org_id:    "org1".to_string(),
762            team_id:   "team1".to_string(),
763            member_id: "member1".to_string()
764        };
765        let callback = nav.push_callback(route);
766        let _ = callback;
767    }
768
769    #[test]
770    fn navigation_callbacks_with_optional_like_params() {
771        #[derive(Clone, PartialEq, Debug, Routable)]
772        enum TestRoute {
773            #[at("/filter/:min/:max")]
774            Filter { min: u32, max: u32 }
775        }
776
777        let nav = Navigation::<TestRoute> {
778            go_back:    Callback::from(|()| {}),
779            go_forward: Callback::from(|()| {}),
780            _marker:    PhantomData
781        };
782
783        let route = TestRoute::Filter {
784            min: 0, max: 100
785        };
786        let callback = nav.push_callback(route);
787        let _ = callback;
788    }
789
790    #[test]
791    fn navigation_go_callback_returns_valid_callback() {
792        #[derive(Clone, PartialEq, Debug, Routable)]
793        enum TestRoute {
794            #[at("/")]
795            Home
796        }
797
798        let nav = Navigation::<TestRoute> {
799            go_back:    Callback::from(|()| {}),
800            go_forward: Callback::from(|()| {}),
801            _marker:    PhantomData
802        };
803
804        let callback = nav.go_callback(-5);
805        let _ = callback;
806    }
807
808    #[test]
809    fn navigation_struct_fields_accessible() {
810        #[derive(Clone, PartialEq, Debug, Routable)]
811        enum TestRoute {
812            #[at("/")]
813            Home
814        }
815
816        let nav = Navigation::<TestRoute> {
817            go_back:    Callback::from(|()| {}),
818            go_forward: Callback::from(|()| {}),
819            _marker:    PhantomData
820        };
821
822        let _nav_back = &nav.go_back;
823        let _nav_forward = &nav.go_forward;
824        let _ = nav._marker;
825    }
826
827    #[test]
828    fn navigation_with_multiple_routable_types() {
829        #[derive(Clone, PartialEq, Debug, Routable)]
830        enum Route1 {
831            #[at("/")]
832            Home
833        }
834
835        #[derive(Clone, PartialEq, Debug, Routable)]
836        enum Route2 {
837            #[at("/")]
838            Page
839        }
840
841        let nav1 = Navigation::<Route1> {
842            go_back:    Callback::from(|()| {}),
843            go_forward: Callback::from(|()| {}),
844            _marker:    PhantomData
845        };
846
847        let nav2 = Navigation::<Route2> {
848            go_back:    Callback::from(|()| {}),
849            go_forward: Callback::from(|()| {}),
850            _marker:    PhantomData
851        };
852
853        let _ = nav1.push_callback(Route1::Home);
854        let _ = nav2.push_callback(Route2::Page);
855    }
856
857    #[test]
858    fn navigation_push_callback_multiple_times() {
859        #[derive(Clone, PartialEq, Debug, Routable)]
860        enum TestRoute {
861            #[at("/")]
862            Home
863        }
864
865        let nav = Navigation::<TestRoute> {
866            go_back:    Callback::from(|()| {}),
867            go_forward: Callback::from(|()| {}),
868            _marker:    PhantomData
869        };
870
871        let _ = nav.push_callback(TestRoute::Home);
872        let _ = nav.push_callback(TestRoute::Home);
873        let _ = nav.push_callback(TestRoute::Home);
874    }
875
876    #[test]
877    fn navigation_replace_callback_multiple_times() {
878        #[derive(Clone, PartialEq, Debug, Routable)]
879        enum TestRoute {
880            #[at("/")]
881            Home
882        }
883
884        let nav = Navigation::<TestRoute> {
885            go_back:    Callback::from(|()| {}),
886            go_forward: Callback::from(|()| {}),
887            _marker:    PhantomData
888        };
889
890        let _ = nav.replace_callback(TestRoute::Home);
891        let _ = nav.replace_callback(TestRoute::Home);
892        let _ = nav.replace_callback(TestRoute::Home);
893    }
894}