Files
FTC25009/scripts/verify_dense_outbound_ui.py

169 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Read-only Playwright verification for dense outbound and relocation screens."""
import json
import os
from pathlib import Path
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
from playwright.sync_api import sync_playwright
BASE_URL = os.getenv("WMS_TEST_BASE_URL", "http://127.0.0.1")
USERNAME = os.environ["WMS_TEST_USERNAME"]
PASSWORD = os.environ["WMS_TEST_PASSWORD"]
OUTPUT_DIR = Path(os.getenv(
"WMS_TEST_SCREENSHOT_DIR",
"docs/testing/screenshots/2026-07-16-dense-outbound",
)).resolve()
def wait_for_app(page):
try:
page.wait_for_load_state("networkidle", timeout=15000)
except PlaywrightTimeoutError:
page.wait_for_load_state("domcontentloaded")
page.wait_for_timeout(800)
def assert_text(page, *labels):
body = page.locator("body").inner_text()
for label in labels:
if label not in body:
raise AssertionError(f"Missing text {label!r} at {page.url}")
def page_overflow(page):
return page.evaluate("""() => ({
viewport: window.innerWidth,
documentWidth: document.documentElement.scrollWidth,
bodyWidth: document.body.scrollWidth
})""")
def wait_for_preview_dialog(page):
dialog = page.locator(".el-dialog:visible")
dialog.get_by_text("指定通道整托出库", exact=True).wait_for(timeout=10000)
dialog.locator(".el-loading-mask").wait_for(state="hidden", timeout=10000)
dialog.locator("tbody .el-table__row").first.wait_for(timeout=10000)
def main():
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
results = {"pages": {}, "consoleErrors": [], "requestFailures": []}
with sync_playwright() as playwright:
browser = playwright.chromium.launch(channel="chrome", headless=True)
context = browser.new_context(viewport={"width": 1440, "height": 1000})
page = context.new_page()
page.on("console", lambda msg: results["consoleErrors"].append(f"{page.url} :: {msg.text}")
if msg.type == "error" else None)
page.on("requestfailed", lambda request: results["requestFailures"].append(
f"{request.method} {request.url}: {request.failure}"))
page.goto(BASE_URL + "/login", wait_until="domcontentloaded")
wait_for_app(page)
page.get_by_placeholder("账号").fill(USERNAME)
page.get_by_placeholder("密码").fill(PASSWORD)
page.get_by_role("button", name="登 录").click()
page.wait_for_url(lambda url: "/login" not in url, timeout=15000)
wait_for_app(page)
pages = [
("outbound-job", "/out/outJob",
("WCS状态", "自动重试", "最后下发错误", "手动下发 WCS")),
("dense-lane-outbound", "/out/denseLaneOutbound",
("通道组编码", "满托盘数", "可出库托盘", "当前最外侧托盘", "整托出库")),
("kpi-plan", "/kpi/planKpi",
("货位平面图", "指定移库")),
]
for name, route, labels in pages:
page.goto(BASE_URL + route, wait_until="domcontentloaded")
wait_for_app(page)
assert_text(page, *labels)
screenshot = OUTPUT_DIR / f"{name}-desktop.png"
page.screenshot(path=str(screenshot), full_page=True)
results["pages"][name] = {
"url": page.url,
"desktopScreenshot": str(screenshot),
"desktopOverflow": page_overflow(page),
}
if name == "dense-lane-outbound":
lane_buttons = page.get_by_role("button", name="整托出库")
enabled_button = next((lane_buttons.nth(index)
for index in range(lane_buttons.count())
if lane_buttons.nth(index).is_enabled()), None)
if enabled_button is not None:
enabled_button.click()
wait_for_preview_dialog(page)
assert_text(page, "整托数量", "出库口", "本次准确出库托盘")
dialog_shot = OUTPUT_DIR / "dense-lane-outbound-dialog-real.png"
page.screenshot(path=str(dialog_shot), full_page=True)
results["pages"][name]["realDialogScreenshot"] = str(dialog_shot)
page.get_by_role("button", name="取消").last.click()
else:
results["pages"][name]["dialog"] = "skipped-no-available-lane"
synthetic_lane = {
"groupCoding": "VERIFY-LANE-01", "cargoArea": "G", "thePlatoon": 7,
"theFloor": 1, "directionType": "1", "fullTrayCount": 3,
"availableTrayCount": 2, "totalQuantity": 24,
"outermostTrayCode": "A-VERIFY-001", "outermostCargoSpaceId": "VERIFY-CS-003",
}
synthetic_detail = {
"projectNo": "VERIFY-PROJECT", "productId": "VERIFY-MATERIAL",
"productName": "验证物料", "lotNo": "VERIFY-LOT", "stockQuantity": 12,
}
synthetic_tray = {
"cargoSpaceId": "VERIFY-CS-003", "cargoSpaceName": "验证端头货位",
"trayCode": "A-VERIFY-001", "theColumn": 3, "stockQuantity": 12,
"stockDetails": [synthetic_detail],
}
page.route("**/dev-api/out/denseLaneOutbound/list*", lambda route: route.fulfill(
status=200, content_type="application/json",
body=json.dumps({"code": 200, "data": [synthetic_lane]}, ensure_ascii=False)))
page.route("**/dev-api/out/denseLaneOutbound/preview*", lambda route: route.fulfill(
status=200, content_type="application/json",
body=json.dumps({"code": 200, "data": {"trays": [synthetic_tray],
"totalQuantity": 12, "previewToken": "verify-token"}}, ensure_ascii=False)))
page.route("**/dev-api/bas/stationpoint/list*", lambda route: route.fulfill(
status=200, content_type="application/json",
body=json.dumps({"code": 200, "rows": [{"pointId": "VERIFY-OUT-1",
"pointName": "验证出库口", "pointType": "2", "pointStatus": "0",
"status": "0", "trayType": "A"}], "total": 1}, ensure_ascii=False)))
page.set_viewport_size({"width": 1440, "height": 1000})
page.goto(BASE_URL + "/out/denseLaneOutbound", wait_until="domcontentloaded")
wait_for_app(page)
page.get_by_role("button", name="整托出库").click()
wait_for_preview_dialog(page)
page.get_by_placeholder("请选择与托盘类型匹配的出库口").click()
page.get_by_text("验证出库口(VERIFY-OUT-1,出库,A类托盘)", exact=True).wait_for(timeout=5000)
assert_text(page, "VERIFY-LANE-01", "A-VERIFY-001", "VERIFY-CS-003",
"验证出库口", "本次准确出库托盘")
dialog_shot = OUTPUT_DIR / "dense-lane-outbound-dialog.png"
page.screenshot(path=str(dialog_shot), full_page=True)
results["pages"]["dense-lane-outbound"]["mockDialogScreenshot"] = str(dialog_shot)
results["pages"]["dense-lane-outbound"]["mockDialog"] = "verified-with-read-only-mocks"
page.set_viewport_size({"width": 390, "height": 844})
page.goto(BASE_URL + "/out/denseLaneOutbound", wait_until="domcontentloaded")
wait_for_app(page)
assert_text(page, "指定通道", "通道组编码")
narrow_shot = OUTPUT_DIR / "dense-lane-outbound-narrow.png"
page.screenshot(path=str(narrow_shot), full_page=True)
results["pages"]["dense-lane-outbound"]["narrowScreenshot"] = str(narrow_shot)
results["pages"]["dense-lane-outbound"]["narrowOverflow"] = page_overflow(page)
context.close()
browser.close()
ignored_console_fragments = ("ResizeObserver loop",)
results["consoleErrors"] = [message for message in results["consoleErrors"]
if not any(fragment in message for fragment in ignored_console_fragments)]
if results["consoleErrors"] or results["requestFailures"]:
raise AssertionError(json.dumps(results, ensure_ascii=False, indent=2))
print(json.dumps(results, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()