public async Task It_produces_failed_notifications_for_Back()
        {
            var sut = new BlindStackNavigator();

            // Add 2 items to be able to perform a back operation.
            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM()));

            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(throwOnDispose: true)));

            var events = new List <StackNavigatorEventArgs>();

            sut.StateChanged += Sut_StateChanged;

            void Sut_StateChanged(object sender, StackNavigatorEventArgs args)
            {
                events.Add(args);
            }

            await Assert.ThrowsAsync <InvalidOperationException>(async() => await sut.NavigateBack(CancellationToken.None));

            sut.StateChanged -= Sut_StateChanged;

            // There must be 2 notifications: 1 Processing when the back starts and 1 FailedToProcess when it ends.
            Assert.Equal(2, events.Count);
            Assert.Equal(NavigatorRequestState.Processing, events[0].CurrentState.LastRequestState);
            Assert.Equal(NavigatorRequestState.FailedToProcess, events[1].CurrentState.LastRequestState);
        }
        public async Task It_removes_multiple_entries_and_navigate_back_correctly()
        {
            var sut = new BlindStackNavigator();

            // Add 6 items to the stack.
            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(0)));

            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(1)));

            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(2)));

            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(3)));

            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(4)));

            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(5)));

            var indexes = Enumerable.Range(2, 3);

            // Remove the third, fourth and fifth items
            await sut.RemoveEntries(CancellationToken.None, indexes);

            // There must be 3 items left
            Assert.Equal(3, sut.State.Stack.Count);

            // Navigates back
            await sut.NavigateBack(CancellationToken.None);

            // The viewmodel count should be 2 and the current viewmodel id should be 1
            Assert.Equal(2, sut.State.Stack.Count);
            Assert.Equal(1, ((TestVM)sut.State.Stack.Last().ViewModel).Id);
        }
        public async Task It_disposes_VM_when_navigating_back()
        {
            var sut = new BlindStackNavigator();

            // Add 2 items to the stack.
            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(0)));

            var vm = (TestVM)await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(1)));

            // Navigate back to remove the last item.
            await sut.NavigateBack(CancellationToken.None);

            // The removed entry must be disposed.
            Assert.True(vm.IsDisposed);
        }
        public async Task Interface_contract_changes_can_be_detected()
        {
            var             ct        = CancellationToken.None;
            IStackNavigator navigator = new BlindStackNavigator();

            // If the core contract changes, we get compilation errors here.
            INavigableViewModel vmAfterNavigate = await navigator.Navigate(ct, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(), false, false));

            INavigableViewModel vmAfterBack = await navigator.NavigateBack(ct);

            await navigator.Clear(ct);

            // Navigate twice so that RemoveEntries works.
            await navigator.Navigate(ct, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(), false, false));

            await navigator.Navigate(ct, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(), false, false));

            await navigator.RemoveEntries(ct, new int[] { 0 }.AsEnumerable());
        }
        public async Task It_fails_to_navigate_back_when_there_arent_enough_entries()
        {
            var sut = new BlindStackNavigator();

            // Add 1 item to the stack.
            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM()));

            var stackBeforeNavigateBack = sut.State.Stack;

            // Navigating back must 'fail' if there is only 1 entry.
            // There is no exception, but the result vm must be null.
            var vm = await sut.NavigateBack(CancellationToken.None);

            Assert.Null(vm);

            var stackAfterNavigateBack = sut.State.Stack;

            // Because the back didn't do anything, the stack must not change.
            Assert.Equal(stackAfterNavigateBack, stackBeforeNavigateBack);
        }
        public async Task It_navigates_back()
        {
            var sut = new BlindStackNavigator();

            // Add 2 items to the stack.
            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(0)));

            await sut.Navigate(CancellationToken.None, StackNavigatorRequest.GetNavigateRequest(() => new TestVM(1)));

            // The last entry must match the last navigation.
            Assert.Equal(1, ((TestVM)sut.State.Stack.Last().ViewModel).Id);

            var vmFromNavigation = await sut.NavigateBack(CancellationToken.None);

            var vmFromStack = (TestVM)sut.State.Stack.Last().ViewModel;

            // The ViewModels from the navigation and stack must be the same.
            Assert.Equal(vmFromStack, vmFromNavigation);

            // The final ViewModel must be the first one we navigated to.
            Assert.Equal(0, vmFromStack.Id);
        }