End-to-end test automation suite for automationexercise.com built with C# / .NET 8, Microsoft Playwright, and NUnit 3.
| Tool | Version | Purpose |
|---|---|---|
| .NET | 8.0 | Runtime |
| Microsoft Playwright | 1.58 | Browser automation |
| NUnit | 4.2 | Test runner |
| FluentAssertions | 6.12 | Readable assertions |
| Bogus | 35.6 | Test data generation |
AutomationExercise.Tests/
├── Helpers/
│ ├── BasePage.cs # Abstract base for all Page Objects
│ ├── BaseTest.cs # Browser lifecycle, route blocking, screenshot on failure
│ ├── BaseApiTest.cs # HTTP client, API response parsing, SLA assertions
│ ├── ApiCleanupHelper.cs # Shared HTTP client for test data setup/teardown
│ ├── TestConstants.cs # URLs, selectors, expected messages, test data factory
│ └── TestSettings.cs # Config loading from appsettings.json
├── Pages/ # Page Object Model classes
│ ├── HomePage.cs
│ ├── LoginPage.cs
│ ├── RegisterPage.cs
│ ├── ProductsPage.cs
│ ├── ProductDetailPage.cs
│ ├── CartPage.cs
│ ├── CheckoutPage.cs
│ └── ContactUsPage.cs
├── Tests/
│ ├── UI/ # Browser-based tests (inherit BaseTest)
│ └── API/ # HTTP contract tests (inherit BaseApiTest)
├── appsettings.json # Base configuration
├── appsettings.local.json # Local overrides with credentials (gitignored)
├── nunit.runsettings # Parallel execution and retry configuration
└── playwright.json # Playwright browser settings
- .NET 8 SDK
- PowerShell (Windows) or
pwsh(macOS/Linux)
git clone https://github.com/QA-yanakin/AutomationExercise.Playwright.git
cd AutomationExercise.Playwright/AutomationExercise.Tests
dotnet restorepowershell -ExecutionPolicy Bypass -File "bin/Debug/net8.0/playwright.ps1" install chromiumCopy the template and fill in your credentials:
cp appsettings.local.template.json appsettings.local.jsonEdit appsettings.local.json:
{
"TestUser": {
"Email": "your-test-email@example.com",
"Password": "your-test-password"
},
"Browser": {
"Headless": false,
"SlowMo": 500
}
}
appsettings.local.jsonis gitignored and never committed.
# All tests
dotnet test
# Smoke gate only (fast, run first)
dotnet test --filter "Category=Smoke"
# UI tests only
dotnet test --filter "Category=UI"
# API tests only (no browser required)
dotnet test --filter "Category=API"
# Negative tests only
dotnet test --filter "Category=Negative"38 tests — ~2.5 min runtime in CI (parallel execution)
| Area | Tests | What is covered |
|---|---|---|
| UI — Home & Smoke | 2 | Page load, newsletter subscribe |
| UI — Login & Auth | 5 | Valid login, invalid credentials, logout, session persistence |
| UI — Registration | 3 | Full form, duplicate email (negative), field validation |
| UI — Products | 5 | Listing, product detail, search, category filter, brand filter |
| UI — Cart & Checkout | 4 | Add to cart, remove from cart, quantity, guest redirect |
| UI — Contact Us | 1 | Form submit with JS dialog handling |
| API — Products | 4 | GET list, GET search, POST search (negative 400), schema validation |
| API — Brands | 2 | GET list (contract), PUT (negative 405) |
| API — Account | 12 | Create, login, get by email, delete — happy path + negative cases |
| Total | 38 | 0 failed · 0 skipped |
graph TD
subgraph Tests
UI[UI Tests<br/>LoginTests · RegisterTests<br/>ProductsTests · CartTests<br/>CheckoutTests · ContactUsTests]
API[API Tests<br/>AccountApiTests · ProductsApiTests<br/>BrandsApiTests]
end
subgraph Pages
PO[Page Objects<br/>HomePage · LoginPage · RegisterPage<br/>ProductsPage · ProductDetailPage<br/>CartPage · CheckoutPage · ContactUsPage]
end
subgraph Helpers
BT[BaseTest<br/>Browser lifecycle<br/>Route blocking<br/>Screenshot on failure]
BP[BasePage<br/>Navigation · Waits<br/>Overlay handling]
BAT[BaseApiTest<br/>HttpClient · Response parsing<br/>SLA assertions]
TC[TestConstants<br/>URLs · Selectors<br/>Test data factory]
TS[TestSettings<br/>Timeouts · BaseUrl<br/>appsettings.json]
AC[ApiCleanupHelper<br/>Create · Delete user<br/>TearDown cleanup]
end
UI -->|inherits| BT
UI -->|uses| PO
UI -->|uses| AC
API -->|inherits| BAT
API -->|uses| TC
PO -->|inherits| BP
BT -->|uses| TS
BAT -->|uses| TS
BP -->|uses| TC
All UI interactions are encapsulated in Page Objects (/Pages). Test classes contain only assertions — no raw Playwright calls, no selectors.
data-qaattributes- ARIA roles —
GetByRole() - Label text —
GetByLabel() - Placeholder —
GetByPlaceholder() #idor[name="..."]- Visible text —
GetByText() - Relative XPath — last resort
CSS class selectors are avoided; exceptions are documented inline.
- UI tests use API state injection for prerequisites (login, user creation) — never the UI
- Every test that creates a user account deletes it in
[TearDown] - Unique emails generated per run using
Guid:user_{guid}@example.com - Stable reference data (product IDs, category names) defined in
TestConstants
- Route interception blocks ad/tracking scripts (reduces page load from ~6s to ~1-2s)
- DOMContentLoaded instead of NetworkIdle for navigation waits
- Parallel execution via
nunit.runsettings(4 workers) - Three-tier timeout:
ElementTimeout(10s) /NavigationTimeout(15s) /DefaultTimeout(30s)
- Screenshot on failure — saved to
/TestResults/Screenshots/ - API last-response logged on failure with status, timing, and body
WaitForVisibleAsyncincludes page name and URL in timeout messages
For pipeline execution, add these steps before running tests:
dotnet restore
dotnet build
pwsh -File "bin/Debug/net8.0/playwright.ps1" install chromium
dotnet test --filter "Category=Smoke" # gate
dotnet test # full suiteSet BASE_URL environment variable to target staging vs production.
flowchart LR
A([Push to main]) --> B
subgraph JOB1 [Smoke Gate]
B[Checkout + Build] --> C[Install Playwright]
C --> D[Run 2 smoke tests]
D --> E{Passed?}
end
E -->|NO — fail fast| F([Pipeline stopped\nNo runner minutes wasted])
E -->|YES| G
subgraph JOB2 [Full Suite]
G[Checkout + Build\ncached NuGet + browser] --> H[Run 38 tests\n4 workers in parallel]
H --> I[Publish test summary\ndorny/test-reporter]
I --> J[Upload TRX artifact\n30 day retention]
end
H -->|on failure| K[Upload screenshots\n7 day retention]
| Branch | Contents |
|---|---|
main |
Current framework (v2) |
v1-basic |
Original basic POM implementation |