public async Task ValidationErrorUsesDisplayAttributeName() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <int, TestInputNumberComponent> { EditContext = new EditContext(model), ValueExpression = () => model.SomeNumber, AdditionalAttributes = new Dictionary <string, object> { { "DisplayName", "Some number" } } }; var fieldIdentifier = FieldIdentifier.Create(() => model.SomeNumber); var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent); // Act await inputComponent.SetCurrentValueAsStringAsync("notANumber"); // Assert var validationMessages = rootComponent.EditContext.GetValidationMessages(fieldIdentifier); Assert.NotEmpty(validationMessages); Assert.Contains("The Some number field must be a number.", validationMessages); }
public async Task RespondsToValidationStateChangeNotifications() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), ValueExpression = () => model.StringProperty }; var fieldIdentifier = FieldIdentifier.Create(() => model.StringProperty); var renderer = new TestRenderer(); var rootComponentId = renderer.AssignRootComponentId(rootComponent); await renderer.RenderRootComponentAsync(rootComponentId); // Initally, it rendered one batch and is valid var batch1 = renderer.Batches.Single(); var componentFrame1 = batch1.GetComponentFrames <TestInputComponent <string> >().Single(); var inputComponentId = componentFrame1.ComponentId; var component = (TestInputComponent <string>)componentFrame1.Component; Assert.Equal("valid", component.CssClass); // Act: update the field state in the EditContext and notify var messageStore = new ValidationMessageStore(rootComponent.EditContext); messageStore.Add(fieldIdentifier, "Some message"); await renderer.Dispatcher.InvokeAsync(rootComponent.EditContext.NotifyValidationStateChanged); // Assert: The input component rendered itself again and now has the new class var batch2 = renderer.Batches.Skip(1).Single(); Assert.Equal(inputComponentId, batch2.DiffsByComponentId.Keys.Single()); Assert.Equal("invalid", component.CssClass); }
public async Task ValidationErrorUsesDisplayAttributeName() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <DateTime, TestInputDateComponent> { EditContext = new EditContext(model), ValueExpression = () => model.DateProperty, AdditionalAttributes = new Dictionary <string, object> { { "DisplayName", "Date property" } } }; var fieldIdentifier = FieldIdentifier.Create(() => model.DateProperty); var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent); // Act await inputComponent.SetCurrentValueAsStringAsync("invalidDate"); // Assert var validationMessages = rootComponent.EditContext.GetValidationMessages(fieldIdentifier); Assert.NotEmpty(validationMessages); Assert.Contains("The Date property field must be a date.", validationMessages); }
public async Task ParsesCurrentValueAsStringWhenChanged_Valid() { // Arrange var model = new TestModel(); var valueChangedArgs = new List <DateTime>(); var rootComponent = new TestInputHostComponent <DateTime, TestDateInputComponent> { EditContext = new EditContext(model), ValueChanged = valueChangedArgs.Add, ValueExpression = () => model.DateProperty }; var fieldIdentifier = FieldIdentifier.Create(() => model.DateProperty); var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); var numValidationStateChanges = 0; rootComponent.EditContext.OnValidationStateChanged += (sender, eventArgs) => { numValidationStateChanges++; }; // Act inputComponent.CurrentValueAsString = "1991/11/20"; // Assert var receivedParsedValue = valueChangedArgs.Single(); Assert.Equal(1991, receivedParsedValue.Year); Assert.Equal(11, receivedParsedValue.Month); Assert.Equal(20, receivedParsedValue.Day); Assert.True(rootComponent.EditContext.IsModified(fieldIdentifier)); Assert.Empty(rootComponent.EditContext.GetValidationMessages(fieldIdentifier)); Assert.Equal(0, numValidationStateChanges); }
public async Task SuppliesFieldClassCorrespondingToFieldState() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), ValueExpression = () => model.StringProperty }; var fieldIdentifier = FieldIdentifier.Create(() => model.StringProperty); // Act/Assert: Initally, it's valid and unmodified var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); Assert.Equal("valid", inputComponent.CssClass); // no Class was specified // Act/Assert: Modify the field rootComponent.EditContext.NotifyFieldChanged(fieldIdentifier); Assert.Equal("modified valid", inputComponent.CssClass); // Act/Assert: Make it invalid var messages = new ValidationMessageStore(rootComponent.EditContext); messages.Add(fieldIdentifier, "I do not like this value"); Assert.Equal("modified invalid", inputComponent.CssClass); // Act/Assert: Clear the modification flag rootComponent.EditContext.MarkAsUnmodified(fieldIdentifier); Assert.Equal("invalid", inputComponent.CssClass); // Act/Assert: Make it valid messages.Clear(); Assert.Equal("valid", inputComponent.CssClass); }
public async Task UserSpecifiedAriaValueIsNotChangedIfInvalid() { // Arrange// Arrange var model = new TestModel(); var invalidContext = new EditContext(model); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = invalidContext, ValueExpression = () => model.StringProperty }; rootComponent.AdditionalAttributes = new Dictionary <string, object>(); rootComponent.AdditionalAttributes["aria-invalid"] = "userSpecifiedValue"; var fieldIdentifier = FieldIdentifier.Create(() => model.StringProperty); var messageStore = new ValidationMessageStore(invalidContext); messageStore.Add(fieldIdentifier, "Test error message"); var renderer = new TestRenderer(); var rootComponentId = renderer.AssignRootComponentId(rootComponent); await renderer.RenderRootComponentAsync(rootComponentId); // Initally, it rendered one batch and is valid var batch1 = renderer.Batches.Single(); var componentFrame1 = batch1.GetComponentFrames <TestInputComponent <string> >().Single(); var inputComponentId = componentFrame1.ComponentId; var component = (TestInputComponent <string>)componentFrame1.Component; Assert.Equal("invalid", component.CssClass); Assert.NotNull(component.AdditionalAttributes); Assert.Equal(1, component.AdditionalAttributes.Count); Assert.Equal("userSpecifiedValue", component.AdditionalAttributes["aria-invalid"]); }
public async Task UnsubscribesFromValidationStateChangeNotifications() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), ValueExpression = () => model.StringProperty }; var fieldIdentifier = FieldIdentifier.Create(() => model.StringProperty); var renderer = new TestRenderer(); var rootComponentId = renderer.AssignRootComponentId(rootComponent); await renderer.RenderRootComponentAsync(rootComponentId); var component = renderer.Batches.Single().GetComponentFrames <TestInputComponent <string> >().Single().Component; // Act: dispose, then update the field state in the EditContext and notify ((IDisposable)component).Dispose(); var messageStore = new ValidationMessageStore(rootComponent.EditContext); messageStore.Add(fieldIdentifier, "Some message"); await renderer.Dispatcher.InvokeAsync(rootComponent.EditContext.NotifyValidationStateChanged); // Assert: No additional render Assert.Empty(renderer.Batches.Skip(1)); }
public async Task CssClassCombinesClassWithFieldClass() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { AdditionalAttributes = new Dictionary <string, object>() { { "class", "my-class other-class" }, }, EditContext = new EditContext(model), ValueExpression = () => model.StringProperty }; var fieldIdentifier = FieldIdentifier.Create(() => model.StringProperty); // Act/Assert var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); Assert.Equal("valid", inputComponent.FieldClass); Assert.Equal("my-class other-class valid", inputComponent.CssClass); // Act/Assert: Retains custom class when changing field class rootComponent.EditContext.NotifyFieldChanged(fieldIdentifier); Assert.Equal("modified valid", inputComponent.FieldClass); Assert.Equal("my-class other-class modified valid", inputComponent.CssClass); }
public async Task ThrowsIfNoValueExpressionIsSupplied() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model) }; // Act/Assert var ex = await Assert.ThrowsAsync <InvalidOperationException>(() => RenderAndGetTestInputComponentAsync(rootComponent)); Assert.Contains($"{typeof(TestInputComponent<string>)} requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'.", ex.Message); }
public async Task ThrowsIfEditContextChanges() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), ValueExpression = () => model.StringProperty }; await InputRenderer.RenderAndGetComponent(rootComponent); // Act/Assert rootComponent.EditContext = new EditContext(model); var ex = Assert.Throws <InvalidOperationException>(() => rootComponent.TriggerRender()); Assert.StartsWith($"{typeof(TestInputComponent<string>)} does not support changing the EditContext dynamically", ex.Message); }
public async Task SuppliesCurrentValueAsStringWithFormatting() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <DateTime, TestDateInputComponent> { EditContext = new EditContext(model), Value = new DateTime(1915, 3, 2), ValueExpression = () => model.DateProperty }; var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); // Act/Assert Assert.Equal("1915/03/02", inputComponent.CurrentValueAsString); }
public async Task ParsesCurrentValueWhenUsingNotNullableEnumWithEmptyValue() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <TestEnum, TestInputSelect <TestEnum> > { EditContext = new EditContext(model), ValueExpression = () => model.NotNullableEnum }; var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent); // Act inputSelectComponent.CurrentValueAsString = ""; // Assert Assert.Equal(default, inputSelectComponent.CurrentValue);
public async Task InputElementIsAssignedSuccessfully() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, InputTextArea> { EditContext = new EditContext(model), ValueExpression = () => model.StringProperty, }; // Act var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent); // Assert Assert.NotNull(inputSelectComponent.Element); }
public async Task CanRenderWithoutEditContext() { // Arrange var model = new TestModel(); var value = "some value"; var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { Value = value, ValueExpression = () => value }; // Act/Assert var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent); Assert.Null(inputComponent.EditContext); }
public async Task GetsCurrentValueFromValueParameter() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), Value = "some value", ValueExpression = () => model.StringProperty }; // Act var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); // Assert Assert.Equal("some value", inputComponent.CurrentValue); }
public async Task ExposesEditContextToSubclass() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), Value = "some value", ValueExpression = () => model.StringProperty }; // Act var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); // Assert Assert.Same(rootComponent.EditContext, inputComponent.EditContext); }
public async Task ExposesFieldIdentifierToSubclass() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), Value = "some value", ValueExpression = () => model.StringProperty }; // Act var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); // Assert Assert.Equal(FieldIdentifier.Create(() => model.StringProperty), inputComponent.FieldIdentifier); }
public async Task CanReadBackChangesToCurrentValue() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), Value = "initial value", ValueExpression = () => model.StringProperty }; var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); Assert.Equal("initial value", inputComponent.CurrentValue); // Act inputComponent.CurrentValue = "new value"; // Assert Assert.Equal("new value", inputComponent.CurrentValue); }
public async Task WritingToCurrentValueNotifiesEditContext() { // Arrange var model = new TestModel(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), Value = "initial value", ValueExpression = () => model.StringProperty }; var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); Assert.False(rootComponent.EditContext.IsModified(() => model.StringProperty)); // Act inputComponent.CurrentValue = "new value"; // Assert Assert.True(rootComponent.EditContext.IsModified(() => model.StringProperty)); }
public async Task WritingToCurrentValueDoesNotInvokeValueChangedIfUnchanged() { // Arrange var model = new TestModel(); var valueChangedCallLog = new List <string>(); var rootComponent = new TestInputHostComponent <string, TestInputComponent <string> > { EditContext = new EditContext(model), Value = "initial value", ValueChanged = val => valueChangedCallLog.Add(val), ValueExpression = () => model.StringProperty }; var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent); Assert.Empty(valueChangedCallLog); // Act inputComponent.CurrentValue = "initial value"; // Assert Assert.Empty(valueChangedCallLog); }
private static async Task <TComponent> RenderAndGetTestInputComponentAsync <TValue, TComponent>(TestInputHostComponent <TValue, TComponent> hostComponent) where TComponent : TestInputComponent <TValue> { var testRenderer = new TestRenderer(); var componentId = testRenderer.AssignRootComponentId(hostComponent); await testRenderer.RenderRootComponentAsync(componentId); return(FindComponent <TComponent>(testRenderer.Batches.Single())); }