refactor: full-stack restructure with multi-tenancy, workspace management, and K8s diagnostics
- Add Workspace domain (entity, repository, service, handler, DTO) - Add multi-tenant K8s client with tenant binding and quota management - Add K8s diagnostics client (instance diagnostics) - Add authorization middleware (authz package) - Restructure frontend to feature-based architecture (features/) - Add User Management page in configuration - Add AccessDenied page and route guards - Refactor shared components (form inputs, layout, UI) - Update Tailwind config for new design system - Add comprehensive documentation (docs/, tasks/, plans) - Improve cluster service with better kubeconfig handling - Add tests for crypto, config, helm client, tenant binding
This commit is contained in:
96
test/frontend-playwright-smoke.py
Normal file
96
test/frontend-playwright-smoke.py
Normal file
@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
# Covers frontend smoke flows: login, chart browser rendering, launch modal,
|
||||
# registry/cluster configuration pages, instances page, and mobile layout sanity.
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from playwright.sync_api import expect, sync_playwright
|
||||
|
||||
|
||||
BASE_URL = os.environ.get("FRONTEND_URL", "http://localhost:18080")
|
||||
ADMIN_USER = os.environ.get("ADMIN_USER", "admin")
|
||||
ADMIN_PASS = os.environ["ADMIN_PASS"]
|
||||
|
||||
|
||||
def login(page):
|
||||
page.goto(BASE_URL, wait_until="networkidle")
|
||||
if page.locator("input[type='password']").count() == 0:
|
||||
return
|
||||
text_inputs = page.locator("input:not([type='password'])")
|
||||
text_inputs.first.fill(ADMIN_USER)
|
||||
page.locator("input[type='password']").first.fill(ADMIN_PASS)
|
||||
page.get_by_role("button").filter(has_text="Login").last.click()
|
||||
page.wait_for_url("**/home", timeout=15000)
|
||||
page.wait_for_load_state("networkidle")
|
||||
expect(page.locator("body")).not_to_contain_text("Login failed")
|
||||
|
||||
|
||||
def screenshot(page, name):
|
||||
page.screenshot(path=f"/tmp/{name}.png", full_page=True)
|
||||
|
||||
|
||||
def click_nav(page, name, index=0):
|
||||
item = page.get_by_role("button", name=name).nth(index)
|
||||
try:
|
||||
item.click(timeout=5000)
|
||||
except Exception:
|
||||
item.evaluate("element => element.click()")
|
||||
page.wait_for_load_state("networkidle")
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
|
||||
def record_console_error(errors, msg):
|
||||
text = msg.text
|
||||
ignored = [
|
||||
"Failed to load resource: the server responded with a status of 404",
|
||||
"[LaunchModal] Failed to load values schema",
|
||||
]
|
||||
if any(item in text for item in ignored):
|
||||
return
|
||||
errors.append(text)
|
||||
|
||||
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
page = browser.new_page(viewport={"width": 1440, "height": 950})
|
||||
errors = []
|
||||
page.on("pageerror", lambda exc: errors.append(str(exc)))
|
||||
page.on("console", lambda msg: record_console_error(errors, msg) if msg.type == "error" else None)
|
||||
|
||||
login(page)
|
||||
|
||||
click_nav(page, "Launch Instance")
|
||||
expect(page.get_by_text("Chart Browser")).to_be_visible(timeout=15000)
|
||||
screenshot(page, "ocdp-chart-browser-desktop")
|
||||
if page.get_by_role("button", name="Launch").count() > 0:
|
||||
page.get_by_role("button", name="Launch").first.click()
|
||||
if page.get_by_role("button", name="Cancel").count() > 0:
|
||||
expect(page.get_by_role("heading", name="Launch Instance")).to_be_visible(timeout=10000)
|
||||
screenshot(page, "ocdp-launch-modal")
|
||||
page.get_by_role("button", name="Cancel").click()
|
||||
|
||||
click_nav(page, "Registries")
|
||||
expect(page.locator("body")).to_contain_text("Registry")
|
||||
screenshot(page, "ocdp-registry-config")
|
||||
|
||||
click_nav(page, "Clusters")
|
||||
expect(page.locator("body")).to_contain_text("Cluster")
|
||||
screenshot(page, "ocdp-cluster-config")
|
||||
|
||||
click_nav(page, "Instances")
|
||||
expect(page.locator("body")).to_contain_text("Instance")
|
||||
screenshot(page, "ocdp-instances")
|
||||
|
||||
mobile = browser.new_page(viewport={"width": 390, "height": 844}, is_mobile=True)
|
||||
login(mobile)
|
||||
click_nav(mobile, "Launch Instance")
|
||||
expect(mobile.get_by_text("Chart Browser")).to_be_visible(timeout=15000)
|
||||
screenshot(mobile, "ocdp-chart-browser-mobile")
|
||||
overflow = mobile.evaluate("document.documentElement.scrollWidth > document.documentElement.clientWidth + 2")
|
||||
assert not overflow, "mobile page has horizontal overflow"
|
||||
|
||||
browser.close()
|
||||
|
||||
if errors:
|
||||
raise AssertionError("\\n".join(errors[:10]))
|
||||
Reference in New Issue
Block a user