144 lines
5.8 KiB
Python
144 lines
5.8 KiB
Python
"""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())
|