Files

70 lines
4.3 KiB
Python

"""Reverse image search browser regression against scripts/preview.py only."""
import io
import sys
from pathlib import Path
from PIL import Image
from playwright.sync_api import sync_playwright, expect
base = sys.argv[1].rstrip("/") if len(sys.argv) > 1 else "http://127.0.0.1:8196"
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
context = browser.new_context(viewport={"width": 1440, "height": 960}, permissions=["clipboard-read", "clipboard-write"])
page = context.new_page()
errors, imports = [], []
page.on("pageerror", lambda error: errors.append(str(error)))
page.on("request", lambda request: imports.append(request.url) if request.url.endswith("/api/import") else None)
page.add_init_script("localStorage.setItem('universal-extractor:onboarding-tour-v1-completed', 'true')")
page.goto(base + "/gallery/", wait_until="networkidle")
images = page.request.get(base + "/universal_gallery/api/images").json()["images"]
original = next(item for item in images if item["filename"] == "Study_001.png")
payload = page.request.get(base + original["original_url"]).body()
page.get_by_role("button", name="以图搜图", exact=True).click()
dialog = page.get_by_role("dialog", name="以图搜图")
expect(dialog).to_be_visible()
dialog.locator('input[type="file"]').set_input_files({"name": "social-original.png", "mimeType": "image/png", "buffer": payload})
expect(dialog.get_by_text("文件完全相同", exact=True).first).to_be_visible(timeout=30000)
expect(dialog.get_by_text("已检索 18 / 18 张", exact=False)).to_be_visible(timeout=30000)
first = dialog.locator(".ue-image-search-result").first
expect(first).to_contain_text("Study_001.png")
first.get_by_role("button", name="复制完整路径").click()
assert page.evaluate("navigator.clipboard.readText()").endswith("Study_001.png")
shots = Path("artifacts/image-search")
shots.mkdir(parents=True, exist_ok=True)
page.screenshot(path=str(shots / "exact-desktop.png"), animations="disabled")
# Compression/resize emulates a platform-downloaded copy without metadata.
with Image.open(io.BytesIO(payload)) as image:
jpeg = io.BytesIO()
image.resize((180, 228)).convert("RGB").save(jpeg, "JPEG", quality=45)
(shots / "social-compressed.jpg").write_bytes(jpeg.getvalue())
dialog.locator('input[type="file"]').set_input_files({"name": "social-compressed.jpg", "mimeType": "image/jpeg", "buffer": jpeg.getvalue()})
expect(dialog.get_by_text("正在查找相同和相似图片…")).not_to_be_visible(timeout=10000)
expect(dialog.locator(".ue-image-search-result").first).to_contain_text("Study_001.png")
expect(dialog.locator(".ue-image-search-match").first).to_contain_text("视觉匹配分")
page.screenshot(path=str(shots / "compressed-desktop.png"), animations="disabled")
dialog.locator(".ue-image-search-result").first.get_by_role("button", name="定位文件夹").click()
expect(dialog).not_to_be_visible()
expect(page.locator(".ue-gallery-card")).to_have_count(6)
expect(page.locator(".ue-gallery-search-input")).to_have_value("")
expect(page.get_by_role("button", name="返回搜图结果与原浏览位置")).to_be_visible()
# Real clipboard image -> global Ctrl+V -> search, never import.
page.evaluate("""async url => {
const blob = await (await fetch(url)).blob();
await navigator.clipboard.write([new ClipboardItem({'image/png': blob})]);
}""", original["original_url"])
page.locator(".ue-filter-kicker").click()
page.keyboard.press("Control+v")
expect(dialog).to_be_visible()
# Native clipboard transfers pixels and may re-encode PNG/remove metadata.
expect(dialog.locator(".ue-image-search-result").first).to_contain_text("Study_001.png", timeout=10000)
# Mobile result paths and actions stay inside the viewport.
page.set_viewport_size({"width": 390, "height": 844})
assert dialog.evaluate("node => node.scrollWidth <= node.clientWidth + 1")
page.screenshot(path=str(shots / "mobile.png"), animations="disabled")
page.keyboard.press("Escape")
expect(dialog).not_to_be_visible()
assert imports == [], imports
assert errors == [], errors
print("PASS: exact, compressed/resized, full path copy, folder locate, real clipboard paste, mobile, no import, no JS errors")
browser.close()