feat: separate create/edit form pages, Playwright test infrastructure, /auth/token endpoint

Move all CRUD create/edit forms from inline on list pages to dedicated form pages
with back-button navigation and post-save redirect. Add Playwright Docker container
(browser server on port 3000) with 25 passing E2E tests covering login, navigation,
and site CRUD workflows. Add POST /auth/token endpoint for clean JWT retrieval.
This commit is contained in:
Joseph Doherty
2026-03-21 15:17:24 -04:00
parent b3f8850711
commit d3194e3634
31 changed files with 2333 additions and 1117 deletions

View File

@@ -1,6 +1,6 @@
# Test Infrastructure
This document describes the local Docker-based test infrastructure for ScadaLink development. Six services provide the external dependencies needed to run and test the system locally. The first six run in `infra/docker-compose.yml`; Traefik runs alongside the cluster nodes in `docker/docker-compose.yml`.
This document describes the local Docker-based test infrastructure for ScadaLink development. Seven services provide the external dependencies needed to run and test the system locally. The first seven run in `infra/docker-compose.yml`; Traefik runs alongside the cluster nodes in `docker/docker-compose.yml`.
## Services
@@ -12,6 +12,7 @@ This document describes the local Docker-based test infrastructure for ScadaLink
| SMTP (Mailpit) | `axllent/mailpit:latest` | 1025 (SMTP), 8025 (web) | Environment vars | `infra/` |
| REST API (Flask) | Custom build (`infra/restapi/Dockerfile`) | 5200 | `infra/restapi/app.py` | `infra/` |
| LmxFakeProxy | Custom build (`infra/lmxfakeproxy/Dockerfile`) | 50051 (gRPC) | Environment vars | `infra/` |
| Playwright | `mcr.microsoft.com/playwright:v1.58.2-noble` | 3000 (WebSocket) | Command args | `infra/` |
| Traefik LB | `traefik:v3.4` | 9000 (proxy), 8180 (dashboard) | `docker/traefik/` | `docker/` |
## Quick Start
@@ -43,6 +44,7 @@ Each service has a dedicated document with configuration details, verification s
- [test_infra_smtp.md](test_infra_smtp.md) — SMTP test server (Mailpit)
- [test_infra_restapi.md](test_infra_restapi.md) — REST API test server (Flask)
- [test_infra_lmxfakeproxy.md](test_infra_lmxfakeproxy.md) — LmxProxy fake server (OPC UA bridge)
- [test_infra_playwright.md](test_infra_playwright.md) — Playwright browser server (Central UI testing)
- Traefik LB — see `docker/README.md` and `docker/traefik/` (runs with the cluster, not in `infra/`)
## Connection Strings
@@ -103,7 +105,7 @@ After a full teardown, the next `docker compose up -d` starts fresh — re-run t
```
infra/
docker-compose.yml # All five services
docker-compose.yml # All seven services
teardown.sh # Teardown script (volumes, images, venv)
glauth/config.toml # LDAP users and groups
mssql/setup.sql # Database and user creation

View File

@@ -0,0 +1,104 @@
# Test Infrastructure: Playwright Browser Server
## Overview
The Playwright browser server provides a remote headless browser (Chromium, Firefox, WebKit) that test scripts connect to over the network. It runs as a Playwright Server on port 3000, allowing UI tests for the Central UI (Blazor Server) to run from the host machine while the browser executes inside the container with access to the Docker network.
## Image & Ports
- **Image**: `mcr.microsoft.com/playwright:v1.58.2-noble` (Ubuntu 24.04 LTS)
- **Server port**: 3000 (Playwright Server WebSocket endpoint)
## Configuration
| Setting | Value | Description |
|---------|-------|-------------|
| `--host 0.0.0.0` | Bind address | Listen on all interfaces |
| `--port 3000` | Server port | Playwright Server WebSocket port |
| `ipc: host` | Docker IPC | Shared IPC namespace (required for Chromium) |
No additional config files are needed. The container runs `npx playwright run-server` on startup.
## Connecting from Test Scripts
Test scripts run on the host and connect to the browser server via WebSocket. The connection URL is:
```
ws://localhost:3000
```
### .NET (Microsoft.Playwright)
```csharp
using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.ConnectAsync("ws://localhost:3000");
var page = await browser.NewPageAsync();
// Browser runs inside Docker — use the Docker network hostname for Traefik.
await page.GotoAsync("http://scadalink-traefik");
```
### Node.js
```javascript
const { chromium } = require('playwright');
const browser = await chromium.connect('ws://localhost:3000');
const page = await browser.newPage();
await page.goto('http://scadalink-traefik');
```
### Python
```python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.connect("ws://localhost:3000")
page = browser.new_page()
page.goto("http://scadalink-traefik")
```
## Central UI Access
The Playwright container is on the `scadalink-net` Docker network, so it can reach the Central UI cluster nodes directly:
| Target | URL in Test Scripts |
|--------|---------------------|
| Traefik LB | `http://scadalink-traefik` |
| Central Node A | `http://scadalink-central-a:5000` |
| Central Node B | `http://scadalink-central-b:5000` |
**Important**: The browser runs inside the Docker container, so `page.goto()` URLs must use Docker network hostnames (not `localhost`). The test script itself connects to the Playwright server via `ws://localhost:3000` (host-mapped port), but all URLs navigated by the browser resolve inside the container.
## Verification
1. Check the container is running:
```bash
docker ps --filter name=scadalink-playwright
```
2. Check the server is accepting connections (look for the WebSocket endpoint in logs):
```bash
docker logs scadalink-playwright 2>&1 | head -5
```
3. Quick smoke test with a one-liner (requires `npx` and `playwright` on the host):
```bash
npx playwright@1.58.2 test --browser chromium --connect ws://localhost:3000
```
## Relevance to ScadaLink Components
- **Central UI** — end-to-end UI testing of all Blazor Server pages (login, admin, design, deployment, monitoring workflows).
- **Traefik Proxy** — verify load balancer behavior, failover, and active node routing from a browser perspective.
## Notes
- The container includes Chromium, Firefox, and WebKit. Connect to the desired browser via `playwright.chromium.connect()`, `playwright.firefox.connect()`, or `playwright.webkit.connect()`.
- The `ipc: host` flag is required for Chromium to avoid out-of-memory crashes in the container.
- The Playwright Server version (`1.58.2`) must match the `@playwright` package version used by test scripts on the host.
- The container is stateless — no test data or browser state persists between restarts.
- To stop only the Playwright container: `cd infra && docker compose stop playwright`.