From 518665babba12c9d0e7a6e1129c3cd53ad7f908c Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Mon, 17 May 2021 09:52:45 +0100 Subject: [PATCH] Feature RoutingState NavigateBack Result Type To IRoutableViewModel Changed Routing state to provide the nullable ViewModel upon navigating or Null if the stack is empty closes #2743 --- ...provalTests.ReactiveUI.net472.approved.txt | 2 +- ...provalTests.ReactiveUI.net5.0.approved.txt | 2 +- ...ests.ReactiveUI.netcoreapp3.1.approved.txt | 2 +- .../Routing/RoutingStateTests.cs | 40 ++++++++++++++++--- .../Mocks/NavigationViewModel.cs | 2 +- src/ReactiveUI/Routing/RoutingState.cs | 6 +-- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt index 966c135fce..67a660b900 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt @@ -706,7 +706,7 @@ namespace ReactiveUI [System.Runtime.Serialization.IgnoreDataMember] public ReactiveUI.ReactiveCommand NavigateAndReset { get; set; } [System.Runtime.Serialization.IgnoreDataMember] - public ReactiveUI.ReactiveCommand NavigateBack { get; set; } + public ReactiveUI.ReactiveCommand NavigateBack { get; set; } [System.Runtime.Serialization.IgnoreDataMember] public System.IObservable> NavigationChanged { get; set; } [System.Runtime.Serialization.IgnoreDataMember] diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt index 52a7d52fb1..3ca073c161 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt @@ -701,7 +701,7 @@ namespace ReactiveUI [System.Runtime.Serialization.IgnoreDataMember] public ReactiveUI.ReactiveCommand NavigateAndReset { get; set; } [System.Runtime.Serialization.IgnoreDataMember] - public ReactiveUI.ReactiveCommand NavigateBack { get; set; } + public ReactiveUI.ReactiveCommand NavigateBack { get; set; } [System.Runtime.Serialization.IgnoreDataMember] public System.IObservable> NavigationChanged { get; set; } [System.Runtime.Serialization.IgnoreDataMember] diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt index fed6f25781..e27f9e5206 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt @@ -699,7 +699,7 @@ namespace ReactiveUI [System.Runtime.Serialization.IgnoreDataMember] public ReactiveUI.ReactiveCommand NavigateAndReset { get; set; } [System.Runtime.Serialization.IgnoreDataMember] - public ReactiveUI.ReactiveCommand NavigateBack { get; set; } + public ReactiveUI.ReactiveCommand NavigateBack { get; set; } [System.Runtime.Serialization.IgnoreDataMember] public System.IObservable> NavigationChanged { get; set; } [System.Runtime.Serialization.IgnoreDataMember] diff --git a/src/ReactiveUI.Tests/Routing/RoutingStateTests.cs b/src/ReactiveUI.Tests/Routing/RoutingStateTests.cs index b51c14b17e..019d2e168d 100644 --- a/src/ReactiveUI.Tests/Routing/RoutingStateTests.cs +++ b/src/ReactiveUI.Tests/Routing/RoutingStateTests.cs @@ -34,29 +34,57 @@ public async Task NavigationPushPopTest() Assert.Equal(2, fixture.NavigationStack.Count); Assert.True(await fixture.NavigateBack.CanExecute.FirstAsync()); - await fixture.NavigateBack.Execute(); - + var navigatedTo = await fixture.NavigateBack.Execute(); + Assert.Equal(navigatedTo.GetType(), input.GetType()); Assert.Equal(1, fixture.NavigationStack.Count); } [Fact] - public void CurrentViewModelObservableIsAccurate() + public async Task CurrentViewModelObservableIsAccurate() { var fixture = new RoutingState(); fixture.CurrentViewModel.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var output).Subscribe(); Assert.Equal(1, output.Count); - fixture.Navigate.Execute(new TestViewModel { SomeProp = "A" }); + await fixture.Navigate.Execute(new TestViewModel { SomeProp = "A" }); Assert.Equal(2, output.Count); - fixture.Navigate.Execute(new TestViewModel { SomeProp = "B" }); + await fixture.Navigate.Execute(new TestViewModel { SomeProp = "B" }); Assert.Equal(3, output.Count); Assert.Equal("B", (output.Last() as TestViewModel)?.SomeProp); - fixture.NavigateBack.Execute(); + var navigatedTo = await fixture.NavigateBack.Execute(); + Assert.Equal(navigatedTo?.GetType(), output.Last()?.GetType()); Assert.Equal(4, output.Count); Assert.Equal("A", (output.Last() as TestViewModel)?.SomeProp); + Assert.Equal((navigatedTo as TestViewModel)?.SomeProp, (output.Last() as TestViewModel)?.SomeProp); + + await fixture.Navigate.Execute(new TestViewModel { SomeProp = "B" }); + Assert.Equal(5, output.Count); + Assert.Equal("B", (output.Last() as TestViewModel)?.SomeProp); + + await fixture.Navigate.Execute(new TestViewModel { SomeProp = "C" }); + Assert.Equal(6, output.Count); + Assert.Equal("C", (output.Last() as TestViewModel)?.SomeProp); + + navigatedTo = await fixture.NavigateBack.Execute(); + Assert.Equal(navigatedTo?.GetType(), output.Last()?.GetType()); + Assert.Equal(7, output.Count); + Assert.Equal("B", (output.Last() as TestViewModel)?.SomeProp); + Assert.Equal((navigatedTo as TestViewModel)?.SomeProp, (output.Last() as TestViewModel)?.SomeProp); + + navigatedTo = await fixture.NavigateBack.Execute(); + Assert.Equal(navigatedTo?.GetType(), output.Last()?.GetType()); + Assert.Equal(8, output.Count); + Assert.Equal("A", (output.Last() as TestViewModel)?.SomeProp); + Assert.Equal((navigatedTo as TestViewModel)?.SomeProp, (output.Last() as TestViewModel)?.SomeProp); + + navigatedTo = await fixture.NavigateBack.Execute(); + Assert.Equal(navigatedTo?.GetType(), output.Last()?.GetType()); + Assert.Equal(9, output.Count); + Assert.Equal(null, (output.Last() as TestViewModel)?.SomeProp); + Assert.Equal(null, (navigatedTo as TestViewModel)?.SomeProp); } [Fact] diff --git a/src/ReactiveUI.XamForms.Tests/Mocks/NavigationViewModel.cs b/src/ReactiveUI.XamForms.Tests/Mocks/NavigationViewModel.cs index e171f9c4b1..9497f159d6 100644 --- a/src/ReactiveUI.XamForms.Tests/Mocks/NavigationViewModel.cs +++ b/src/ReactiveUI.XamForms.Tests/Mocks/NavigationViewModel.cs @@ -62,6 +62,6 @@ public IObservable NavigateAndResetToChild(string value) /// Navigates back. /// /// An observable. - public IObservable NavigateBack() => Router.NavigateBack.Execute(); + public IObservable NavigateBack() => Router.NavigateBack.Execute(); } } diff --git a/src/ReactiveUI/Routing/RoutingState.cs b/src/ReactiveUI/Routing/RoutingState.cs index bebffd6298..e57272308d 100644 --- a/src/ReactiveUI/Routing/RoutingState.cs +++ b/src/ReactiveUI/Routing/RoutingState.cs @@ -81,7 +81,7 @@ public IScheduler Scheduler /// Gets or sets a command which will navigate back to the previous element in the stack. /// [IgnoreDataMember] - public ReactiveCommand NavigateBack { get; protected set; } + public ReactiveCommand NavigateBack { get; protected set; } /// /// Gets or sets a command that navigates to the a new element in the stack - the Execute parameter @@ -122,11 +122,11 @@ private void SetupRx() var countAsBehavior = Observable.Defer(() => Observable.Return(NavigationStack.Count)).Concat(NavigationChanged.CountChanged().Select(_ => NavigationStack.Count)); NavigateBack = - ReactiveCommand.CreateFromObservable( + ReactiveCommand.CreateFromObservable( () => { _navigationStack.RemoveAt(NavigationStack.Count - 1); - return Observables.Unit; + return Observable.Return(NavigationStack.Count > 0 ? _navigationStack[NavigationStack.Count - 1] : default); }, countAsBehavior.Select(x => x > 1), navigateScheduler);