Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
554 changes: 13 additions & 541 deletions Source/Csla.Blazor/ViewModel.cs

Large diffs are not rendered by default.

993 changes: 346 additions & 647 deletions Source/Csla.Xaml.Shared/ViewModelBase.cs

Large diffs are not rendered by default.

915 changes: 915 additions & 0 deletions Source/Csla/Mvvm/ViewModel.cs

Large diffs are not rendered by default.

155 changes: 155 additions & 0 deletions Source/tests/Csla.Xaml.Wpf.Tests/ViewModelBaseTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//-----------------------------------------------------------------------
// <copyright file="ViewModelBaseTests.cs" company="Marimer LLC">
// Copyright (c) Marimer LLC. All rights reserved.
// Website: https://cslanet.com
// </copyright>
// <summary>Tests for the WPF ViewModelBase wrapper over Csla.Mvvm.ViewModel</summary>
//-----------------------------------------------------------------------

using Csla.Configuration;
using Csla.Core;
using Csla.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Csla.Xaml.Wpf.Tests
{
[TestClass]
public class ViewModelBaseTests
{
private static CslaTestHost _testHost;

[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
_testHost = CslaTestHost.Create(o => o.ConfigureCsla(c => c.AddXaml()));
}

[ClassCleanup]
public static void ClassCleanup()
{
_testHost?.Dispose();
}

private class ReplacingViewModel : ViewModel<SavableRoot>
{
public SavableRoot Replacement { get; set; }

protected override Task<SavableRoot> DoSaveAsync(ISavable cloned) => Task.FromResult(Replacement);
}

private class DenyEditViewModel : ViewModel<SavableRoot>
{
public override bool CanEditObject => false;
}

[TestMethod]
public void DefaultsToManagingObjectLifetime()
{
DispatcherContext.Run(async () =>
{
var model = await _testHost.GetDataPortal<SavableRoot>().CreateAsync();
var vm = new ViewModel<SavableRoot>();

vm.Model = model;

Assert.IsTrue(vm.ManageObjectLifetime);
Assert.AreEqual(1, ((IUndoableObject)model).EditLevel);
});
}

[TestMethod]
public void SettingModelDependencyProperty_HooksModel()
{
DispatcherContext.Run(async () =>
{
var model = await _testHost.GetDataPortal<SavableRoot>().CreateAsync();
var vm = new ViewModel<SavableRoot>();
object sender = null;
vm.ModelPropertyChanged += (s, _) => sender = s;

vm.SetValue(ViewModelBase<SavableRoot>.ModelProperty, model);
model.Name = "Changed";

Assert.AreSame(vm, sender);
Assert.IsTrue(vm.IsDirty);
});
}

[TestMethod]
public void SaveAsync_ReplacedModel_UpdatesDependencyProperty()
{
DispatcherContext.Run(async () =>
{
var model = await _testHost.GetDataPortal<SavableRoot>().CreateAsync();
var replacement = await _testHost.GetDataPortal<SavableRoot>().CreateAsync();
var vm = new ReplacingViewModel { Model = model, Replacement = replacement };
model.Name = "Valid";

await vm.SaveAsync();

Assert.IsNull(vm.Exception);
Assert.AreSame(replacement, vm.GetValue(ViewModelBase<SavableRoot>.ModelProperty));
Assert.AreSame(replacement, vm.Model);
});
}

[TestMethod]
public void SaveAsync_Failure_CapturesExceptionWithViewModelAsSender()
{
DispatcherContext.Run(async () =>
{
var model = await _testHost.GetDataPortal<SavableRoot>().CreateAsync();
var vm = new ViewModel<SavableRoot> { Model = model };
object errorSender = null;
vm.Error += (s, _) => errorSender = s;
model.Name = SavableRoot.FailOnSave;

await vm.SaveAsync();

Assert.IsNotNull(vm.Exception);
Assert.AreNotEqual(string.Empty, vm.ViewModelErrorText);
Assert.AreSame(vm, errorSender);
});
}

[TestMethod]
public void OverriddenObjectPermission_IsUsedForStatus()
{
DispatcherContext.Run(async () =>
{
var model = await _testHost.GetDataPortal<SavableRoot>().CreateAsync();
var vm = new DenyEditViewModel { Model = model };

model.Name = "Valid";

Assert.IsTrue(model.IsSavable);
Assert.IsFalse(vm.CanSave);
});
}
}

[Serializable]
public class SavableRoot : BusinessBase<SavableRoot>
{
public const string FailOnSave = "fail";

public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(nameof(Name));
public string Name
{
get => GetProperty(NameProperty);
set => SetProperty(NameProperty, value);
}

[Create]
private void Create()
{ }

[Insert]
[Update]
private void Save()

Check warning on line 149 in Source/tests/Csla.Xaml.Wpf.Tests/ViewModelBaseTests.cs

View workflow job for this annotation

GitHub Actions / build

'SavableRoot.Save()' hides inherited member 'BusinessBase<SavableRoot>.Save()'. Use the new keyword if hiding was intended.
{
if (Name == FailOnSave)
throw new InvalidOperationException("Save failed");
}
}
}
131 changes: 131 additions & 0 deletions Source/tests/csla.netcore.test/Mvvm/MvvmTestObjects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//-----------------------------------------------------------------------
// <copyright file="MvvmTestObjects.cs" company="Marimer LLC">
// Copyright (c) Marimer LLC. All rights reserved.
// Website: https://cslanet.com
// </copyright>
// <summary>Business types used by the Csla.Mvvm tests</summary>
//-----------------------------------------------------------------------

using System.ComponentModel;
using Csla.Core;
using Csla.Rules;
using Csla.Rules.CommonRules;

namespace Csla.Test.Mvvm
{
public class TestViewModel<T> : Csla.Mvvm.ViewModel<T>
{
public TestViewModel(ApplicationContext applicationContext)
: base(applicationContext)
{ }

public void Cancel() => DoCancel();
public object AddNew() => DoAddNew();
public void Remove(object item) => DoRemove(item);
public void Delete() => DoDelete();
}

public class NoContextViewModel<T> : Csla.Mvvm.ViewModel<T>
{
}

[Serializable]
public class MvvmRoot : BusinessBase<MvvmRoot>
{
public const string FailOnSave = "fail";

public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(nameof(Name));
public string Name
{
get => GetProperty(NameProperty);
set => SetProperty(NameProperty, value);
}

protected override void AddBusinessRules()
{
base.AddBusinessRules();
BusinessRules.AddRule(new Required(NameProperty));
}

[Create]
private void Create()
{
BusinessRules.CheckRules();
}

[Fetch]
private void Fetch(int id)
{
if (id < 0)
throw new ArgumentOutOfRangeException(nameof(id));
using (BypassPropertyChecks)
Name = $"Fetched {id}";
BusinessRules.CheckRules();
}

[Insert]
[Update]
private void Save()
{
if (Name == FailOnSave)
throw new InvalidOperationException("Save failed");
}

[DeleteSelf]
private void DeleteSelf()
{ }
}

[Serializable]
public class MvvmChild : BusinessBase<MvvmChild>
{
public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(nameof(Name));
public string Name
{
get => GetProperty(NameProperty);
set => SetProperty(NameProperty, value);
}

[CreateChild]
private void Create()
{ }
}

[Serializable]
public class MvvmList : BusinessListBase<MvvmList, MvvmChild>
{
[Create]
private void Create()
{ }
}

public class SyncAuthRule : AuthorizationRule
{
private readonly bool _hasPermission;

public SyncAuthRule(AuthorizationActions action, bool hasPermission)
: base(action)
{
_hasPermission = hasPermission;
}

protected override void Execute(IAuthorizationContext context)
{
context.HasPermission = _hasPermission;
}
}

[Serializable]
public class MvvmDenyEditRoot : BusinessBase<MvvmDenyEditRoot>
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static void AddObjectAuthorizationRules()
{
BusinessRules.AddRule(typeof(MvvmDenyEditRoot), new SyncAuthRule(AuthorizationActions.EditObject, false));
}

[Create]
private void Create()
{ }
}
}
Loading
Loading