Files
goodwe-addon/goodwe_controller/tools/ux13_screenshots.py
T
Claude dev agent 667b8e9145
checks / unit (pull_request) Successful in 10m45s
infra-pin / fresh (pull_request) Successful in 1s
UX-13 P5: Dutch text, STYLE check, DOCS screenshots, period editor
- app/ui_text/nl.json and app/uitext.py: every page word in Dutch, English
  fallback per key; the page picks by browser language. Home's sentences
  move into ui_text `home_words`, so /status?lang=nl builds them in Dutch.
- test_ui_text.py: EN/NL key and placeholder parity, STYLE limits, jargon
  lists for both languages, no inline details, untranslated Dutch caught.
  Shared rules moved unchanged to tools/style_rules.py.
- tools/ux13_screenshots.sh/.py: DOCS screenshots from the demo fixtures in
  headless Chromium; docs/img/ux13/. Battery modes (P3): no screenshot.
- "Other time windows": a list editor (ui.periods) that builds
  static_profile and names bad times, empty prices, overlaps and gaps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:33:32 +02:00

97 lines
3.6 KiB
Python

"""UX-13 P5: the DOCS screenshots of the new screens, taken from the real page.
Run by tools/ux13_screenshots.sh, which serves tools/ux13_demo.py fixtures and
runs this inside the Playwright image. Each shot fails the run on a page
error, so a broken screen is never pictured.
python3 ux13_screenshots.py OUT_DIR # DEMO_<STATE> = the fixture's URL
The battery-mode screens (P3) are not built, so there is no shot of them:
DOCS.md says so instead of showing a mock-up.
"""
import os
import sys
from playwright.sync_api import sync_playwright
OUT = sys.argv[1]
DESKTOP = {"width": 1000, "height": 900}
PHONE = {"width": 390, "height": 844}
def url(state):
return os.environ[f"DEMO_{state.upper()}"]
def shot(browser, name, state, route, *, lang="en-GB", size=DESKTOP,
full=True, act=None, only=None):
ctx = browser.new_context(locale=lang, viewport=size,
color_scheme="light", reduced_motion="reduce")
page = ctx.new_page()
errors = []
page.on("pageerror", lambda e: errors.append(str(e)))
page.goto(url(state) + route)
page.wait_for_load_state("networkidle")
page.wait_for_timeout(1500) # the first /status and checks
if act:
act(page)
page.wait_for_timeout(1500) # the debounced start checks
path = os.path.join(OUT, name + ".png")
if only:
page.locator(only).screenshot(path=path)
else:
page.screenshot(path=path, full_page=full)
ctx.close()
if errors:
raise SystemExit(f"{name}: page errors {errors}")
print(f" {name}.png")
def open_tariff(page):
page.locator("details.grp[data-g=tariff] > summary").click()
page.locator("details.grp[data-g=meter] > summary").click() # close it
# The fixture site is on a dynamic contract: its fallback gets windows.
page.get_by_role("radiogroup", name="Price when the feed is down") \
.get_by_role("radio", name="Other time windows").click()
def windows(page, overlap=False):
open_tariff(page)
add = page.get_by_role("button", name="+ Add a time window")
rows = (("07:00", "23:00" if overlap else "22:00", "mon-fri", "0.34"),
("22:00", "07:00", "mon-fri", "0.26"),
("00:00", "24:00", "sat,sun", "0.24"))
for i, (a, b, days, price) in enumerate(rows, start=1):
add.click()
page.get_by_label(f"Window {i}, From").fill(a)
page.get_by_label(f"Window {i}, To").fill(b)
page.get_by_label(f"Window {i}, Days").select_option(days)
page.get_by_label(f"Window {i}, Price").fill(price)
page.locator("[data-key='ui.periods']").scroll_into_view_if_needed()
def main():
os.makedirs(OUT, exist_ok=True)
with sync_playwright() as p:
b = p.chromium.launch()
shot(b, "home-running", "running", "#home")
shot(b, "home-running-nl", "running", "#home", lang="nl-BE")
shot(b, "home-shadow", "shadow", "#home")
shot(b, "home-phone", "running", "#home", size=PHONE)
shot(b, "home-setup-only", "setup", "#home", full=False)
shot(b, "settings-installer", "running", "#settings")
shot(b, "settings-installer-nl", "running", "#settings", lang="nl-BE")
tariff = "details.grp[data-g=tariff]"
shot(b, "settings-time-windows", "running", "#settings",
act=windows, only=tariff)
shot(b, "settings-time-windows-overlap", "running", "#settings",
act=lambda page: windows(page, overlap=True), only=tariff)
shot(b, "setup-step-1", "new", "#setup")
shot(b, "setup-step-1-nl", "new", "#setup", lang="nl-BE")
b.close()
if __name__ == "__main__":
main()