Skip to main content

yew_nav_link/hooks/navigation/
use_navigation.rs

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