feat(wiki): 新增 GHC06 任务页与图库发布体系,级联更新既有页面
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
"""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())
|
||||
Reference in New Issue
Block a user