Last active
May 27, 2020 15:52
-
-
Save ryanholden8/21b1dddc4da428c1cfdc70da6ecf58f7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics.CodeAnalysis; | |
using System.Linq; | |
using DynamicData; | |
using DynamicData.Binding; | |
using DynamicDataGist.TransformWithInlineUpdate; | |
using FluentAssertions; | |
using Microsoft.Reactive.Testing; | |
using ReactiveUI.Fody.Helpers; | |
using Xunit; | |
namespace WMS.Tests.Models | |
{ | |
public class DynamicDataPipelineTests | |
{ | |
[Fact] | |
public void SoureCacheTransformWithInlineUpdate_Emits_Refreshed() | |
{ | |
// Arrange | |
var sourceCache = new SourceCache<Model, int>(model => model.Id); | |
var changeSetPipeline = sourceCache | |
.Connect() | |
.TransformWithInlineUpdate( | |
// The transformFactory is invoked when a new model is added to the upstream SourceCache | |
transformFactory: model => new ViewModel(model), | |
// The updateAction is invoked when a model in the upstream SourceCache has been replaced with a new model which has the same unique ID | |
updateAction: (existingViewModel, updatedModel) => existingViewModel.Model = updatedModel); | |
var changesOutput = new TestScheduler().CreateObserver(changeSetPipeline); | |
// Act | |
sourceCache.AddOrUpdate(Model.First); | |
sourceCache.AddOrUpdate(Model.Second); | |
sourceCache.RemoveKey(2); | |
sourceCache.AddOrUpdate(Model.FirstButUpdated); | |
// Assert | |
changesOutput | |
.Values() | |
.SelectMany(changeSet => changeSet.Select(change => (change.Reason, change.Current))) | |
.Should() | |
.BeInOrderSameTypeEquivalentTo( | |
(ChangeReason.Add, ViewModel.FirstButUpdated), // The same reference was updated already at this point so the model is updated. | |
(ChangeReason.Add, ViewModel.Second), | |
(ChangeReason.Remove, ViewModel.Second), | |
(ChangeReason.Refresh, ViewModel.FirstButUpdated)); | |
changesOutput.OnCompleted(); | |
sourceCache.Dispose(); | |
} | |
[Fact] | |
public void CollectionTransformWithInlineUpdate_FailsToEmit_RefreshedOrAddRemove() | |
{ | |
// Arrange | |
var sourceCache = new SourceCache<Model, int>(model => model.Id); | |
var disposable = sourceCache | |
.Connect() | |
.TransformWithInlineUpdate( | |
// The transformFactory is invoked when a new model is added to the upstream SourceCache | |
transformFactory: model => new ViewModel(model), | |
// The updateAction is invoked when a model in the upstream SourceCache has been replaced with a new model which has the same unique ID | |
updateAction: (existingViewModel, updatedModel) => existingViewModel.Model = updatedModel) | |
.Bind(out System.Collections.ObjectModel.ReadOnlyObservableCollection<ViewModel> collection) | |
.Subscribe(); | |
// This is what a reactiveUI recycler view converts the collection into | |
// https://github.com/reactiveui/ReactiveUI/blob/master/src/ReactiveUI.AndroidSupport/ReactiveRecyclerViewAdapter.cs#L146 | |
var changesOutput = new TestScheduler().CreateObserver(collection.ToObservableChangeSet()); | |
// Act | |
sourceCache.AddOrUpdate(Model.First); | |
sourceCache.AddOrUpdate(Model.Second); | |
sourceCache.RemoveKey(2); | |
sourceCache.AddOrUpdate(Model.FirstButUpdated); | |
// Assert | |
changesOutput | |
.Values() | |
.SelectMany(changeSet => changeSet.Select(change => (change.Reason, change.Range?.FirstOrDefault() ?? change.Item.Current))) | |
.Should() | |
.BeInOrderSameTypeEquivalentTo( | |
(ListChangeReason.AddRange, ViewModel.FirstButUpdated), // The same reference was updated already at this point so the model is updated. | |
(ListChangeReason.Add, ViewModel.Second), | |
(ListChangeReason.Remove, ViewModel.Second) | |
// What was logically expected: | |
// (ListChangeReason.Refresh, SimpleViewModel.FirstButUpdated) or (ListChangeReason.Replace, SimpleViewModel.FirstButUpdated) | |
// Reality: | |
/* No change set at all, it appears when binding to a collection refreshes are ignored */); | |
changesOutput.OnCompleted(); | |
sourceCache.Dispose(); | |
disposable.Dispose(); | |
} | |
#region Private | |
private class Model : IEquatable<Model> | |
{ | |
public readonly static Model First = new Model(id: 1, name: "first"); | |
public readonly static Model Second = new Model(id: 2, name: "second"); | |
public readonly static Model FirstButUpdated = new Model(id: 1, name: "first but updated"); | |
public int Id { get; } | |
public string Name { get; } | |
public Model(int id, string name) | |
{ | |
Id = id; | |
Name = name; | |
} | |
public override string ToString() => $"{nameof(Model)}({Id}): {Name}"; | |
public bool Equals([AllowNull] Model other) => Id == other?.Id && Name == other.Name; | |
} | |
private class ViewModel : ReactiveUI.ReactiveObject, IEquatable<ViewModel> | |
{ | |
public readonly static ViewModel First = new ViewModel(Model.First); | |
public readonly static ViewModel Second = new ViewModel(Model.Second); | |
public readonly static ViewModel FirstButUpdated = new ViewModel(Model.FirstButUpdated); | |
[Reactive] | |
public Model Model { get; set; } | |
public ViewModel(Model model) | |
{ | |
Model = model; | |
} | |
public override string ToString() => $"{nameof(ViewModel)}({Model.Id}): {Model.Name}"; | |
public bool Equals([AllowNull] ViewModel other) => Model == other?.Model; | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment