fix(tests): add sections J-N, force WAITLIST_MODE=false in visual server
- Add 18 new E2E tests from master: pricing, checkout, supplier signup, supplier dashboard, and business plan export (sections J-N) - Force WAITLIST_MODE=false in visual server subprocess — the root .env sets WAITLIST_MODE=true, and since Config class attributes evaluate at import time (before fork), the subprocess inherits the parent's value. Patching both os.environ and core.config directly ensures feature pages render instead of waitlist templates. - All 77 visual tests now pass in ~59 seconds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
Comprehensive E2E flow tests using Playwright.
|
||||
|
||||
Covers all major user flows: public pages, planner, auth, directory, quote wizard,
|
||||
and cross-cutting checks (translations, footer, language switcher).
|
||||
billing, supplier dashboard, and cross-cutting checks (translations, footer, language switcher).
|
||||
|
||||
Skipped by default (requires `playwright install chromium`).
|
||||
Run explicitly with:
|
||||
@@ -403,3 +403,178 @@ def test_planner_tooltips_present(live_server, page):
|
||||
|
||||
ti_spans = page.locator(".ti")
|
||||
assert ti_spans.count() >= 1, "No tooltip spans (.ti) found on results tab"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# J. Pricing Page
|
||||
# =============================================================================
|
||||
|
||||
def test_pricing_page_loads_en(live_server, page):
|
||||
"""Pricing page renders in English."""
|
||||
resp = page.goto(live_server + "/billing/pricing")
|
||||
assert resp.ok, f"/billing/pricing returned {resp.status}"
|
||||
expect(page.locator("h1").first).to_be_visible()
|
||||
|
||||
|
||||
def test_pricing_page_loads_de(live_server, page):
|
||||
"""Pricing page renders in German when lang cookie is set."""
|
||||
page.context.add_cookies([{
|
||||
"name": "lang", "value": "de",
|
||||
"domain": "127.0.0.1", "path": "/"
|
||||
}])
|
||||
resp = page.goto(live_server + "/billing/pricing")
|
||||
assert resp.ok, f"/billing/pricing returned {resp.status}"
|
||||
expect(page.locator("h1").first).to_be_visible()
|
||||
|
||||
|
||||
def test_pricing_page_has_plan_cards(live_server, page):
|
||||
"""Pricing page shows at least 2 plan cards with CTAs."""
|
||||
page.goto(live_server + "/billing/pricing")
|
||||
page.wait_for_load_state("networkidle")
|
||||
cards = page.locator(".card-header")
|
||||
assert cards.count() >= 2, f"Expected >=2 plan cards, found {cards.count()}"
|
||||
ctas = page.locator("a.btn, a.btn-outline")
|
||||
assert ctas.count() >= 2, f"Expected >=2 CTAs, found {ctas.count()}"
|
||||
|
||||
|
||||
def test_pricing_page_no_auth_required(live_server, page):
|
||||
"""Pricing page is accessible without authentication."""
|
||||
resp = page.goto(live_server + "/billing/pricing")
|
||||
assert resp.ok
|
||||
assert "login" not in page.url and "auth" not in page.url
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# K. Checkout Success
|
||||
# =============================================================================
|
||||
|
||||
def test_checkout_success_requires_auth(live_server, page):
|
||||
"""Checkout success page redirects unauthenticated users to login."""
|
||||
page.goto(live_server + "/billing/success", wait_until="networkidle")
|
||||
assert "login" in page.url or "auth" in page.url, (
|
||||
f"Expected redirect to login, got: {page.url}"
|
||||
)
|
||||
|
||||
|
||||
def test_checkout_success_renders_for_authed_user(live_server, page):
|
||||
"""Checkout success page renders for authenticated user."""
|
||||
dev_login(page, live_server, "checkout@example.com")
|
||||
resp = page.goto(live_server + "/billing/success")
|
||||
assert resp.ok
|
||||
expect(page.locator("h1").first).to_be_visible()
|
||||
expect(page.locator("a.btn").first).to_be_visible()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# L. Supplier Signup Wizard
|
||||
# =============================================================================
|
||||
|
||||
def test_supplier_signup_step1_loads(live_server, page):
|
||||
"""Supplier signup wizard step 1 renders."""
|
||||
resp = page.goto(live_server + "/en/suppliers/signup")
|
||||
assert resp.ok, f"/en/suppliers/signup returned {resp.status}"
|
||||
page.wait_for_load_state("networkidle")
|
||||
expect(page.locator("[data-step='1']").first).to_be_visible()
|
||||
|
||||
|
||||
def test_supplier_signup_step1_has_plan_cards(live_server, page):
|
||||
"""Step 1 shows plan cards for selecting a tier."""
|
||||
page.goto(live_server + "/en/suppliers/signup")
|
||||
page.wait_for_load_state("networkidle")
|
||||
plan_cards = page.locator(".s-plan-card")
|
||||
assert plan_cards.count() >= 2, (
|
||||
f"Expected >=2 plan cards, found {plan_cards.count()}"
|
||||
)
|
||||
expect(page.locator(".s-btn-next").first).to_be_visible()
|
||||
|
||||
|
||||
def test_supplier_signup_step1_de_loads(live_server, page):
|
||||
"""German variant of supplier signup wizard loads."""
|
||||
resp = page.goto(live_server + "/de/suppliers/signup")
|
||||
assert resp.ok, f"/de/suppliers/signup returned {resp.status}"
|
||||
page.wait_for_load_state("networkidle")
|
||||
expect(page.locator("[data-step='1']").first).to_be_visible()
|
||||
|
||||
|
||||
def test_supplier_signup_success_page_loads(live_server, page):
|
||||
"""Supplier signup success page renders."""
|
||||
resp = page.goto(live_server + "/en/suppliers/signup/success")
|
||||
assert resp.ok, f"signup success returned {resp.status}"
|
||||
expect(page.locator("h1, h2").first).to_be_visible()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# M. Supplier Dashboard (seeded supplier data)
|
||||
# =============================================================================
|
||||
|
||||
def test_supplier_dashboard_loads(live_server, page):
|
||||
"""Dashboard loads for authenticated supplier."""
|
||||
dev_login(page, live_server, "supplier@test.com")
|
||||
resp = page.goto(live_server + "/en/suppliers/dashboard")
|
||||
assert resp.ok, f"dashboard returned {resp.status}"
|
||||
expect(page.locator(".dash").first).to_be_visible()
|
||||
expect(page.locator(".dash-sidebar__name").first).to_be_visible()
|
||||
|
||||
|
||||
def test_supplier_dashboard_overview_tab(live_server, page):
|
||||
"""Overview tab renders with stats grid."""
|
||||
dev_login(page, live_server, "supplier@test.com")
|
||||
page.goto(live_server + "/en/suppliers/dashboard")
|
||||
page.wait_for_load_state("networkidle")
|
||||
page.wait_for_timeout(1000)
|
||||
stats = page.locator(".ov-stat")
|
||||
assert stats.count() >= 2, f"Expected >=2 stat cards, found {stats.count()}"
|
||||
|
||||
|
||||
def test_supplier_dashboard_boosts_tab(live_server, page):
|
||||
"""Boosts tab renders with boost options."""
|
||||
dev_login(page, live_server, "supplier@test.com")
|
||||
resp = page.goto(live_server + "/en/suppliers/dashboard/boosts")
|
||||
assert resp.ok, f"boosts tab returned {resp.status}"
|
||||
|
||||
|
||||
def test_supplier_dashboard_boosts_has_credit_packs(live_server, page):
|
||||
"""Boosts tab shows credit pack purchase options."""
|
||||
dev_login(page, live_server, "supplier@test.com")
|
||||
page.goto(live_server + "/en/suppliers/dashboard/boosts")
|
||||
page.wait_for_load_state("networkidle")
|
||||
content = page.content()
|
||||
assert "25" in content or "50" in content or "100" in content, (
|
||||
"No credit pack amounts found on boosts page"
|
||||
)
|
||||
|
||||
|
||||
def test_supplier_dashboard_listing_tab(live_server, page):
|
||||
"""Listing tab renders for authenticated supplier."""
|
||||
dev_login(page, live_server, "supplier@test.com")
|
||||
resp = page.goto(live_server + "/en/suppliers/dashboard/listing")
|
||||
assert resp.ok, f"listing tab returned {resp.status}"
|
||||
|
||||
|
||||
def test_supplier_dashboard_leads_tab(live_server, page):
|
||||
"""Leads tab renders for pro-tier supplier."""
|
||||
dev_login(page, live_server, "supplier@test.com")
|
||||
resp = page.goto(live_server + "/en/suppliers/dashboard/leads")
|
||||
assert resp.ok, f"leads tab returned {resp.status}"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# N. Business Plan Export
|
||||
# =============================================================================
|
||||
|
||||
def test_export_page_requires_auth(live_server, page):
|
||||
"""Export page redirects unauthenticated users to login."""
|
||||
page.goto(live_server + "/en/planner/export", wait_until="networkidle")
|
||||
assert "login" in page.url or "auth" in page.url, (
|
||||
f"Expected redirect to login, got: {page.url}"
|
||||
)
|
||||
|
||||
|
||||
def test_export_page_loads_for_authed_user(live_server, page):
|
||||
"""Export page renders for authenticated user with form."""
|
||||
dev_login(page, live_server, "export@example.com")
|
||||
resp = page.goto(live_server + "/en/planner/export")
|
||||
assert resp.ok, f"export page returned {resp.status}"
|
||||
expect(page.locator("h1").first).to_be_visible()
|
||||
expect(page.locator("#export-form").first).to_be_visible()
|
||||
expect(page.locator("#export-buy-btn").first).to_be_visible()
|
||||
|
||||
Reference in New Issue
Block a user