Add UI Tests Skill
Add Selenium WebDriver + xUnit UI tests for NovaTune's Vue SPAs, running against an Aspire-hosted backend with Vite dev servers.
Steps
Read the execution plan at tasks/add_ui_tests/main_exec.md for the full implementation specification including phases, code, and acceptance criteria.
Read the planning doc at tasks/add_ui_tests/main.md for architecture context and design rationale.
Check current state of the ui_tests directory and solution:
ls src/ui_tests/ — check if project already exists
- Check
src/NovaTuneApp/NovaTuneApp.sln for existing ui_tests entries
- Check Vue components for existing
data-testid attributes
Execute phases sequentially as defined in main_exec.md:
- Phase 0: Add
data-testid attributes to Vue components
- Phase 1: Bootstrap the xUnit + Selenium project
- Phase 2: Create fixtures (UiTestFixture, WebDriverFactory, UiTestBase)
- Phase 3: Player UI tests (auth, library, playlists)
- Phase 4: Admin UI tests (auth, dashboard)
- Phase 5: Gitignore + cleanup
- Phase 6: Verify full suite
Verify each phase passes its acceptance criteria before proceeding.
Key Patterns
Project Structure
src/ui_tests/NovaTuneApp.UiTests/
Fixtures/
UiTestFixture.cs # Aspire host + Vite processes + data seeding
WebDriverFactory.cs # ChromeDriver creation (headless by default)
TestCollections.cs # xUnit collection definition
Infrastructure/
WaitHelpers.cs # Explicit waits (never Thread.Sleep)
ScreenshotHelper.cs # Screenshot on failure
UiTestBase.cs # Base class with driver lifecycle + login helpers
Player/
PlayerAuthTests.cs # Login, register, redirect, error states
PlayerLibraryTests.cs # Track listing, empty state
PlayerPlaylistsTests.cs # Create playlist, empty state
Admin/
AdminAuthTests.cs # Admin login, non-admin rejection, register
AdminDashboardTests.cs # Stat cards, sidebar navigation
Test Convention
- Collection:
[Collection("UI Tests")] — shared fixture, no parallel execution
- Traits:
[Trait("Category", "UI")], [Trait("App", "Player")] or [Trait("App", "Admin")]
- Base class:
UiTestBase handles ChromeDriver lifecycle, ClearDataAsync(), screenshot-on-failure
- Login helpers:
PlayerLogin(email, password) and AdminLogin(email, password) in base class
- Selectors: Prefer
data-testid via WaitHelpers.WaitForTestId(), fall back to text via WaitHelpers.WaitForText()
- No
Thread.Sleep — only explicit WebDriverWait via WaitHelpers
Fixture Lifecycle
UiTestFixture (collection-level): Starts Aspire + 2 Vite servers once per test run
UiTestBase (per-class): Creates fresh ChromeDriver, clears DB, captures screenshot on failure
- Vite ports: 26173 (player), 26174 (admin) — offset from dev ports to avoid conflicts
VITE_API_BASE_URL injected into Vite processes pointing to test API
Data Seeding
SeedUserAsync(email, displayName, password) — register via API
SeedAdminUserAsync(email, displayName, password) — register + grant Admin role via RavenDB
SeedTracksForUserAsync(userId, count) — batch insert Track documents
ClearDataAsync() — wipe all users, tracks, playlists, audit logs
data-testid Conventions
- Auth forms:
email, password, login-button, register-button, displayName, confirmPassword
- Error states:
error-message, success-message
- Page headings:
library-heading, playlists-heading, dashboard-heading
- Empty states:
empty-state
- Interactive:
create-playlist-button, playlist-name-input, playlist-submit-button
- Lists:
track-list
Commands
# Build
dotnet build src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj
# Run all UI tests
dotnet test src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj -c Debug
# Player only
dotnet test src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj -c Debug \
--filter "Trait=App&Value=Player"
# Admin only
dotnet test src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj -c Debug \
--filter "Trait=App&Value=Admin"
# Headed mode (see browser)
UI_TESTS_HEADED=1 dotnet test src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj -c Debug
# Frontend lint/typecheck after adding data-testid
cd src/NovaTuneClient && pnpm lint && pnpm typecheck
Prerequisites
- Docker/Podman running (for Aspire infrastructure containers)
- Chrome/Chromium installed (Selenium Manager handles chromedriver)
pnpm install completed in src/NovaTuneClient/
- .NET 9 SDK installed
1---2name: add-ui-tests-23description: Add Selenium-based UI tests for NovaTune player and admin Vue SPAs against Aspire backend4---56# Add UI Tests Skill78Add Selenium WebDriver + xUnit UI tests for NovaTune's Vue SPAs, running against an Aspire-hosted backend with Vite dev servers.910## Steps11121. **Read the execution plan** at `tasks/add_ui_tests/main_exec.md` for the full implementation specification including phases, code, and acceptance criteria.13142. **Read the planning doc** at `tasks/add_ui_tests/main.md` for architecture context and design rationale.15163. **Check current state** of the ui_tests directory and solution:17 - `ls src/ui_tests/` — check if project already exists18 - Check `src/NovaTuneApp/NovaTuneApp.sln` for existing ui_tests entries19 - Check Vue components for existing `data-testid` attributes20214. **Execute phases sequentially** as defined in `main_exec.md`:22 - Phase 0: Add `data-testid` attributes to Vue components23 - Phase 1: Bootstrap the xUnit + Selenium project24 - Phase 2: Create fixtures (UiTestFixture, WebDriverFactory, UiTestBase)25 - Phase 3: Player UI tests (auth, library, playlists)26 - Phase 4: Admin UI tests (auth, dashboard)27 - Phase 5: Gitignore + cleanup28 - Phase 6: Verify full suite29305. **Verify each phase** passes its acceptance criteria before proceeding.3132## Key Patterns3334### Project Structure35```36src/ui_tests/NovaTuneApp.UiTests/37 Fixtures/38 UiTestFixture.cs # Aspire host + Vite processes + data seeding39 WebDriverFactory.cs # ChromeDriver creation (headless by default)40 TestCollections.cs # xUnit collection definition41 Infrastructure/42 WaitHelpers.cs # Explicit waits (never Thread.Sleep)43 ScreenshotHelper.cs # Screenshot on failure44 UiTestBase.cs # Base class with driver lifecycle + login helpers45 Player/46 PlayerAuthTests.cs # Login, register, redirect, error states47 PlayerLibraryTests.cs # Track listing, empty state48 PlayerPlaylistsTests.cs # Create playlist, empty state49 Admin/50 AdminAuthTests.cs # Admin login, non-admin rejection, register51 AdminDashboardTests.cs # Stat cards, sidebar navigation52```5354### Test Convention55- Collection: `[Collection("UI Tests")]` — shared fixture, no parallel execution56- Traits: `[Trait("Category", "UI")]`, `[Trait("App", "Player")]` or `[Trait("App", "Admin")]`57- Base class: `UiTestBase` handles ChromeDriver lifecycle, `ClearDataAsync()`, screenshot-on-failure58- Login helpers: `PlayerLogin(email, password)` and `AdminLogin(email, password)` in base class59- Selectors: Prefer `data-testid` via `WaitHelpers.WaitForTestId()`, fall back to text via `WaitHelpers.WaitForText()`60- No `Thread.Sleep` — only explicit WebDriverWait via `WaitHelpers`6162### Fixture Lifecycle63- `UiTestFixture` (collection-level): Starts Aspire + 2 Vite servers once per test run64- `UiTestBase` (per-class): Creates fresh ChromeDriver, clears DB, captures screenshot on failure65- Vite ports: 26173 (player), 26174 (admin) — offset from dev ports to avoid conflicts66- `VITE_API_BASE_URL` injected into Vite processes pointing to test API6768### Data Seeding69- `SeedUserAsync(email, displayName, password)` — register via API70- `SeedAdminUserAsync(email, displayName, password)` — register + grant Admin role via RavenDB71- `SeedTracksForUserAsync(userId, count)` — batch insert Track documents72- `ClearDataAsync()` — wipe all users, tracks, playlists, audit logs7374### data-testid Conventions75- Auth forms: `email`, `password`, `login-button`, `register-button`, `displayName`, `confirmPassword`76- Error states: `error-message`, `success-message`77- Page headings: `library-heading`, `playlists-heading`, `dashboard-heading`78- Empty states: `empty-state`79- Interactive: `create-playlist-button`, `playlist-name-input`, `playlist-submit-button`80- Lists: `track-list`8182## Commands8384```bash85# Build86dotnet build src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj8788# Run all UI tests89dotnet test src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj -c Debug9091# Player only92dotnet test src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj -c Debug \93 --filter "Trait=App&Value=Player"9495# Admin only96dotnet test src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj -c Debug \97 --filter "Trait=App&Value=Admin"9899# Headed mode (see browser)100UI_TESTS_HEADED=1 dotnet test src/ui_tests/NovaTuneApp.UiTests/NovaTuneApp.UiTests.csproj -c Debug101102# Frontend lint/typecheck after adding data-testid103cd src/NovaTuneClient && pnpm lint && pnpm typecheck104```105106## Prerequisites107108- Docker/Podman running (for Aspire infrastructure containers)109- Chrome/Chromium installed (Selenium Manager handles chromedriver)110- `pnpm install` completed in `src/NovaTuneClient/`111- .NET 9 SDK installed