Skip to content

Commit 7b0a272

Browse files
committed
Add test for overridden attributes
1 parent 6585dad commit 7b0a272

File tree

5 files changed

+69
-15
lines changed

5 files changed

+69
-15
lines changed

src/GovUk.Frontend.AspNetCore/TagHelpers/CheckboxesErrorMessageTagHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ private protected override void SetErrorMessage(TagHelperContent? content, TagHe
3333
var attributes = new AttributeCollection(output.Attributes);
3434

3535
checkboxesContext.SetErrorMessage(
36-
VisuallyHiddenText,
36+
VisuallyHiddenText is not null ? new TemplateString(VisuallyHiddenText) : null,
3737
attributes,
3838
content?.ToTemplateString(),
3939
output.TagName);

src/GovUk.Frontend.AspNetCore/TagHelpers/CheckboxesItemTagHelper.cs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,9 @@ bool DoesModelMatchItemValue()
181181

182182
if (modelExpression.Metadata.IsEnumerableType)
183183
{
184-
// Try to get the value from ModelState first, but fall back to model if ModelState doesn't have a valid enumerable value
185-
var value = model;
186-
187-
if (ViewContext!.ModelState.TryGetValue(modelExpression.Name, out var entry) && entry.RawValue != null)
188-
{
189-
// Only use RawValue if it looks like it's an enumerable collection
190-
if (entry.RawValue is IEnumerable && !(entry.RawValue is string))
191-
{
192-
value = entry.RawValue;
193-
}
194-
}
184+
var value = ViewContext!.ModelState.TryGetValue(modelExpression.Name, out var entry) ?
185+
entry.RawValue :
186+
model;
195187

196188
var values = (value as IEnumerable)?.Cast<object>();
197189
return values?.Any(v => v?.ToString() == Value) is true;

src/GovUk.Frontend.AspNetCore/TagHelpers/CheckboxesTagHelper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ internal CheckboxesTagHelper(IComponentGenerator componentGenerator, IModelHelpe
107107
/// <inheritdoc/>
108108
public override void Init(TagHelperContext context)
109109
{
110-
context.SetContextItem(new CheckboxesContext(Name, For));
110+
var checkboxesContext = new CheckboxesContext(Name, For);
111+
context.SetContextItem(checkboxesContext);
112+
context.SetContextItem<FormGroupContext3>(checkboxesContext);
111113
}
112114

113115
/// <inheritdoc/>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@model GovUk.Frontend.AspNetCore.IntegrationTests.TagHelperModelBindingTests.CheckboxesTestsModel
2+
3+
<govuk-checkboxes for="Options" id-prefix="OverriddenIdPrefix" name="OverriddenName">
4+
<govuk-checkboxes-fieldset>
5+
<govuk-checkboxes-fieldset-legend>
6+
Overridden legend
7+
</govuk-checkboxes-fieldset-legend>
8+
<govuk-checkboxes-hint>Overridden hint</govuk-checkboxes-hint>
9+
<govuk-checkboxes-error-message>Overridden error message</govuk-checkboxes-error-message>
10+
<govuk-checkboxes-item value="option1" checked="true">Option 1</govuk-checkboxes-item>
11+
<govuk-checkboxes-item value="option2" checked="false">Option 2</govuk-checkboxes-item>
12+
</govuk-checkboxes-fieldset>
13+
</govuk-checkboxes>

tests/GovUk.Frontend.AspNetCore.IntegrationTests/TagHelperModelBindingTests/Tests.cs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.ComponentModel.DataAnnotations;
22
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.EntityFrameworkCore;
34

45
namespace GovUk.Frontend.AspNetCore.IntegrationTests.TagHelperModelBindingTests;
56

@@ -362,7 +363,6 @@ public async Task Checkboxes_RendersCorrectly()
362363
var checkbox2 = page.Locator("input[value='option2']").First;
363364
Assert.Equal("Options", await checkbox2.GetAttributeAsync("name"));
364365
Assert.Equal("Options-2", await checkbox2.GetAttributeAsync("id"));
365-
// This is the critical assertion that should pass with the fix
366366
Assert.True(await checkbox2.IsCheckedAsync());
367367

368368
var hint = page.Locator(".govuk-hint").First;
@@ -371,6 +371,29 @@ public async Task Checkboxes_RendersCorrectly()
371371
var errorMessage = page.Locator(".govuk-error-message").First;
372372
Assert.Equal("Error: Model error message", await errorMessage.TextContentAsync());
373373
}
374+
375+
[Fact]
376+
public async Task CheckboxesOverridden_RendersCorrectly()
377+
{
378+
var page = await fixture.Browser!.NewPageAsync();
379+
await page.GotoAsync($"{ServerFixture.BaseUrl}/ModelBindingTests/CheckboxesOverridden");
380+
381+
var checkbox1 = page.Locator("input[value='option1']").First;
382+
Assert.Equal("OverriddenName", await checkbox1.GetAttributeAsync("name"));
383+
Assert.Equal("OverriddenIdPrefix", await checkbox1.GetAttributeAsync("id"));
384+
Assert.True(await checkbox1.IsCheckedAsync());
385+
386+
var checkbox2 = page.Locator("input[value='option2']").First;
387+
Assert.Equal("OverriddenName", await checkbox2.GetAttributeAsync("name"));
388+
Assert.Equal("OverriddenIdPrefix-2", await checkbox2.GetAttributeAsync("id"));
389+
Assert.False(await checkbox2.IsCheckedAsync());
390+
391+
var hint = page.Locator(".govuk-hint").First;
392+
Assert.Equal("Overridden hint", await hint.InnerTextAsync());
393+
394+
var errorMessage = page.Locator(".govuk-error-message").First;
395+
Assert.Equal("Error: Overridden error message", await errorMessage.TextContentAsync());
396+
}
374397
}
375398

376399
[Route("/[controller]/[action]")]
@@ -379,111 +402,135 @@ public class ModelBindingTestsController : Controller
379402
[HttpGet]
380403
public IActionResult TextInput()
381404
{
405+
ModelState.SetModelValue(nameof(TextInputTestsModel.Text), "Model value", "Model value");
382406
ModelState.AddModelError(nameof(TextInputTestsModel.Text), "Model error message");
383407
return View(new TextInputTestsModel { Text = "Model value" });
384408
}
385409

386410
[HttpGet]
387411
public IActionResult TextInputOverridden()
388412
{
413+
ModelState.SetModelValue(nameof(TextInputTestsModel.Text), "Model value", "Model value");
389414
ModelState.AddModelError(nameof(TextInputTestsModel.Text), "Model error message");
390415
return View(new TextInputTestsModel { Text = "Model value" });
391416
}
392417

393418
[HttpGet]
394419
public IActionResult PasswordInput()
395420
{
421+
ModelState.SetModelValue(nameof(PasswordInputTestsModel.Password), "Model value", "Model value");
396422
ModelState.AddModelError(nameof(PasswordInputTestsModel.Password), "Model error message");
397423
return View(new PasswordInputTestsModel { Password = "Model value" });
398424
}
399425

400426
[HttpGet]
401427
public IActionResult PasswordInputOverridden()
402428
{
429+
ModelState.SetModelValue(nameof(PasswordInputTestsModel.Password), "Model value", "Model value");
403430
ModelState.AddModelError(nameof(PasswordInputTestsModel.Password), "Model error message");
404431
return View(new PasswordInputTestsModel { Password = "Model value" });
405432
}
406433

407434
[HttpGet]
408435
public IActionResult DateInput()
409436
{
437+
ModelState.SetModelValue(nameof(DateInputTestsModel.Date), new DateOnly(2024, 3, 15), "15,3,2024");
410438
ModelState.AddModelError(nameof(DateInputTestsModel.Date), "Model error message");
411439
return View(new DateInputTestsModel { Date = new DateOnly(2024, 3, 15) });
412440
}
413441

414442
[HttpGet]
415443
public IActionResult DateInputOverridden()
416444
{
445+
ModelState.SetModelValue(nameof(DateInputTestsModel.Date), new DateOnly(2024, 3, 15), "15,3,2024");
417446
ModelState.AddModelError(nameof(DateInputTestsModel.Date), "Model error message");
418447
return View(new DateInputTestsModel { Date = new DateOnly(2024, 3, 15) });
419448
}
420449

421450
[HttpGet]
422451
public IActionResult DateInputOverriddenItems()
423452
{
453+
ModelState.SetModelValue(nameof(DateInputTestsModel.Date), new DateOnly(2024, 3, 15), "15,3,2024");
424454
ModelState.AddModelError(nameof(DateInputTestsModel.Date), "Model error message");
425455
return View(new DateInputTestsModel { Date = new DateOnly(2024, 3, 15) });
426456
}
427457

428458
[HttpGet]
429459
public IActionResult Textarea()
430460
{
461+
ModelState.SetModelValue(nameof(TextareaTestsModel.Text), "Model value", "Model value");
431462
ModelState.AddModelError(nameof(TextareaTestsModel.Text), "Model error message");
432463
return View(new TextareaTestsModel { Text = "Model value" });
433464
}
434465

435466
[HttpGet]
436467
public IActionResult TextareaOverridden()
437468
{
469+
ModelState.SetModelValue(nameof(TextareaTestsModel.Text), "Model value", "Model value");
438470
ModelState.AddModelError(nameof(TextareaTestsModel.Text), "Model error message");
439471
return View(new TextareaTestsModel { Text = "Model value" });
440472
}
441473

442474
[HttpGet]
443475
public IActionResult CharacterCount()
444476
{
477+
ModelState.SetModelValue(nameof(CharacterCountTestsModel.Text), "Model value", "Model value");
445478
ModelState.AddModelError(nameof(CharacterCountTestsModel.Text), "Model error message");
446479
return View(new CharacterCountTestsModel { Text = "Model value" });
447480
}
448481

449482
[HttpGet]
450483
public IActionResult CharacterCountOverridden()
451484
{
485+
ModelState.SetModelValue(nameof(CharacterCountTestsModel.Text), "Model value", "Model value");
452486
ModelState.AddModelError(nameof(CharacterCountTestsModel.Text), "Model error message");
453487
return View(new CharacterCountTestsModel { Text = "Model value" });
454488
}
455489

456490
[HttpGet]
457491
public IActionResult FileUpload()
458492
{
493+
ModelState.SetModelValue(nameof(FileUploadTestsModel.File), "", "");
459494
ModelState.AddModelError(nameof(FileUploadTestsModel.File), "Model error message");
460495
return View(new FileUploadTestsModel());
461496
}
462497

463498
[HttpGet]
464499
public IActionResult FileUploadOverridden()
465500
{
501+
ModelState.SetModelValue(nameof(FileUploadTestsModel.File), "", "");
466502
ModelState.AddModelError(nameof(FileUploadTestsModel.File), "Model error message");
467503
return View(new FileUploadTestsModel());
468504
}
469505

470506
[HttpGet]
471507
public IActionResult Select()
472508
{
509+
ModelState.SetModelValue(nameof(SelectTestsModel.Option), "option2", "option2");
473510
ModelState.AddModelError(nameof(SelectTestsModel.Option), "Model error message");
474511
return View(new SelectTestsModel { Option = "option2" });
475512
}
476513

477514
[HttpGet]
478515
public IActionResult SelectOverridden()
479516
{
517+
ModelState.SetModelValue(nameof(SelectTestsModel.Option), "option2", "option2");
480518
ModelState.AddModelError(nameof(SelectTestsModel.Option), "Model error message");
481519
return View(new SelectTestsModel { Option = "option2" });
482520
}
483521

484522
[HttpGet]
485523
public IActionResult Checkboxes()
486524
{
525+
ModelState.SetModelValue(nameof(CheckboxesTestsModel.Options), new[] { "option2" }, null);
526+
ModelState.AddModelError(nameof(CheckboxesTestsModel.Options), "Model error message");
527+
return View(new CheckboxesTestsModel { Options = ["option2"] });
528+
}
529+
530+
[HttpGet]
531+
public IActionResult CheckboxesOverridden()
532+
{
533+
ModelState.SetModelValue(nameof(CheckboxesTestsModel.Options), new[] { "option2" }, null);
487534
ModelState.AddModelError(nameof(CheckboxesTestsModel.Options), "Model error message");
488535
return View(new CheckboxesTestsModel { Options = ["option2"] });
489536
}
@@ -522,7 +569,7 @@ public record CharacterCountTestsModel
522569
public record FileUploadTestsModel
523570
{
524571
[Display(Name = "ModelMetadata display name", Description = "ModelMetadata description")]
525-
public Microsoft.AspNetCore.Http.IFormFile? File { get; set; }
572+
public IFormFile? File { get; set; }
526573
}
527574

528575
public record SelectTestsModel

0 commit comments

Comments
 (0)