Skip to content

Commit b9a8eb9

Browse files
mpauloskyCopilot
andauthored
test(coverage): Sprint 3 — Component & Documentation Coverage (S3-1 through S3-5) (#213)
* docs: add code coverage standards section to CONTRIBUTING.md (S3-4, #207) * test(bunit): add AttachmentCard, BulkConfirmation, BulkProgress, UndoToast tests (S3-5, #208) - AttachmentCardTests (13 tests): image/thumbnail/blob URL, PDF icon, MD icon, generic TXT icon, filename display, formatted file size, upload date, uploader name, CanDelete flag, delete callback fires with attachment ID - BulkConfirmationModalTests (13 tests): visibility toggle, title/message display, affected count badge with singular/plural forms, delete vs non-delete icon backgrounds, confirm/cancel/backdrop callbacks, IsProcessing disables buttons, IsVisibleChanged fires on cancel - BulkProgressIndicatorTests (16 tests): visibility toggle, title, percentage, processed/total counts, null progress coalesces to zero, spinner present while processing, spinner hidden when complete, success/failure messages, success count, failure count visibility (shown >0 / hidden ==0), cancel and close buttons, OnClosed callback - UndoToastTests (12 tests): visibility toggle, message text, undo button shown with token / hidden with null or empty token, countdown seconds display, close button fires IsVisibleChanged(false)+OnDismissed, undo button fires OnUndo(token) then also dismisses (IsVisibleChanged + OnDismissed) Side effect: adds <Compile Remove> for untracked Theme test files that use removed bUnit 1.x APIs (CS0619/CS1061 compile errors introduced by another squad member; not committed to the repo). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(bunit): add AttachmentCard, BulkConfirmation, BulkProgress, UndoToast tests (S3-5, #208) - AttachmentCardTests (13 tests): image with thumbnail URL, image with blob URL fallback, image with empty thumbnail fallback, PDF icon label, MD icon label, generic TXT icon, filename display, formatted file size (1KB), upload date formatting, uploader name, CanDelete shows/hides delete button, OnDelete callback fires with attachment ID - BulkConfirmationModalTests (13 tests): IsVisible=false renders nothing, IsVisible=true renders modal, title+message display, affected count badge, singular/plural issue wording, Delete action uses red background, non-delete uses yellow, confirm callback, cancel callback, IsVisibleChanged(false) on cancel, backdrop click cancels, IsProcessing disables both buttons - BulkProgressIndicatorTests (16 tests): IsVisible=false renders nothing, title display, percentage (50%), processed/total counts, null progress coalesces to 0%, spinner present when incomplete, spinner hidden when complete, success/partial-failure completion messages, success count display, failure count shown when >0 and hidden when 0, cancel button when CanCancel, close button when complete, OnClosed callback - UndoToastTests (12 tests): IsVisible=false renders nothing, message text, undo button shown/hidden with token/null/empty, countdown seconds shown in undo badge, close button fires IsVisibleChanged(false)+OnDismissed, undo button fires OnUndo(token) then dismisses (IsVisibleChanged(false)+OnDismissed) Also adds <Compile Remove> for untracked bUnit 1.x Theme tests (not committed, break full recompile with CS0619/CS1061 - pre-existing issue from another squad member). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(bunit): add Analytics page bUnit tests (S3-1, #204) - Add AnalyticsPageTests with 20 comprehensive bUnit tests covering: - Loading state: animate-pulse skeleton and 4 skeleton card count - Error state: exact error message, red CSS styling, absent summary cards - Success state: scoped value assertions per SummaryCard for Total/Open/Closed - FormatResolutionTime: hours (12.5h), days (2.0d), minutes (30m) - All four SummaryCard titles verified together - DateRangePicker renders two date inputs in both loaded and loading states - Export to CSV button present and enabled before export - Authorize attribute contract verified via reflection (AdminPolicy) - Mediator called exactly once on initialization - Empty collections do not crash PrepareChartData - Post-review improvements: scoped DOM element assertions for value cards; error-state test now has positive assertion to guard against blank-markup false pass Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(bunit): add Pagination, SearchInput, StatusBadge, DeleteConfirmationModal tests (S3-2, #205) - PaginationTests (15 tests): hidden when TotalPages<=1, nav rendered when >1, prev/next button visibility, prev/next fire OnPageChange with correct page number, current-page click is a no-op, active border class, border-transparent on non-active, ellipsis rendered mid-range, no ellipsis for small ranges, ellipsis boundary tests (page-4 no start ellipsis / page-5 start+end ellipsis), status text shows page of total - SearchInputTests (15 tests): input/placeholder/id/aria-label rendering, search SVG icon, clear button hidden on null/empty value, clear button visible with value, ClearSearch fires ValueChanged(null) and OnSearch(null) without debounce timer, clear button hides after clearing; debounce tests use TaskCompletionSource (not Task.Delay) to avoid race conditions in CI - StatusBadgeTests (14 tests): null → 'Unknown' text + default class, status name rendered, Theory covering Open/InProgress/UnderReview/Resolved/Closed/WontFix/unknown backgrounds, individual text-color assertions for Open/InProgress/Resolved/WontFix, AdditionalClasses appended, 'badge' base class always present - CategoryBadgeTests (10 tests): null → 'Unknown', name rendered, Theory covering Bug/Feature/Enhancement/Question/Documentation/unknown backgrounds, text-color assertions for Bug/Feature/Documentation, AdditionalClasses, 'badge' base class Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(bunit): add ThemeProvider and theme component tests (S3-3, #206) - ThemeProviderTests (10 tests): renders child content, IsDarkMode (default/light/dark), Color/Brightness reflect JS values, OnThemeChanged fires on SetColorAsync/SetBrightnessAsync, SetColorAsync/SetBrightnessAsync update properties, guard-path tests when JS init fails, DisposeAsync safety - ThemeBrightnessToggleTests (6 tests): renders, aria-label, moon in light mode, sun in dark mode, click light→dark + verifies setBrightness('dark') JS call, click dark→light + verifies call - ThemeColorDropdownTests (7 tests): renders, aria-haspopup/expanded, swatches hidden before click, all four swatches visible after click, aria-expanded on open, selection closes dropdown, setColor JS call verified with correct 'blue' arg - Removed Compile Remove exclusion from csproj (leftover from pre-bUnit-2.x stub files) Total: 23 new tests in Components.Theme namespace; full suite 934 passed, 0 failed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(tests): stabilize ColorScheme_OpenDropdownShowsColorOptions E2E test Add NetworkIdle wait after clicking the dropdown toggle to allow Blazor Server SignalR round-trip to complete before asserting on color option button visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8c68819 commit b9a8eb9

15 files changed

Lines changed: 3348 additions & 0 deletions

CONTRIBUTING.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ If you hit a snag at any point, open an issue or drop a question in the relevant
1919
- [Gate 4 — Integration Tests + Playwright E2E (Docker required)](#gate-4--integration-tests--playwright-e2e-docker-required)
2020
- [The Docker Requirement](#the-docker-requirement)
2121
- [Running Tests Manually](#running-tests-manually)
22+
- [Code Coverage](#code-coverage)
2223
- [Code Conventions](#code-conventions)
2324
- [PR Process](#pr-process)
2425
- [Troubleshooting](#troubleshooting)
@@ -295,6 +296,84 @@ dotnet test IssueTrackerApp.slnx --configuration Release
295296
296297
---
297298

299+
## Code Coverage
300+
301+
The CI pipeline enforces an **80% line coverage gate** via the `Coverage Analysis` job in `.github/workflows/squad-test.yml`. PRs that drop below this threshold will fail CI and cannot be merged.
302+
303+
Coverage is collected with [coverlet](https://github.com/coverlet-coverage/coverlet) (`coverlet.collector` v8.0.0) and merged into a single Cobertura report by [ReportGenerator](https://reportgenerator.io/). Results are also published to [Codecov](https://codecov.io/gh/mpaulosky/IssueTrackerApp) — see the badges at the top of `README.md`.
304+
305+
### Running coverage locally
306+
307+
**Step 1 — collect coverage:**
308+
309+
```bash
310+
dotnet test IssueTrackerApp.slnx \
311+
--collect:"XPlat Code Coverage" \
312+
--results-directory ./coverage-results
313+
```
314+
315+
**Step 2 — generate an HTML report:**
316+
317+
```bash
318+
dotnet tool run reportgenerator \
319+
-reports:"coverage-results/**/coverage.cobertura.xml" \
320+
-targetdir:"coverage-report" \
321+
-reporttypes:Html
322+
```
323+
324+
Then open `coverage-report/index.html` in your browser.
325+
326+
> If `dotnet tool run reportgenerator` fails, install it globally first:
327+
> ```bash
328+
> dotnet tool install -g dotnet-reportgenerator-globaltool
329+
> ```
330+
331+
### What counts toward coverage
332+
333+
Only source projects under `src/` are measured. The following test projects all contribute coverage data:
334+
335+
| Test project | What it covers |
336+
|---|---|
337+
| `tests/Architecture.Tests` | Layer boundary and naming-convention checks |
338+
| `tests/Domain.Tests` | Domain command/query handlers and validators |
339+
| `tests/Web.Tests` | Web service unit tests (mocked dependencies) |
340+
| `tests/Web.Tests.Bunit` | Blazor component rendering (bUnit) |
341+
| `tests/Web.Tests.Integration` | API endpoints and SignalR (requires Docker) |
342+
| `tests/Persistence.MongoDb.Tests` | MongoDB repository unit tests (mocked) |
343+
| `tests/Persistence.MongoDb.Tests.Integration` | Repository integration tests (requires Docker) |
344+
345+
Test projects themselves, generated code, and `obj/` directories are excluded automatically by coverlet.
346+
347+
### The 80% threshold
348+
349+
The `Coverage Analysis` CI job reads `Summary.json` produced by ReportGenerator and compares `linecoverage` against `80`. If the value is below 80, the job exits with an error and the PR cannot be merged:
350+
351+
```
352+
::error::Code coverage is below 80% threshold: 74.3% (required: 80%)
353+
```
354+
355+
When coverage passes the gate you will see:
356+
357+
```
358+
::notice::Coverage gate passed: 83.1% >= 80%
359+
```
360+
361+
### The coverage badge
362+
363+
The `CodeCov Coverage` badge in `README.md` is served by Codecov and reflects the most recent successful merge to `main`. Click the badge to see the full Codecov dashboard with per-file breakdown and trend graphs.
364+
365+
### Adding new tests
366+
367+
Use the correct test project for each test type:
368+
369+
- **`tests/Domain.Tests/`** — unit tests for domain command/query handlers
370+
- **`tests/Web.Tests/`** — unit tests for web services with mocked dependencies
371+
- **`tests/Web.Tests.Bunit/`** — Blazor component tests with bUnit
372+
- **`tests/Web.Tests.Integration/`** — API endpoint and SignalR integration tests (requires Docker)
373+
- **`tests/Persistence.MongoDb.Tests.Integration/`** — repository integration tests against real MongoDB (requires Docker)
374+
375+
---
376+
298377
## Code Conventions
299378

300379
### Language and framework

tests/AppHost.Tests/Tests/Theme/ColorSchemeTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ await InteractWithPageAsync("web", async page =>
5858
await schemeBtn.WaitForAsync();
5959
await schemeBtn.ClickAsync();
6060

61+
// Allow Blazor Server to process the click via SignalR before checking the dropdown
62+
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
63+
6164
// Color swatch buttons: title="Blue theme", title="Red theme", etc.
6265
var blueOption = page.Locator("button[aria-label=\"Blue color theme\"]");
6366
var redOption = page.Locator("button[aria-label=\"Red color theme\"]");

0 commit comments

Comments
 (0)