"""End-to-end regression for every Guanghan mission gallery on Wiki.js.""" from __future__ import annotations import argparse import json import sys from pathlib import Path from playwright.sync_api import TimeoutError as PlaywrightTimeoutError from playwright.sync_api import sync_playwright REAL_GALLERIES = ( "BL-01", "BL-02", "BL-03", "GHC-01", "GHC-02", "GHC-03", "GHC-04", "GHC-05", "GHC-06", "JC-01", ) PLANNING_GALLERIES = ("JC-02",) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--base-url", default="http://118.89.192.134:59100") parser.add_argument("--screenshot-dir", type=Path) return parser.parse_args() def gallery_url(base_url: str, mission: str) -> str: return f"{base_url.rstrip('/')}/zh/home/mission/Guanghan_Program/{mission}/Gallery" def verify_real_gallery(page, mission: str, url: str, screenshot_dir: Path | None) -> dict: page.goto(url, wait_until="domcontentloaded", timeout=60_000) article = page.locator(".wiki-article") article.wait_for(state="attached", timeout=30_000) images = page.locator(".wiki-article img[data-wiki-asset]") images.first.wait_for(state="attached", timeout=30_000) image_count = images.count() if image_count < 1: raise AssertionError("no real gallery images found") page.wait_for_function( "document.querySelector('.wiki-article')?.dataset.galleryLightboxReady === '1'", timeout=15_000, ) first_image = images.first first_image.scroll_into_view_if_needed(timeout=15_000) first_image.wait_for(state="visible", timeout=15_000) first_src = first_image.get_attribute("data-wiki-asset") or first_image.get_attribute("src") first_image.click(timeout=15_000) overlay = page.locator(".wiki-image-lightbox:not([hidden])") overlay.wait_for(state="visible", timeout=10_000) preview = overlay.locator("img") page.wait_for_function( "document.querySelector('.wiki-image-lightbox:not([hidden]) img')?.naturalWidth > 0", timeout=30_000, ) assert overlay.locator(".wiki-image-lightbox-counter").inner_text().strip() == f"1 / {image_count}" assert preview.get_attribute("src") == first_src if screenshot_dir is not None: screenshot_dir.mkdir(parents=True, exist_ok=True) page.screenshot(path=str(screenshot_dir / f"{mission.lower()}-lightbox.png"), timeout=120_000) if image_count > 1: page.get_by_role("button", name="Next image").click(timeout=10_000) assert overlay.locator(".wiki-image-lightbox-counter").inner_text().strip() == f"2 / {image_count}" assert preview.get_attribute("src") != first_src page.keyboard.press("ArrowLeft") assert overlay.locator(".wiki-image-lightbox-counter").inner_text().strip() == f"1 / {image_count}" page.keyboard.press("Escape") overlay.wait_for(state="hidden", timeout=10_000) first_image.focus() page.keyboard.press("Enter") overlay.wait_for(state="visible", timeout=10_000) page.get_by_role("button", name="Close image preview").click(timeout=10_000) overlay.wait_for(state="hidden", timeout=10_000) return {"mission": mission, "images": image_count, "click": True, "next": image_count > 1, "keyboard": True} def verify_planning_gallery(page, mission: str, url: str) -> dict: page.goto(url, wait_until="domcontentloaded", timeout=60_000) page.locator(".wiki-article").wait_for(state="attached", timeout=30_000) page.locator(".wiki-article.gallery-planning").wait_for(state="attached", timeout=30_000) assert page.locator(".wiki-article.gallery-planning").count() == 1 assert page.locator("img[data-wiki-asset]").count() == 0 assert page.locator(".wiki-image-lightbox:not([hidden])").count() == 0 return {"mission": mission, "images": 0, "planning": True} def main() -> int: args = parse_args() results = [] failures = [] with sync_playwright() as playwright: browser = playwright.chromium.launch(headless=True) context = browser.new_context(viewport={"width": 1440, "height": 1000}) for mission in REAL_GALLERIES: page = context.new_page() page_errors = [] page.on("pageerror", lambda error, bucket=page_errors: bucket.append(str(error))) try: result = verify_real_gallery(page, mission, gallery_url(args.base_url, mission), args.screenshot_dir) if page_errors: raise AssertionError(f"page errors: {page_errors}") results.append(result) print(f"gallery_e2e_ok mission={mission} images={result['images']}", flush=True) except (AssertionError, PlaywrightTimeoutError, Exception) as exc: failures.append({"mission": mission, "error": f"{type(exc).__name__}: {exc}"}) print(f"gallery_e2e_failed mission={mission} error={type(exc).__name__}: {exc}", flush=True) finally: page.close() for mission in PLANNING_GALLERIES: page = context.new_page() try: results.append(verify_planning_gallery(page, mission, gallery_url(args.base_url, mission))) print(f"gallery_e2e_ok mission={mission} planning=true", flush=True) except (AssertionError, PlaywrightTimeoutError, Exception) as exc: failures.append({"mission": mission, "error": f"{type(exc).__name__}: {exc}"}) print(f"gallery_e2e_failed mission={mission} error={type(exc).__name__}: {exc}", flush=True) finally: page.close() browser.close() print(json.dumps({"results": results, "failures": failures}, ensure_ascii=False, indent=2)) return 1 if failures else 0 if __name__ == "__main__": sys.exit(main())