1
0

TextBoxTests_DataValidation.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Linq;
  6. using Avalonia.Controls.Presenters;
  7. using Avalonia.Controls.Templates;
  8. using Avalonia.Data;
  9. using Avalonia.Data.Core;
  10. using Avalonia.Headless;
  11. using Avalonia.Markup.Data;
  12. using Avalonia.Markup.Xaml.MarkupExtensions;
  13. using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
  14. using Avalonia.Platform;
  15. using Avalonia.UnitTests;
  16. using Moq;
  17. using Xunit;
  18. namespace Avalonia.Controls.UnitTests
  19. {
  20. public class TextBoxTests_DataValidation : ScopedTestBase
  21. {
  22. [Fact]
  23. public void Setter_Exceptions_Should_Set_Error_Pseudoclass()
  24. {
  25. using (UnitTestApplication.Start(Services))
  26. {
  27. var target = new TextBox
  28. {
  29. DataContext = new ExceptionTest(),
  30. [!TextBox.TextProperty] = new Binding(nameof(ExceptionTest.LessThan10), BindingMode.TwoWay),
  31. Template = CreateTemplate(),
  32. };
  33. target.ApplyTemplate();
  34. Assert.DoesNotContain(":error", target.Classes);
  35. target.Text = "20";
  36. Assert.Contains(":error", target.Classes);
  37. target.Text = "1";
  38. Assert.DoesNotContain(":error", target.Classes);
  39. }
  40. }
  41. [Fact]
  42. public void Setter_Exceptions_Should_Set_DataValidationErrors_Errors()
  43. {
  44. using (UnitTestApplication.Start(Services))
  45. {
  46. var target = new TextBox
  47. {
  48. DataContext = new ExceptionTest(),
  49. [!TextBox.TextProperty] = new Binding(nameof(ExceptionTest.LessThan10), BindingMode.TwoWay),
  50. Template = CreateTemplate(),
  51. };
  52. target.ApplyTemplate();
  53. Assert.Null(DataValidationErrors.GetErrors(target));
  54. target.Text = "20";
  55. IEnumerable<object> errors = DataValidationErrors.GetErrors(target);
  56. Assert.Single(errors);
  57. Assert.IsType<InvalidOperationException>(errors.Single());
  58. target.Text = "1";
  59. Assert.Null(DataValidationErrors.GetErrors(target));
  60. }
  61. }
  62. [Fact]
  63. public void Setter_Exceptions_Should_Be_Converter_If_Error_Converter_Set()
  64. {
  65. using (UnitTestApplication.Start(Services))
  66. {
  67. var target = new TextBox
  68. {
  69. DataContext = new ExceptionTest(),
  70. [!TextBox.TextProperty] = new Binding(nameof(ExceptionTest.LessThan10), BindingMode.TwoWay),
  71. Template = CreateTemplate()
  72. };
  73. DataValidationErrors.SetErrorConverter(target, err => "Error: " + err);
  74. target.ApplyTemplate();
  75. target.Text = "20";
  76. IEnumerable<object> errors = DataValidationErrors.GetErrors(target);
  77. Assert.Single(errors);
  78. var error = Assert.IsType<string>(errors.Single());
  79. Assert.StartsWith("Error: ", error);
  80. }
  81. }
  82. [Fact]
  83. public void Setter_Exceptions_Should_Set_DataValidationErrors_HasErrors()
  84. {
  85. using (UnitTestApplication.Start(Services))
  86. {
  87. var target = new TextBox
  88. {
  89. DataContext = new ExceptionTest(),
  90. [!TextBox.TextProperty] = new Binding(nameof(ExceptionTest.LessThan10), BindingMode.TwoWay),
  91. Template = CreateTemplate(),
  92. };
  93. target.ApplyTemplate();
  94. Assert.False(DataValidationErrors.GetHasErrors(target));
  95. target.Text = "20";
  96. Assert.True(DataValidationErrors.GetHasErrors(target));
  97. target.Text = "1";
  98. Assert.False(DataValidationErrors.GetHasErrors(target));
  99. }
  100. }
  101. [Fact]
  102. public void CompiledBindings_TypeConverter_Exceptions_Should_Set_DataValidationErrors_HasErrors()
  103. {
  104. var path = new CompiledBindingPathBuilder()
  105. .Property(
  106. new ClrPropertyInfo(
  107. nameof(ExceptionTest.LessThan10),
  108. target => ((ExceptionTest)target).LessThan10,
  109. (target, value) => ((ExceptionTest)target).LessThan10 = (int)value,
  110. typeof(int)),
  111. PropertyInfoAccessorFactory.CreateInpcPropertyAccessor)
  112. .Build();
  113. using (UnitTestApplication.Start(Services))
  114. {
  115. var target = new TextBox
  116. {
  117. DataContext = new ExceptionTest(),
  118. [!TextBox.TextProperty] = new CompiledBindingExtension
  119. {
  120. Source = new ExceptionTest(),
  121. Path = path,
  122. Mode = BindingMode.TwoWay
  123. },
  124. Template = CreateTemplate(),
  125. };
  126. target.ApplyTemplate();
  127. target.Text = "a";
  128. Assert.True(DataValidationErrors.GetHasErrors(target));
  129. }
  130. }
  131. private static TestServices Services => TestServices.MockThreadingInterface.With(
  132. standardCursorFactory: Mock.Of<ICursorFactory>(),
  133. textShaperImpl: new HeadlessTextShaperStub(),
  134. fontManagerImpl: new HeadlessFontManagerStub());
  135. private static IControlTemplate CreateTemplate()
  136. {
  137. return new FuncControlTemplate<TextBox>((control, scope) =>
  138. new TextPresenter
  139. {
  140. Name = "PART_TextPresenter",
  141. [!!TextPresenter.TextProperty] = new Binding
  142. {
  143. Path = "Text",
  144. Mode = BindingMode.TwoWay,
  145. Priority = BindingPriority.Template,
  146. RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
  147. },
  148. }.RegisterInNameScope(scope));
  149. }
  150. private class ExceptionTest
  151. {
  152. private int _lessThan10;
  153. public int LessThan10
  154. {
  155. get { return _lessThan10; }
  156. set
  157. {
  158. if (value < 10)
  159. {
  160. _lessThan10 = value;
  161. }
  162. else
  163. {
  164. throw new InvalidOperationException("More than 10.");
  165. }
  166. }
  167. }
  168. }
  169. private class IndeiTest : INotifyDataErrorInfo
  170. {
  171. private int _lessThan10;
  172. private Dictionary<string, IList<string>> _errors = new Dictionary<string, IList<string>>();
  173. public int LessThan10
  174. {
  175. get { return _lessThan10; }
  176. set
  177. {
  178. if (value < 10)
  179. {
  180. _lessThan10 = value;
  181. _errors.Remove(nameof(LessThan10));
  182. ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(LessThan10)));
  183. }
  184. else
  185. {
  186. _errors[nameof(LessThan10)] = new[] { "More than 10" };
  187. ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(LessThan10)));
  188. }
  189. }
  190. }
  191. public bool HasErrors => _lessThan10 >= 10;
  192. public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
  193. public IEnumerable GetErrors(string propertyName)
  194. {
  195. IList<string> result;
  196. _errors.TryGetValue(propertyName, out result);
  197. return result;
  198. }
  199. }
  200. }
  201. }