Skip to content

Commit 8dda188

Browse files
Copilotcsharpfritzhishamcodependabot[bot]
authored
Fix AdRotator deployment failure - add CopyToOutputDirectory for Ads.xml and integration tests (#290)
Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Jeffrey T. Fritz <csharpfritz@users.noreply.github.com> Co-authored-by: hishamco <hishamco_2007@yahoo.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent 914f065 commit 8dda188

9 files changed

Lines changed: 577 additions & 22 deletions

File tree

.github/skills/component-development/SKILL.md

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ This skill covers creating new Blazor components that emulate ASP.NET Web Forms
2525
- `BaseStyledComponent` - Components with styling
2626
- `DataBoundComponent<T>` - Data-bound components
2727
5. **Add unit tests** in `src/BlazorWebFormsComponents.Test/{ComponentName}/`
28-
6. **Add sample page** in `samples/AfterBlazorServerSide/Pages/ControlSamples/`
29-
7. **Create documentation** in `docs/{Category}/{ComponentName}.md`
30-
8. **Update `mkdocs.yml`** and `README.md`
28+
6. **Add sample page** in `samples/AfterBlazorServerSide/Components/Pages/ControlSamples/{ComponentName}/`
29+
7. **Add integration tests** using Playwright in `samples/AfterBlazorServerSide.Tests/`
30+
8. **Create documentation** in `docs/{Category}/{ComponentName}.md`
31+
9. **Update navigation:**
32+
- Add to `samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor` (TreeView)
33+
- Add to `samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor` (home page catalog)
34+
- Update `mkdocs.yml` and `README.md`
3135

3236
### Base Class Selection
3337

@@ -54,3 +58,47 @@ Prefix with `On`:
5458
- `OnCommand` for command events
5559
- `OnSelectedIndexChanged` for selection changes
5660
- `OnDataBinding` for data binding events
61+
62+
### Integration Testing Requirements
63+
64+
Every component must have integration tests in `samples/AfterBlazorServerSide.Tests/` using Playwright:
65+
66+
1. **Page load test** in `ControlSampleTests.cs`:
67+
- Add route to the appropriate `[Theory]` test (EditorControl, DataControl, etc.)
68+
- Verifies page loads without console errors or page errors
69+
70+
2. **Interactive test** in `InteractiveComponentTests.cs` (for interactive components):
71+
- Test user interactions (clicks, input, selection changes)
72+
- Verify component responds correctly to user actions
73+
- Assert no console errors during interaction
74+
75+
Example page load test entry:
76+
```csharp
77+
[Theory]
78+
[InlineData("/ControlSamples/YourComponent")]
79+
public async Task EditorControl_Loads_WithoutErrors(string path)
80+
```
81+
82+
Example interactive test:
83+
```csharp
84+
[Fact]
85+
public async Task YourComponent_Interaction_Works()
86+
{
87+
var page = await _fixture.NewPageAsync();
88+
try
89+
{
90+
await page.GotoAsync($"{_fixture.BaseUrl}/ControlSamples/YourComponent");
91+
// Test interactions...
92+
// Assert expected behavior...
93+
}
94+
finally
95+
{
96+
await page.CloseAsync();
97+
}
98+
}
99+
```
100+
101+
Run integration tests with:
102+
```bash
103+
dotnet test samples/AfterBlazorServerSide.Tests
104+
```

.github/skills/documentation/SKILL.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,6 @@ samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Label/Index.razor
689689
# 6. Update README.md
690690
- [Label](docs/EditorControls/Label.md)
691691
```
692-
693692
## Quality Checklist
694693

695694
Before submitting documentation:
@@ -704,6 +703,13 @@ Before submitting documentation:
704703
- [ ] Spell-checked
705704

706705
Before submitting sample pages:
706+
- [ ] Includes both demo and source code sections
707+
- [ ] Code block exactly matches the demo
708+
- [ ] HTML entities properly encoded in code block
709+
- [ ] `@` symbols doubled in code block
710+
- [ ] Includes complete `@code` block with all handlers
711+
- [ ] Brief description explains what sample demonstrates
712+
- [ ] Sample is accessible from navigation or component list
707713
- [ ] Created in correct folder: `ControlSamples/[ComponentName]/`
708714
- [ ] Follows naming convention (Index.razor for main sample)
709715
- [ ] Includes `@page` directive with correct route

.github/workflows/demo.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Build Demo Sites
2+
3+
on:
4+
push:
5+
branches:
6+
- 'main'
7+
- 'v*'
8+
paths:
9+
- 'src/**'
10+
- 'samples/**'
11+
- '.github/workflows/demo.yml'
12+
pull_request:
13+
branches:
14+
- 'main'
15+
- 'v*'
16+
paths:
17+
- 'src/**'
18+
- 'samples/**'
19+
- '.github/workflows/demo.yml'
20+
workflow_run:
21+
workflows: ["Integration Tests"]
22+
types:
23+
- completed
24+
25+
jobs:
26+
build-demos:
27+
runs-on: ubuntu-latest
28+
# Only run if integration tests passed (or if triggered directly without workflow_run)
29+
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
30+
31+
steps:
32+
- name: Checkout
33+
uses: actions/checkout@v4
34+
with:
35+
fetch-depth: 0
36+
37+
- name: Setup .NET
38+
uses: actions/setup-dotnet@v4
39+
with:
40+
dotnet-version: '10.0.x'
41+
42+
- name: Restore dependencies
43+
run: |
44+
dotnet restore samples/AfterBlazorServerSide/AfterBlazorServerSide.csproj
45+
dotnet restore samples/AfterBlazorClientSide/AfterBlazorClientSide.csproj
46+
47+
- name: Publish Server-Side Demo
48+
run: dotnet publish samples/AfterBlazorServerSide/AfterBlazorServerSide.csproj --configuration Release --output ./publish/server-side
49+
50+
- name: Publish Client-Side Demo (WebAssembly)
51+
run: dotnet publish samples/AfterBlazorClientSide/AfterBlazorClientSide.csproj --configuration Release --output ./publish/client-side
52+
53+
- name: Upload Server-Side Demo artifact
54+
uses: actions/upload-artifact@v4
55+
with:
56+
name: demo-server-side
57+
path: ./publish/server-side
58+
59+
- name: Upload Client-Side Demo artifact
60+
uses: actions/upload-artifact@v4
61+
with:
62+
name: demo-client-side
63+
path: ./publish/client-side

samples/AfterBlazorClientSide/AfterBlazorClientSide.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
<Content Include="..\AfterBlazorServerSide\Components\Layout\**">
1616
<Link>Layout\%(RecursiveDir)%(Filename)%(Extension)</Link>
1717
</Content>
18+
<Content Include="..\AfterBlazorServerSide\Ads.xml" Link="wwwroot\Ads.xml">
19+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
20+
</Content>
1821
</ItemGroup>
1922

2023
<ItemGroup>

samples/AfterBlazorServerSide.Tests/ControlSampleTests.cs

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,17 @@ public ControlSampleTests(PlaywrightFixture fixture)
1616
[Theory]
1717
[InlineData("/ControlSamples/Button")]
1818
[InlineData("/ControlSamples/CheckBox")]
19+
[InlineData("/ControlSamples/CheckBox/Events")]
20+
[InlineData("/ControlSamples/CheckBox/Style")]
1921
[InlineData("/ControlSamples/HyperLink")]
2022
[InlineData("/ControlSamples/LinkButton")]
2123
[InlineData("/ControlSamples/Literal")]
2224
[InlineData("/ControlSamples/DropDownList")]
25+
[InlineData("/ControlSamples/Panel")]
26+
[InlineData("/ControlSamples/PlaceHolder")]
27+
[InlineData("/ControlSamples/RadioButton")]
28+
[InlineData("/ControlSamples/RadioButtonList")]
29+
[InlineData("/ControlSamples/TextBox")]
2330
public async Task EditorControl_Loads_WithoutErrors(string path)
2431
{
2532
await VerifyPageLoadsWithoutErrors(path);
@@ -49,6 +56,16 @@ public async Task NavigationControl_Loads_WithoutErrors(string path)
4956
await VerifyPageLoadsWithoutErrors(path);
5057
}
5158

59+
// Menu component tests - Menu has known JS interop requirements that may produce console errors
60+
// Testing separately to verify the page loads and renders content
61+
[Theory]
62+
[InlineData("/ControlSamples/Menu")]
63+
[InlineData("/ControlSamples/Menu/DatabindingSitemap")]
64+
public async Task MenuControl_Loads_AndRendersContent(string path)
65+
{
66+
await VerifyMenuPageLoads(path);
67+
}
68+
5269
// Validation Controls
5370
[Theory]
5471
[InlineData("/ControlSamples/RequiredFieldValidator")]
@@ -77,6 +94,53 @@ public async Task LoginControl_Loads_WithoutErrors(string path)
7794
[Theory]
7895
[InlineData("/ControlSamples/AdRotator")]
7996
public async Task OtherControl_Loads_WithoutErrors(string path)
97+
{
98+
await VerifyPageLoadsWithoutErrors(path);
99+
}
100+
101+
/// <summary>
102+
/// Validates that AdRotator displays an ad with the correct attributes.
103+
/// This specifically tests that Ads.xml is properly deployed and accessible.
104+
/// </summary>
105+
[Fact]
106+
public async Task AdRotator_DisplaysAd_WithCorrectAttributes()
107+
{
108+
// Arrange
109+
var page = await _fixture.NewPageAsync();
110+
111+
try
112+
{
113+
// Act
114+
var response = await page.GotoAsync($"{_fixture.BaseUrl}/ControlSamples/AdRotator", new PageGotoOptions
115+
{
116+
WaitUntil = WaitUntilState.NetworkIdle,
117+
Timeout = 30000
118+
});
119+
120+
// Assert - Page loads successfully
121+
Assert.NotNull(response);
122+
Assert.True(response.Ok, $"Page failed to load with status: {response.Status}");
123+
124+
// Assert - AdRotator component rendered (look for images from Ads.xml)
125+
// The AdRotator renders as: <a href="..."><img src="/img/CSharp.png" OR "/img/VB.png" alt="..." /></a>
126+
// Look for the specific image sources that come from Ads.xml
127+
var adImage = await page.QuerySelectorAsync("img[src='/img/CSharp.png'], img[src='/img/VB.png']");
128+
Assert.NotNull(adImage);
129+
Assert.True(await adImage.IsVisibleAsync(), "Ad image should be visible");
130+
131+
// Verify alt text is one of the expected values from Ads.xml
132+
var altText = await adImage.GetAttributeAsync("alt");
133+
Assert.NotNull(altText);
134+
Assert.NotEmpty(altText);
135+
Assert.Contains(altText, new[] { "CSharp", "Visual Basic" });
136+
}
137+
finally
138+
{
139+
await page.CloseAsync();
140+
}
141+
}
142+
143+
private async Task VerifyPageLoadsWithoutErrors(string path)
80144
{
81145
// Arrange
82146
var page = await _fixture.NewPageAsync();
@@ -105,33 +169,29 @@ public async Task OtherControl_Loads_WithoutErrors(string path)
105169
Timeout = 30000
106170
});
107171

108-
// Assert - AdRotator may have issues with file loading, so we just verify page loads
172+
// Assert
109173
Assert.NotNull(response);
110-
// Note: AdRotator may return 500 if Ads.xml is not found in production, but that's a known limitation
111-
Assert.True(response.Ok || response.Status == 500,
112-
$"Page {path} failed to load with status: {response.Status}");
174+
Assert.True(response.Ok, $"Page {path} failed to load with status: {response.Status}");
175+
Assert.Empty(consoleErrors);
176+
Assert.Empty(pageErrors);
113177
}
114178
finally
115179
{
116180
await page.CloseAsync();
117181
}
118182
}
119183

120-
private async Task VerifyPageLoadsWithoutErrors(string path)
184+
/// <summary>
185+
/// Verifies Menu pages load and render content. Menu component has known JS interop
186+
/// requirements (bwfc.Page.AddScriptElement) that may produce console errors when
187+
/// the JavaScript setup is not configured, but the page should still render.
188+
/// </summary>
189+
private async Task VerifyMenuPageLoads(string path)
121190
{
122191
// Arrange
123192
var page = await _fixture.NewPageAsync();
124-
var consoleErrors = new List<string>();
125193
var pageErrors = new List<string>();
126194

127-
page.Console += (_, msg) =>
128-
{
129-
if (msg.Type == "error")
130-
{
131-
consoleErrors.Add($"{path}: {msg.Text}");
132-
}
133-
};
134-
135195
page.PageError += (_, error) =>
136196
{
137197
pageErrors.Add($"{path}: {error}");
@@ -146,10 +206,18 @@ private async Task VerifyPageLoadsWithoutErrors(string path)
146206
Timeout = 30000
147207
});
148208

149-
// Assert
209+
// Assert - Page loads successfully
150210
Assert.NotNull(response);
151211
Assert.True(response.Ok, $"Page {path} failed to load with status: {response.Status}");
152-
Assert.Empty(consoleErrors);
212+
213+
// Assert - Page renders menu content (tables, links, or list items)
214+
var menuContent = await page.Locator("table, a, li, td").AllAsync();
215+
Assert.NotEmpty(menuContent);
216+
217+
// Note: We don't check console errors for Menu pages because the Menu component
218+
// requires JavaScript setup (bwfc.Page.AddScriptElement) that may not be configured
219+
// in all environments. The important thing is that the page renders.
220+
153221
Assert.Empty(pageErrors);
154222
}
155223
finally

0 commit comments

Comments
 (0)