test:ui
SkillWeb & browsingLets your agent write and run Playwright end-to-end UI tests covering login, navigation, and chat flows.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the test:ui skill
About this skill
Write and run Playwright UI E2E tests for Rossoctl - login, navigation, agent chat, across CI/Kind/HyperShift
What this skill tells your AI
The instructions your AI receives, as published by rossoctl/rossoctl in .claude/skills/test:ui/SKILL.md and read by ahel’s review.
Write and run Playwright UI E2E tests for the Rossoctl UI across all environments.
Not to be confused with
test:playwrightwhich is for recording narrated demo videos. This skill is for functional UI testing (login, navigate, interact, assert).
Test Location
rossoctl/ui-v2/
├── e2e/
│ ├── agent-chat.spec.ts # Login -> agent -> chat flow
│ ├── agent-catalog.spec.ts # Agent listing page
│ ├── tool-catalog.spec.ts # Tool listing page
│ └── home.spec.ts # Home page
├── playwright.config.ts # Playwright config (baseURL, webServer)
└── package.json # npm run test:e2e
Writing Tests
Login Helper
All tests should handle Keycloak login when auth is enabled:
import { test, expect, Page } from '@playwright/test';
const KEYCLOAK_USER = process.env.KEYCLOAK_USER || 'admin';
const KEYCLOAK_PASSWORD = process.env.KEYCLOAK_PASSWORD || 'admin';
async function loginIfNeeded(page: Page) {
await page.waitForLoadState('networkidle', { timeout: 15000 });
const isKeycloakLogin = await page.locator('#kc-form-login, #username, input[name="username"]')
.first().isVisible({ timeout: 3000 }).catch(() => false);
if (isKeycloakLogin) {
await page.locator('#username, input[name="username"]').first().fill(KEYCLOAK_USER);
await page.locator('#password, input[name="password"]').first().fill(KEYCLOAK_PASSWORD);
await page.locator('#kc-login, input[type="submit"]').first().click();
await page.waitForURL(/^(?!.*keycloak)/, { timeout: 15000 });
await page.waitForLoadState('networkidle');
}
}
Test Pattern
test('should login and interact with agent', async ({ page }) => {
test.setTimeout(120000); // LLM calls can be slow
await page.goto('/agents');
await loginIfNeeded(page);
// Navigate
await expect(page.getByRole('heading', { name: /Agent Catalog/i })).toBeVisible();
await page.getByRole('link', { name: /weather-service/i }).click();
// Interact
await page.getByPlaceholder('Type your message...').fill('What is the weather?');
await page.getByRole('button', { name: /Send/i }).click();
// Assert response
await expect(page.locator('text=/weather|temperature/i').first())
.toBeVisible({ timeout: 90000 });
});
Selector Patterns
| Element | Selector |
|---|---|
| Chat input | page.getByPlaceholder('Type your message...') |
| Send button | page.getByRole('button', { name: /Send/i }) |
| Agent link | page.getByRole('link', { name: /weather-service/i }) |
| Page heading | page.getByRole('heading', { name: /Agent Catalog/i }) |
| Namespace selector | page.locator('[aria-label="Select namespace"]') |
| Status badge | page.locator('.pf-v5-c-label') |
| Import button | page.getByRole('button', { name: /Import Agent/i }) |
Timeouts
| Operation | Timeout | Reason |
|---|---|---|
| Page load | 15s | Network + Keycloak init |
| API response | 30s | Backend query |
| LLM response | 90s | Ollama inference + tool calls |
| Full test | 120s | End-to-end including chat |
Running Tests
On Kind (local)
# Prerequisites: Kind cluster running, backend port-forwarded
kubectl port-forward -n rossoctl-system svc/rossoctl-backend 8000:8000 &
cd rossoctl/ui-v2
npm ci
npx playwright install chromium
npm run test:e2e # Headless (starts Vite dev server)
npm run test:e2e:ui # Interactive Playwright UI
npm run test:e2e:debug # Debug mode
npx playwright test agent-chat.spec.ts # Single file
The Vite dev server starts automatically and proxies /api to localhost:8000.
On HyperShift
export CLUSTER=<suffix> MANAGED_BY_TAG=${MANAGED_BY_TAG:-rossoctl-hypershift-custom}
export KUBECONFIG=~/clusters/hcp/$MANAGED_BY_TAG-$CLUSTER/auth/kubeconfig
# Get UI URL from OpenShift route
ROSSOCTL_UI_URL="https://$(kubectl get route rossoctl-ui -n rossoctl-system -o jsonpath='{.spec.host}')"
cd rossoctl/ui-v2
npm ci
npx playwright install chromium
ROSSOCTL_UI_URL="$ROSSOCTL_UI_URL" npx playwright test
ROSSOCTL_UI_URL="$ROSSOCTL_UI_URL" npm run test:e2e:ui # Interactive
HyperShift notes:
- Always set
ROSSOCTL_UI_URLto the OpenShift route (no dev server needed) - May need
PLAYWRIGHT_IGNORE_HTTPS_ERRORS=1for self-signed certs - Keycloak credentials:
kubectl get secret keycloak-initial-admin -n keycloak -o jsonpath='{.data.password}' | base64 -d
In CI
CI step 91-run-ui-tests.sh runs after pytest E2E tests:
- Installs npm dependencies + Playwright chromium
- Starts Vite dev server (proxies to port-forwarded backend)
- Runs
npx playwright test - Uploads
playwright-report/as artifact
Analyzing CI Failures
# Download Playwright report
gh run download <run-id> -n e2e-test-results -D /tmp/test-results
# Open HTML report
npx playwright show-report /tmp/test-results/rossoctl/ui-v2/playwright-report
Related Skills
test:playwright- Playwright demo video tests (different purpose)test:write- General E2E test writing patternstest:review- Review test qualitytdd:ci- CI-driven TDD (invokes this skill for UI tests)tdd:kind- Kind TDD (invokes this skill for UI tests)tdd:hypershift- HyperShift TDD (invokes this skill for UI tests)rossoctl:ui-debug- Debug UI issues (502, proxy, auth)
Signals
- GitHub stars
- 301
- Forks
- 107
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
test-ui-rossoctl- Source
- github.com/rossoctl/rossoctl