File size: 5,266 Bytes
ca81019
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
"""Browser click-test of the dashboard address popup (view + drag-edit + history/revert), driven
by Playwright for Python against a locally-seeded app (see scripts/dev_seed.py). No Node / no MCP.

    # terminal 1
    ./.venv/Scripts/python.exe scripts/dev_seed.py
    # terminal 2
    ./.venv/Scripts/python.exe scripts/browser_smoke.py

Prints PASS/FAIL per step and drops screenshots in scripts/_browser_shots/.
"""

from __future__ import annotations

import sys
from pathlib import Path

from playwright.sync_api import expect, sync_playwright

BASE = sys.argv[1] if len(sys.argv) > 1 else "http://127.0.0.1:8000"
TOKEN = sys.argv[2] if len(sys.argv) > 2 else "devtoken"
SHOTS = Path(__file__).parent / "_browser_shots"
SHOTS.mkdir(exist_ok=True)

results: list[tuple[bool, str]] = []


def check(cond: bool, msg: str):
    results.append((bool(cond), msg))
    print(f"  {'PASS' if cond else 'FAIL'}  {msg}")


def run():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page(viewport={"width": 900, "height": 1200})
        page.goto(f"{BASE}/dashboard?token={TOKEN}", wait_until="networkidle")

        # 1) auto-login + nav renders
        page.wait_for_selector(".navlink", timeout=15000)
        check(page.is_hidden("#gate"), "signed in (gate hidden)")

        # 2) go to Leads, a batch row is clickable
        page.click('.navlink[data-target="leads"]')
        page.wait_for_selector("#leads a.viewl", timeout=10000)
        rows = page.locator("#leads a.viewl")
        check(rows.count() >= 1, f"Leads shows {rows.count()} clickable batch row(s)")
        # a batch contact reads "Batch: …"
        check("Batch:" in page.inner_text("#leads"), 'contact shows "Batch: <email>"')

        # 3) open the popup on the row that has prior history (the 8,600 edit)
        target = None
        for i in range(rows.count()):
            if "144th" in rows.nth(i).inner_text():
                target = rows.nth(i)
                break
        (target or rows.first).click()
        page.wait_for_selector("#resultModal.show", timeout=10000)
        page.wait_for_selector("#modalImgWrap img", timeout=10000)
        check(page.locator("#modalImgWrap img").count() == 1, "overlay image rendered in popup")
        check(page.locator("#modalPrice tr").count() >= 1, "price breakdown rows present")
        area_before = page.inner_text("#modalArea")
        check("sqft" in area_before, f"lawn area shown ({area_before})")
        page.screenshot(path=str(SHOTS / "01_popup_open.png"))

        # 4) drag editor present + a drag changes the area and enables Save
        check(page.locator("#modalImgWrap svg.lawnsvg .vh").count() >= 4, "drag handles present")
        handle = page.locator("#modalImgWrap svg.lawnsvg .vh").first
        box = handle.bounding_box()
        page.mouse.move(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
        page.mouse.down()
        page.mouse.move(box["x"] - 60, box["y"] - 60, steps=8)   # drag the corner outward
        page.mouse.up()
        page.wait_for_timeout(200)
        area_after = page.inner_text("#modalArea")
        check(area_after != area_before, f"area updates live on drag ({area_before} -> {area_after})")
        check(not page.locator("#modalSave").is_disabled(), "Save enabled after an edit")
        page.screenshot(path=str(SHOTS / "02_after_drag.png"))

        # 5) save persists (server round-trip) → "Saved."
        page.click("#modalSave")
        page.wait_for_function("document.getElementById('modalSaveMsg').textContent.includes('Saved')", timeout=10000)
        check(True, "Save round-trip succeeded (Saved.)")

        # 6) history + revert
        try:   # two async loadHistory() calls can flicker the box — expect() auto-retries
            expect(page.locator("#modalHistory")).to_contain_text("Measurement history", timeout=10000)
            check(True, "history section shown")
        except Exception:
            check(False, "history section shown")
        revs = page.locator("#modalHistory .hrev")
        check(revs.count() >= 1, f"{revs.count()} revert target(s) available")
        page.screenshot(path=str(SHOTS / "03_history.png"))
        area_pre_revert = page.inner_text("#modalArea")
        revs.first.click()
        page.wait_for_function(
            f"document.getElementById('modalArea').textContent !== {area_pre_revert!r}", timeout=10000)
        check(page.inner_text("#modalArea") != area_pre_revert,
              f"revert changed the area ({area_pre_revert} -> {page.inner_text('#modalArea')})")

        # 7) close
        page.click("#modalClose")
        page.wait_for_selector("#resultModal", state="hidden", timeout=5000)
        check(not page.is_visible("#resultModal"), "popup closes with X")
        page.screenshot(path=str(SHOTS / "04_closed.png"))

        browser.close()


if __name__ == "__main__":
    try:
        run()
    except Exception as exc:  # noqa: BLE001
        print(f"  FAIL  harness error: {type(exc).__name__}: {exc}")
        results.append((False, "harness error"))
    passed = sum(1 for ok, _ in results if ok)
    print(f"\n{passed}/{len(results)} checks passed")
    sys.exit(0 if passed == len(results) and results else 1)