Skip to content

Commit 46cf124

Browse files
committed
Add test for overridden attributes
1 parent 6585dad commit 46cf124

File tree

5 files changed

+68
-15
lines changed

5 files changed

+68
-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: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@ public async Task Checkboxes_RendersCorrectly()
362362
var checkbox2 = page.Locator("input[value='option2']").First;
363363
Assert.Equal("Options", await checkbox2.GetAttributeAsync("name"));
364364
Assert.Equal("Options-2", await checkbox2.GetAttributeAsync("id"));
365-
// This is the critical assertion that should pass with the fix
366365
Assert.True(await checkbox2.IsCheckedAsync());
367366

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

376398
[Route("/[controller]/[action]")]
@@ -379,111 +401,135 @@ public class ModelBindingTestsController : Controller
379401
[HttpGet]
380402
public IActionResult TextInput()
381403
{
404+
ModelState.SetModelValue(nameof(TextInputTestsModel.Text), "Model value", "Model value");
382405
ModelState.AddModelError(nameof(TextInputTestsModel.Text), "Model error message");
383406
return View(new TextInputTestsModel { Text = "Model value" });
384407
}
385408

386409
[HttpGet]
387410
public IActionResult TextInputOverridden()
388411
{
412+
ModelState.SetModelValue(nameof(TextInputTestsModel.Text), "Model value", "Model value");
389413
ModelState.AddModelError(nameof(TextInputTestsModel.Text), "Model error message");
390414
return View(new TextInputTestsModel { Text = "Model value" });
391415
}
392416

393417
[HttpGet]
394418
public IActionResult PasswordInput()
395419
{
420+
ModelState.SetModelValue(nameof(PasswordInputTestsModel.Password), "Model value", "Model value");
396421
ModelState.AddModelError(nameof(PasswordInputTestsModel.Password), "Model error message");
397422
return View(new PasswordInputTestsModel { Password = "Model value" });
398423
}
399424

400425
[HttpGet]
401426
public IActionResult PasswordInputOverridden()
402427
{
428+
ModelState.SetModelValue(nameof(PasswordInputTestsModel.Password), "Model value", "Model value");
403429
ModelState.AddModelError(nameof(PasswordInputTestsModel.Password), "Model error message");
404430
return View(new PasswordInputTestsModel { Password = "Model value" });
405431
}
406432

407433
[HttpGet]
408434
public IActionResult DateInput()
409435
{
436+
ModelState.SetModelValue(nameof(DateInputTestsModel.Date), new DateOnly(2024, 3, 15), "15,3,2024");
410437
ModelState.AddModelError(nameof(DateInputTestsModel.Date), "Model error message");
411438
return View(new DateInputTestsModel { Date = new DateOnly(2024, 3, 15) });
412439
}
413440

414441
[HttpGet]
415442
public IActionResult DateInputOverridden()
416443
{
444+
ModelState.SetModelValue(nameof(DateInputTestsModel.Date), new DateOnly(2024, 3, 15), "15,3,2024");
417445
ModelState.AddModelError(nameof(DateInputTestsModel.Date), "Model error message");
418446
return View(new DateInputTestsModel { Date = new DateOnly(2024, 3, 15) });
419447
}
420448

421449
[HttpGet]
422450
public IActionResult DateInputOverriddenItems()
423451
{
452+
ModelState.SetModelValue(nameof(DateInputTestsModel.Date), new DateOnly(2024, 3, 15), "15,3,2024");
424453
ModelState.AddModelError(nameof(DateInputTestsModel.Date), "Model error message");
425454
return View(new DateInputTestsModel { Date = new DateOnly(2024, 3, 15) });
426455
}
427456

428457
[HttpGet]
429458
public IActionResult Textarea()
430459
{
460+
ModelState.SetModelValue(nameof(TextareaTestsModel.Text), "Model value", "Model value");
431461
ModelState.AddModelError(nameof(TextareaTestsModel.Text), "Model error message");
432462
return View(new TextareaTestsModel { Text = "Model value" });
433463
}
434464

435465
[HttpGet]
436466
public IActionResult TextareaOverridden()
437467
{
468+
ModelState.SetModelValue(nameof(TextareaTestsModel.Text), "Model value", "Model value");
438469
ModelState.AddModelError(nameof(TextareaTestsModel.Text), "Model error message");
439470
return View(new TextareaTestsModel { Text = "Model value" });
440471
}
441472

442473
[HttpGet]
443474
public IActionResult CharacterCount()
444475
{
476+
ModelState.SetModelValue(nameof(CharacterCountTestsModel.Text), "Model value", "Model value");
445477
ModelState.AddModelError(nameof(CharacterCountTestsModel.Text), "Model error message");
446478
return View(new CharacterCountTestsModel { Text = "Model value" });
447479
}
448480

449481
[HttpGet]
450482
public IActionResult CharacterCountOverridden()
451483
{
484+
ModelState.SetModelValue(nameof(CharacterCountTestsModel.Text), "Model value", "Model value");
452485
ModelState.AddModelError(nameof(CharacterCountTestsModel.Text), "Model error message");
453486
return View(new CharacterCountTestsModel { Text = "Model value" });
454487
}
455488

456489
[HttpGet]
457490
public IActionResult FileUpload()
458491
{
492+
ModelState.SetModelValue(nameof(FileUploadTestsModel.File), "", "");
459493
ModelState.AddModelError(nameof(FileUploadTestsModel.File), "Model error message");
460494
return View(new FileUploadTestsModel());
461495
}
462496

463497
[HttpGet]
464498
public IActionResult FileUploadOverridden()
465499
{
500+
ModelState.SetModelValue(nameof(FileUploadTestsModel.File), "", "");
466501
ModelState.AddModelError(nameof(FileUploadTestsModel.File), "Model error message");
467502
return View(new FileUploadTestsModel());
468503
}
469504

470505
[HttpGet]
471506
public IActionResult Select()
472507
{
508+
ModelState.SetModelValue(nameof(SelectTestsModel.Option), "option2", "option2");
473509
ModelState.AddModelError(nameof(SelectTestsModel.Option), "Model error message");
474510
return View(new SelectTestsModel { Option = "option2" });
475511
}
476512

477513
[HttpGet]
478514
public IActionResult SelectOverridden()
479515
{
516+
ModelState.SetModelValue(nameof(SelectTestsModel.Option), "option2", "option2");
480517
ModelState.AddModelError(nameof(SelectTestsModel.Option), "Model error message");
481518
return View(new SelectTestsModel { Option = "option2" });
482519
}
483520

484521
[HttpGet]
485522
public IActionResult Checkboxes()
486523
{
524+
ModelState.SetModelValue(nameof(CheckboxesTestsModel.Options), new[] { "option2" }, null);
525+
ModelState.AddModelError(nameof(CheckboxesTestsModel.Options), "Model error message");
526+
return View(new CheckboxesTestsModel { Options = ["option2"] });
527+
}
528+
529+
[HttpGet]
530+
public IActionResult CheckboxesOverridden()
531+
{
532+
ModelState.SetModelValue(nameof(CheckboxesTestsModel.Options), new[] { "option2" }, null);
487533
ModelState.AddModelError(nameof(CheckboxesTestsModel.Options), "Model error message");
488534
return View(new CheckboxesTestsModel { Options = ["option2"] });
489535
}
@@ -522,7 +568,7 @@ public record CharacterCountTestsModel
522568
public record FileUploadTestsModel
523569
{
524570
[Display(Name = "ModelMetadata display name", Description = "ModelMetadata description")]
525-
public Microsoft.AspNetCore.Http.IFormFile? File { get; set; }
571+
public IFormFile? File { get; set; }
526572
}
527573

528574
public record SelectTestsModel

0 commit comments

Comments
 (0)