Skip to content

Commit 88cd951

Browse files
csharpfritzCopilot
andcommitted
feat: migration script enhancements + integration test gaps
Script enhancements (bwfc-migrate.ps1): - ConvertFrom-LoginView: auto-converts LoginView to AuthorizeView (AnonymousTemplateNotAuthorized, LoggedInTemplateAuthorized) - ConvertFrom-GetRouteUrl: converts Page.GetRouteUrl calls to BWFC GetRouteUrlHelper pattern with Evalcontext translation - ConvertFrom-SelectMethod: strips SelectMethod attributes and inserts TODO-annotated DI service injection guidance Integration tests (6 new entries): - CheckBoxList, DataPager, ImageButton, ListBox, LoginView, Theming added to ControlSampleTests.cs with matching interactive tests Housekeeping: - .gitignore: exclude samples/FreshWingtipToys/ and feasibility doc - Capture directive about excluded scratch artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 57ce807 commit 88cd951

5 files changed

Lines changed: 402 additions & 3 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
### 2026-03-04: User directive — exclude FreshWingtipToys and feasibility doc
2+
**By:** Jeffrey T. Fritz (via Copilot)
3+
**What:** FreshWingtipToys sample site (samples/FreshWingtipToys/) and the ASPX middleware feasibility doc (planning-docs/ASPX-MIDDLEWARE-FEASIBILITY.md) should NOT be committed to the repo. They are scratch artifacts from the migration benchmarking work.
4+
**Why:** User request — captured for team memory

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@ ASALocalRun/
341341
# MFractors (Xamarin productivity tool) working folder
342342
.mfractor/
343343

344+
# Migration benchmark scratch artifacts
345+
samples/FreshWingtipToys/
346+
planning-docs/ASPX-MIDDLEWARE-FEASIBILITY.md
347+
344348
# macOS
345349
.DS_Store
346350
.AppleDouble

samples/AfterBlazorServerSide.Tests/ControlSampleTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ public ControlSampleTests(PlaywrightFixture fixture)
2424
[InlineData("/ControlSamples/CheckBox")]
2525
[InlineData("/ControlSamples/CheckBox/Events")]
2626
[InlineData("/ControlSamples/CheckBox/Style")]
27+
[InlineData("/ControlSamples/CheckBoxList")]
2728
[InlineData("/ControlSamples/DropDownList")]
2829
[InlineData("/ControlSamples/FileUpload")]
2930
[InlineData("/ControlSamples/HiddenField")]
3031
[InlineData("/ControlSamples/HyperLink")]
3132
[InlineData("/ControlSamples/Image")]
33+
[InlineData("/ControlSamples/ImageButton")]
3234
[InlineData("/ControlSamples/LinkButton")]
3335
[InlineData("/ControlSamples/LinkButton/JavaScript")]
36+
[InlineData("/ControlSamples/ListBox")]
3437
[InlineData("/ControlSamples/Literal")]
3538
[InlineData("/ControlSamples/Localize")]
3639
[InlineData("/ControlSamples/MultiView")]
@@ -59,6 +62,7 @@ public async Task EditorControl_Loads_WithoutErrors(string path)
5962
[InlineData("/ControlSamples/DataList/FooterStyle")]
6063
[InlineData("/ControlSamples/DataList/HeaderStyle")]
6164
[InlineData("/ControlSamples/DataList/ComplexStyle")]
65+
[InlineData("/ControlSamples/DataPager")]
6266
[InlineData("/ControlSamples/Repeater")]
6367
[InlineData("/ControlSamples/ListView")]
6468
[InlineData("/ControlSamples/ListView/ItemDataBound")]
@@ -184,6 +188,7 @@ public async Task ValidationControl_Loads_WithoutErrors(string path)
184188
[InlineData("/ControlSamples/LoginName")]
185189
[InlineData("/ControlSamples/LoginStatusAuthenticated")]
186190
[InlineData("/ControlSamples/LoginStatusNotAuthenticated")]
191+
[InlineData("/ControlSamples/LoginView")]
187192
[InlineData("/ControlSamples/ChangePassword")]
188193
[InlineData("/ControlSamples/CreateUserWizard")]
189194
[InlineData("/ControlSamples/PasswordRecovery")]
@@ -215,6 +220,7 @@ public async Task ChartControl_Loads_AndRendersContent(string path)
215220
// Utility Features
216221
[Theory]
217222
[InlineData("/ControlSamples/DataBinder")]
223+
[InlineData("/ControlSamples/Theming")]
218224
[InlineData("/ControlSamples/ViewState")]
219225
public async Task UtilityFeature_Loads_WithoutErrors(string path)
220226
{

samples/AfterBlazorServerSide.Tests/InteractiveComponentTests.cs

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2923,6 +2923,248 @@ public async Task ModelErrorMessage_ValidSubmit_NoErrors()
29232923
}
29242924
}
29252925

2926+
[Fact]
2927+
public async Task CheckBoxList_CheckItem_UpdatesSelection()
2928+
{
2929+
var page = await _fixture.NewPageAsync();
2930+
var consoleErrors = new List<string>();
2931+
page.Console += (_, msg) =>
2932+
{
2933+
if (msg.Type == "error" && !System.Text.RegularExpressions.Regex.IsMatch(msg.Text, @"^\[\d{4}-\d{2}-\d{2}T"))
2934+
consoleErrors.Add(msg.Text);
2935+
};
2936+
2937+
try
2938+
{
2939+
await page.GotoAsync($"{_fixture.BaseUrl}/ControlSamples/CheckBoxList", new PageGotoOptions
2940+
{
2941+
WaitUntil = WaitUntilState.NetworkIdle,
2942+
Timeout = 30000
2943+
});
2944+
2945+
// Find checkboxes in the first CheckBoxList
2946+
var checkboxes = await page.Locator("[data-audit-control='CheckBoxList-1'] input[type='checkbox']").AllAsync();
2947+
Assert.NotEmpty(checkboxes);
2948+
2949+
// Click the first checkbox
2950+
var firstCheckbox = page.Locator("[data-audit-control='CheckBoxList-1'] input[type='checkbox']").First;
2951+
await firstCheckbox.ClickAsync();
2952+
await page.WaitForTimeoutAsync(300);
2953+
2954+
// Verify it's checked
2955+
var isChecked = await firstCheckbox.IsCheckedAsync();
2956+
Assert.True(isChecked, "Checkbox should be checked after clicking");
2957+
2958+
Assert.Empty(consoleErrors);
2959+
}
2960+
finally
2961+
{
2962+
await page.CloseAsync();
2963+
}
2964+
}
2965+
2966+
[Fact]
2967+
public async Task DataPager_ClickPage_ChangesDisplayedItems()
2968+
{
2969+
var page = await _fixture.NewPageAsync();
2970+
var consoleErrors = new List<string>();
2971+
page.Console += (_, msg) =>
2972+
{
2973+
if (msg.Type == "error" && !System.Text.RegularExpressions.Regex.IsMatch(msg.Text, @"^\[\d{4}-\d{2}-\d{2}T"))
2974+
consoleErrors.Add(msg.Text);
2975+
};
2976+
2977+
try
2978+
{
2979+
await page.GotoAsync($"{_fixture.BaseUrl}/ControlSamples/DataPager", new PageGotoOptions
2980+
{
2981+
WaitUntil = WaitUntilState.NetworkIdle,
2982+
Timeout = 30000
2983+
});
2984+
2985+
// Capture initial table content
2986+
var initialContent = await page.Locator("[data-audit-control='DataPager'] tbody").TextContentAsync();
2987+
Assert.NotNull(initialContent);
2988+
2989+
// Find pager buttons and click a page number (skip first/prev, look for numeric)
2990+
var pagerButtons = await page.Locator("[data-audit-control='DataPager'] a, [data-audit-control='DataPager'] button").AllAsync();
2991+
if (pagerButtons.Count > 1)
2992+
{
2993+
// Click the second pager link to navigate to page 2
2994+
await pagerButtons[1].ClickAsync();
2995+
await page.WaitForTimeoutAsync(500);
2996+
2997+
// Verify content changed
2998+
var newContent = await page.Locator("[data-audit-control='DataPager'] tbody").TextContentAsync();
2999+
Assert.NotEqual(initialContent, newContent);
3000+
}
3001+
3002+
Assert.Empty(consoleErrors);
3003+
}
3004+
finally
3005+
{
3006+
await page.CloseAsync();
3007+
}
3008+
}
3009+
3010+
[Fact]
3011+
public async Task ImageButton_Click_IncrementsCounter()
3012+
{
3013+
var page = await _fixture.NewPageAsync();
3014+
var consoleErrors = new List<string>();
3015+
page.Console += (_, msg) =>
3016+
{
3017+
if (msg.Type == "error")
3018+
{
3019+
if (!System.Text.RegularExpressions.Regex.IsMatch(msg.Text, @"^\[\d{4}-\d{2}-\d{2}T")
3020+
&& !msg.Text.StartsWith("Failed to load resource"))
3021+
consoleErrors.Add(msg.Text);
3022+
}
3023+
};
3024+
3025+
try
3026+
{
3027+
await page.GotoAsync($"{_fixture.BaseUrl}/ControlSamples/ImageButton", new PageGotoOptions
3028+
{
3029+
WaitUntil = WaitUntilState.NetworkIdle,
3030+
Timeout = 30000
3031+
});
3032+
3033+
// Find the first ImageButton (renders as <input type="image">)
3034+
var imageButton = page.Locator("[data-audit-control='ImageButton-1'] input[type='image']").First;
3035+
await imageButton.WaitForAsync(new() { Timeout = 5000 });
3036+
3037+
// Click the image button
3038+
await imageButton.ClickAsync();
3039+
await page.WaitForTimeoutAsync(500);
3040+
3041+
// Verify click count incremented
3042+
var countText = await page.Locator("[data-audit-control='ImageButton-1'] + p strong").TextContentAsync();
3043+
Assert.NotNull(countText);
3044+
Assert.Equal("1", countText);
3045+
3046+
Assert.Empty(consoleErrors);
3047+
}
3048+
finally
3049+
{
3050+
await page.CloseAsync();
3051+
}
3052+
}
3053+
3054+
[Fact]
3055+
public async Task ListBox_Selection_Works()
3056+
{
3057+
var page = await _fixture.NewPageAsync();
3058+
var consoleErrors = new List<string>();
3059+
page.Console += (_, msg) =>
3060+
{
3061+
if (msg.Type == "error" && !System.Text.RegularExpressions.Regex.IsMatch(msg.Text, @"^\[\d{4}-\d{2}-\d{2}T"))
3062+
consoleErrors.Add(msg.Text);
3063+
};
3064+
3065+
try
3066+
{
3067+
await page.GotoAsync($"{_fixture.BaseUrl}/ControlSamples/ListBox", new PageGotoOptions
3068+
{
3069+
WaitUntil = WaitUntilState.NetworkIdle,
3070+
Timeout = 30000
3071+
});
3072+
3073+
// Find the first ListBox (renders as <select>)
3074+
var listBox = page.Locator("[data-audit-control='ListBox-1'] select").First;
3075+
await listBox.WaitForAsync(new() { Timeout = 5000 });
3076+
3077+
// Check it has options
3078+
var options = await listBox.Locator("option").AllAsync();
3079+
Assert.NotEmpty(options);
3080+
3081+
// Select the second option
3082+
if (options.Count > 1)
3083+
{
3084+
await listBox.SelectOptionAsync(new SelectOptionValue { Index = 1 });
3085+
await page.WaitForTimeoutAsync(300);
3086+
3087+
// Verify selection changed
3088+
var selectedValue = await listBox.EvaluateAsync<string>("el => el.value");
3089+
Assert.NotNull(selectedValue);
3090+
Assert.NotEmpty(selectedValue);
3091+
}
3092+
3093+
Assert.Empty(consoleErrors);
3094+
}
3095+
finally
3096+
{
3097+
await page.CloseAsync();
3098+
}
3099+
}
3100+
3101+
[Fact]
3102+
public async Task LoginView_AnonymousTemplate_RendersContent()
3103+
{
3104+
var page = await _fixture.NewPageAsync();
3105+
var consoleErrors = new List<string>();
3106+
page.Console += (_, msg) =>
3107+
{
3108+
if (msg.Type == "error" && !System.Text.RegularExpressions.Regex.IsMatch(msg.Text, @"^\[\d{4}-\d{2}-\d{2}T"))
3109+
consoleErrors.Add(msg.Text);
3110+
};
3111+
3112+
try
3113+
{
3114+
await page.GotoAsync($"{_fixture.BaseUrl}/ControlSamples/LoginView", new PageGotoOptions
3115+
{
3116+
WaitUntil = WaitUntilState.NetworkIdle,
3117+
Timeout = 30000
3118+
});
3119+
3120+
// LoginView should render the AnonymousTemplate since user is not authenticated
3121+
var content = await page.Locator("[data-audit-control='LoginView-1']").TextContentAsync();
3122+
Assert.NotNull(content);
3123+
Assert.Contains("not logged in", content);
3124+
3125+
Assert.Empty(consoleErrors);
3126+
}
3127+
finally
3128+
{
3129+
await page.CloseAsync();
3130+
}
3131+
}
3132+
3133+
[Fact]
3134+
public async Task Theming_ThemeProvider_AppliesStyles()
3135+
{
3136+
var page = await _fixture.NewPageAsync();
3137+
var consoleErrors = new List<string>();
3138+
page.Console += (_, msg) =>
3139+
{
3140+
if (msg.Type == "error" && !System.Text.RegularExpressions.Regex.IsMatch(msg.Text, @"^\[\d{4}-\d{2}-\d{2}T"))
3141+
consoleErrors.Add(msg.Text);
3142+
};
3143+
3144+
try
3145+
{
3146+
await page.GotoAsync($"{_fixture.BaseUrl}/ControlSamples/Theming", new PageGotoOptions
3147+
{
3148+
WaitUntil = WaitUntilState.NetworkIdle,
3149+
Timeout = 30000
3150+
});
3151+
3152+
// Verify themed buttons are present with inline styles applied by ThemeProvider
3153+
var themedButtons = await page.Locator("button, input[type='submit']").AllAsync();
3154+
Assert.NotEmpty(themedButtons);
3155+
3156+
// Verify the page has demo containers with themed content
3157+
var demoContainers = await page.Locator(".demo-container").AllAsync();
3158+
Assert.True(demoContainers.Count >= 5, "Expected at least 5 demo sections on the Theming page");
3159+
3160+
Assert.Empty(consoleErrors);
3161+
}
3162+
finally
3163+
{
3164+
await page.CloseAsync();
3165+
}
3166+
}
3167+
29263168
[Fact]
29273169
public async Task ModelErrorMessage_ClearButton_RemovesErrors()
29283170
{

0 commit comments

Comments
 (0)