using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NUnit.Framework; namespace Xamarin.Forms.Core.UnitTests { [TestFixture] public class NavigationUnitTest : BaseTestFixture { [Test] public async Task TestNavigationImplPush () { NavigationPage nav = new NavigationPage (); Assert.IsNull(nav.RootPage); Assert.IsNull (nav.CurrentPage); Label child = new Label {Text = "Label"}; Page childRoot = new ContentPage {Content = child}; await nav.Navigation.PushAsync (childRoot); Assert.AreSame(childRoot, nav.RootPage); Assert.AreSame(childRoot, nav.CurrentPage); Assert.AreSame(nav.RootPage, nav.CurrentPage); } [Test] public async Task TestNavigationImplPop () { NavigationPage nav = new NavigationPage (); Label child = new Label (); Page childRoot = new ContentPage {Content = child}; Label child2 = new Label (); Page childRoot2 = new ContentPage {Content = child2}; await nav.Navigation.PushAsync (childRoot); await nav.Navigation.PushAsync (childRoot2); bool fired = false; nav.Popped += (sender, e) => fired = true; Assert.AreSame(childRoot, nav.RootPage); Assert.AreNotSame(childRoot2, nav.RootPage); Assert.AreNotSame(nav.RootPage, nav.CurrentPage); var popped = await nav.Navigation.PopAsync (); Assert.True (fired); Assert.AreSame(childRoot, nav.RootPage); Assert.AreSame(childRoot, nav.CurrentPage); Assert.AreSame(nav.RootPage, nav.CurrentPage); Assert.AreEqual (childRoot2, popped); await nav.PopAsync (); var last = await nav.Navigation.PopAsync (); Assert.IsNull (last); Assert.IsNotNull(nav.RootPage); Assert.IsNotNull(nav.CurrentPage); Assert.AreSame(nav.RootPage, nav.CurrentPage); } [Test] public async Task TestPushRoot () { NavigationPage nav = new NavigationPage (); Assert.IsNull(nav.RootPage); Assert.IsNull (nav.CurrentPage); Label child = new Label {Text = "Label"}; Page childRoot = new ContentPage {Content = child}; await nav.PushAsync (childRoot); Assert.AreSame (childRoot, nav.RootPage); Assert.AreSame(childRoot, nav.CurrentPage); Assert.AreSame(nav.RootPage, nav.CurrentPage); } [Test] public async Task TestPushEvent () { NavigationPage nav = new NavigationPage (); Label child = new Label (); Page childRoot = new ContentPage {Content = child}; bool fired = false; nav.Pushed += (sender, e) => fired = true; await nav.PushAsync (childRoot); Assert.True (fired); } [Test] public async Task TestDoublePush () { NavigationPage nav = new NavigationPage (); Label child = new Label (); Page childRoot = new ContentPage {Content = child}; await nav.PushAsync (childRoot); bool fired = false; nav.Pushed += (sender, e) => fired = true; Assert.AreSame(childRoot, nav.RootPage); Assert.AreSame(childRoot, nav.CurrentPage); await nav.PushAsync (childRoot); Assert.False (fired); Assert.AreSame(childRoot, nav.RootPage); Assert.AreSame(childRoot, nav.CurrentPage); Assert.AreSame(nav.RootPage, nav.CurrentPage); } [Test] public async Task TestPop () { NavigationPage nav = new NavigationPage (); Label child = new Label (); Page childRoot = new ContentPage {Content = child}; Label child2 = new Label (); Page childRoot2 = new ContentPage {Content = child2}; await nav.PushAsync (childRoot); await nav.PushAsync (childRoot2); bool fired = false; nav.Popped += (sender, e) => fired = true; var popped = await nav.PopAsync (); Assert.True (fired); Assert.AreSame (childRoot, nav.CurrentPage); Assert.AreEqual (childRoot2, popped); await nav.PopAsync (); var last = await nav.PopAsync (); Assert.IsNull (last); } [Test] public void TestTint () { var nav = new NavigationPage (); Assert.AreEqual (Color.Default, nav.Tint); bool signaled = false; nav.PropertyChanged += (sender, args) => { if (args.PropertyName == "Tint") signaled = true; }; nav.Tint = new Color (1, 0, 0); Assert.AreEqual (new Color (1, 0, 0), nav.Tint); Assert.True (signaled); } [Test] public void TestTintDoubleSet () { var nav = new NavigationPage (); bool signaled = false; nav.PropertyChanged += (sender, args) => { if (args.PropertyName == "Tint") signaled = true; }; nav.Tint = nav.Tint; Assert.False (signaled); } [Test] public async Task TestPopToRoot () { var nav = new NavigationPage (); bool signaled = false; nav.PoppedToRoot += (sender, args) => signaled = true; var root = new ContentPage {Content = new View ()}; var child1 = new ContentPage {Content = new View ()}; var child2 = new ContentPage {Content = new View ()}; await nav.PushAsync (root); await nav.PushAsync (child1); await nav.PushAsync (child2); nav.PopToRootAsync (); Assert.True (signaled); Assert.AreSame (root, nav.RootPage); Assert.AreSame(root, nav.CurrentPage); Assert.AreSame(nav.RootPage, nav.CurrentPage); } [Test] public async Task TestPopToRootEventArgs () { var nav = new NavigationPage (); List poppedChildren = null; nav.PoppedToRoot += (sender, args) => poppedChildren = (args as PoppedToRootEventArgs).PoppedPages.ToList(); var root = new ContentPage {Content = new View ()}; var child1 = new ContentPage {Content = new View ()}; var child2 = new ContentPage {Content = new View ()}; await nav.PushAsync (root); await nav.PushAsync (child1); await nav.PushAsync (child2); await nav.PopToRootAsync (); Assert.IsNotNull (poppedChildren); Assert.AreEqual (2, poppedChildren.Count); Assert.Contains (child1, poppedChildren); Assert.Contains (child2, poppedChildren); Assert.AreSame(root, nav.RootPage); Assert.AreSame(root, nav.CurrentPage); Assert.AreSame(nav.RootPage, nav.CurrentPage); } [Test] public async Task PeekOne() { var nav = new NavigationPage(); bool signaled = false; nav.PoppedToRoot += (sender, args) => signaled = true; var root = new ContentPage { Content = new View() }; var child1 = new ContentPage { Content = new View() }; var child2 = new ContentPage { Content = new View() }; await nav.PushAsync(root); await nav.PushAsync(child1); await nav.PushAsync(child2); Assert.AreEqual(((INavigationPageController)nav).Peek(1), child1); } [Test] public async Task PeekZero() { var nav = new NavigationPage(); bool signaled = false; nav.PoppedToRoot += (sender, args) => signaled = true; var root = new ContentPage { Content = new View() }; var child1 = new ContentPage { Content = new View() }; var child2 = new ContentPage { Content = new View() }; await nav.PushAsync(root); await nav.PushAsync(child1); await nav.PushAsync(child2); Assert.AreEqual(((INavigationPageController)nav).Peek(0), child2); Assert.AreEqual(((INavigationPageController)nav).Peek(), child2); } [Test] public async Task PeekPastStackDepth() { var nav = new NavigationPage(); bool signaled = false; nav.PoppedToRoot += (sender, args) => signaled = true; var root = new ContentPage { Content = new View() }; var child1 = new ContentPage { Content = new View() }; var child2 = new ContentPage { Content = new View() }; await nav.PushAsync(root); await nav.PushAsync(child1); await nav.PushAsync(child2); Assert.AreEqual(((INavigationPageController)nav).Peek(3), null); } [Test] public async Task PeekShallow() { var nav = new NavigationPage(); bool signaled = false; nav.PoppedToRoot += (sender, args) => signaled = true; var root = new ContentPage { Content = new View() }; var child1 = new ContentPage { Content = new View() }; var child2 = new ContentPage { Content = new View() }; await nav.PushAsync(root); await nav.PushAsync(child1); await nav.PushAsync(child2); Assert.AreEqual(((INavigationPageController)nav).Peek(-1), null); } [Test] public async Task PeekEmpty([Range(0, 3)] int depth) { var nav = new NavigationPage(); bool signaled = false; nav.PoppedToRoot += (sender, args) => signaled = true; Assert.AreEqual(((INavigationPageController)nav).Peek(depth), null); } [Test] public void ConstructWithRoot () { var root = new ContentPage (); var nav = new NavigationPage (root); Assert.AreEqual (1, ((INavigationPageController)nav).StackDepth); Assert.AreEqual (root, ((IElementController)nav).LogicalChildren[0]); } [Test] public void TitleViewSetProperty() { var root = new ContentPage(); var nav = new NavigationPage(root); View target = new View(); NavigationPage.SetTitleView(root, target); var result = NavigationPage.GetTitleView(root); Assert.AreSame(result, target); } [Test] public void TitleViewSetsParentWhenAdded() { var root = new ContentPage(); var nav = new NavigationPage(root); View target = new View(); NavigationPage.SetTitleView(root, target); Assert.AreSame(root, target.Parent); } [Test] public void TitleViewClearsParentWhenRemoved() { var root = new ContentPage(); var nav = new NavigationPage(root); View target = new View(); NavigationPage.SetTitleView(root, target); NavigationPage.SetTitleView(root, null); Assert.IsNull(target.Parent); } [Test] public async Task NavigationChangedEventArgs () { var rootPage = new ContentPage { Title = "Root" }; var navPage = new NavigationPage (rootPage); var rootArg = new Page (); navPage.Pushed += (s, e) => { rootArg = e.Page; }; var pushPage = new ContentPage { Title = "Page 2" }; await navPage.PushAsync (pushPage); Assert.AreEqual (rootArg, pushPage); var secondPushPage = new ContentPage { Title = "Page 3" }; await navPage.PushAsync (secondPushPage); Assert.AreEqual (rootArg, secondPushPage); } [Test] public async Task CurrentPageChanged() { var root = new ContentPage { Title = "Root" }; var navPage = new NavigationPage (root); bool changing = false; navPage.PropertyChanging += (object sender, PropertyChangingEventArgs e) => { if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName) { Assert.That (navPage.CurrentPage, Is.SameAs (root)); changing = true; } }; var next = new ContentPage { Title = "Next" }; bool changed = false; navPage.PropertyChanged += (sender, e) => { if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName) { Assert.That (navPage.CurrentPage, Is.SameAs (next)); changed = true; } }; await navPage.PushAsync (next); Assert.That (changing, Is.True, "PropertyChanging was not raised for 'CurrentPage'"); Assert.That (changed, Is.True, "PropertyChanged was not raised for 'CurrentPage'"); } [Test] public async void HandlesPopToRoot () { var root = new ContentPage { Title = "Root" }; var navPage = new NavigationPage (root); await navPage.PushAsync (new ContentPage ()); await navPage.PushAsync (new ContentPage ()); bool popped = false; navPage.PoppedToRoot += (sender, args) => { popped = true; }; await navPage.Navigation.PopToRootAsync (); Assert.True (popped); } [Test] public void SendsBackButtonEventToCurrentPage () { var current = new BackButtonPage (); var navPage = new NavigationPage (current); var emitted = false; current.BackPressed += (sender, args) => emitted = true; navPage.SendBackButtonPressed (); Assert.True (emitted); } [Test] public void DoesNotSendBackEventToNonCurrentPage () { var current = new BackButtonPage (); var navPage = new NavigationPage (current); navPage.PushAsync (new ContentPage ()); var emitted = false; current.BackPressed += (sender, args) => emitted = true; navPage.SendBackButtonPressed (); Assert.False (emitted); } [Test] public async void NavigatesBackWhenBackButtonPressed () { var root = new ContentPage { Title = "Root" }; var navPage = new NavigationPage (root); await navPage.PushAsync (new ContentPage ()); var result = navPage.SendBackButtonPressed (); Assert.AreEqual (root, navPage.CurrentPage); Assert.True (result); } [Test] public async void DoesNotNavigatesBackWhenBackButtonPressedIfHandled () { var root = new BackButtonPage { Title = "Root" }; var second = new BackButtonPage () {Handle = true}; var navPage = new NavigationPage (root); await navPage.PushAsync (second); navPage.SendBackButtonPressed (); Assert.AreEqual (second, navPage.CurrentPage); } [Test] public void DoesNotHandleBackButtonWhenNoNavStack () { var root = new ContentPage { Title = "Root" }; var navPage = new NavigationPage (root); var result = navPage.SendBackButtonPressed (); Assert.False (result); } [Test] public void TestInsertPage() { var root = new ContentPage { Title = "Root" }; var newPage = new ContentPage(); var navPage = new NavigationPage(root); navPage.Navigation.InsertPageBefore(newPage, navPage.RootPage); Assert.AreSame(newPage, navPage.RootPage); Assert.AreNotSame(newPage, navPage.CurrentPage); Assert.AreNotSame(navPage.RootPage, navPage.CurrentPage); Assert.AreSame(root, navPage.CurrentPage); Assert.Throws(() => { navPage.Navigation.InsertPageBefore(new ContentPage(), new ContentPage()); }); Assert.Throws(() => { navPage.Navigation.InsertPageBefore(navPage.RootPage, navPage.CurrentPage); }); Assert.Throws(() => { navPage.Navigation.InsertPageBefore(null, navPage.CurrentPage); }); Assert.Throws(() => { navPage.Navigation.InsertPageBefore(new ContentPage(), null); }); } [Test] public async void TestRemovePage() { var root = new ContentPage { Title = "Root" }; var newPage = new ContentPage(); var navPage = new NavigationPage(root); await navPage.PushAsync(newPage); navPage.Navigation.RemovePage(root); Assert.AreSame(newPage, navPage.RootPage); Assert.AreSame(newPage, navPage.CurrentPage); Assert.AreSame(navPage.RootPage, navPage.CurrentPage); Assert.AreNotSame(root, navPage.CurrentPage); Assert.Throws(() => { navPage.Navigation.RemovePage(root); }); Assert.Throws(() => { navPage.Navigation.RemovePage(newPage); }); Assert.Throws(() => { navPage.Navigation.RemovePage(null); }); } [Test (Description = "CurrentPage should not be set to null when you attempt to pop the last page")] [Property ("Bugzilla", 28335)] public async Task CurrentPageNotNullPoppingRoot() { var root = new ContentPage { Title = "Root" }; var navPage = new NavigationPage (root); var popped = await navPage.PopAsync (); Assert.That (popped, Is.Null); Assert.That (navPage.CurrentPage, Is.SameAs (root)); } [Test] [Property("Bugzilla", 31171)] public async Task ReleasesPoppedPage() { var root = new ContentPage { Title = "Root" }; var navPage = new NavigationPage(root); var isFinalized = false; await navPage.PushAsync(new PageWithFinalizer(() => isFinalized = true)); await navPage.PopAsync(); await Task.Delay(100); GC.Collect(); GC.WaitForPendingFinalizers(); Assert.IsTrue(isFinalized); } } internal class BackButtonPage : ContentPage { public event EventHandler BackPressed; public bool Handle = false; protected override bool OnBackButtonPressed () { if (BackPressed != null) BackPressed (this, EventArgs.Empty); return Handle; } } internal class PageWithFinalizer : Page { Action OnFinalize; public PageWithFinalizer(Action onFinalize) { OnFinalize = onFinalize; } ~PageWithFinalizer() { OnFinalize(); } } }