feat(wiki): 新增 GHC06 任务页与图库发布体系,级联更新既有页面

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 15:03:14 +08:00
co-authored by Claude
parent 26d2fcd0b1
commit 4ef15545e3
34 changed files with 3515 additions and 1572 deletions
+143
View File
@@ -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())