You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
6.7 KiB
141 lines
6.7 KiB
|
7 hours ago
|
# -*- coding: utf-8 -*-
|
||
|
|
"""probe: 字典项「设为分组默认项」链路(弹窗勾选 / 新建反查联动 / 同组原默认自动取消 / 编辑回显 / 列表独立列)"""
|
||
|
|
import json, re, time, pathlib, sys, io, urllib.request, urllib.parse
|
||
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||
|
|
from playwright.sync_api import sync_playwright
|
||
|
|
|
||
|
|
ROOT = pathlib.Path(__file__).resolve().parent
|
||
|
|
DEMO = ROOT / "demo" / "index.html"
|
||
|
|
API = "http://localhost:8080"
|
||
|
|
TS = time.strftime("%H%M%S")
|
||
|
|
P_CODE, P_NAME = f"e2d_probe_d_{TS}", f"e2d-探针默认组-{TS}"
|
||
|
|
|
||
|
|
def step(n, ok, note=""):
|
||
|
|
print(("PASS " if ok else "FAIL ") + n + (" | " + note if note else ""))
|
||
|
|
return bool(ok)
|
||
|
|
|
||
|
|
tok = json.loads(urllib.request.urlopen(
|
||
|
|
f"{API}/api/auth/debug/token?userId=739564171091247104", timeout=10).read().decode())["data"]
|
||
|
|
|
||
|
|
def api_post(url, data=None):
|
||
|
|
body = urllib.parse.urlencode(data or {}).encode()
|
||
|
|
req = urllib.request.Request(url, data=body, method="POST")
|
||
|
|
req.add_header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
|
||
|
|
req.add_header("Authorization", "Bearer " + tok)
|
||
|
|
with urllib.request.urlopen(req, timeout=20) as r:
|
||
|
|
return json.loads(r.read().decode())
|
||
|
|
|
||
|
|
def page_items(gid):
|
||
|
|
r = api_post(f"{API}/api/dict/item/page", {"current": 1, "size": 500, "groupId": gid})
|
||
|
|
return (r.get("data") or {}).get("content", [])
|
||
|
|
|
||
|
|
def opt_val(page, sel, pat):
|
||
|
|
pairs = page.eval_on_selector_all(sel + " option", "os => os.map(o => [o.value, o.textContent])")
|
||
|
|
for v, t in pairs:
|
||
|
|
if re.search(pat, t): return v
|
||
|
|
return ""
|
||
|
|
|
||
|
|
console_errors, page_errors = [], []
|
||
|
|
ok_all = True
|
||
|
|
with sync_playwright() as p:
|
||
|
|
browser = p.chromium.launch(headless=True)
|
||
|
|
page = browser.new_page()
|
||
|
|
page.set_default_timeout(20000)
|
||
|
|
page.on("console", lambda m: console_errors.append(m.text) if m.type == "error" else None)
|
||
|
|
page.on("pageerror", lambda e: page_errors.append(str(e)))
|
||
|
|
page.on("dialog", lambda d: d.accept())
|
||
|
|
page.goto(DEMO.as_uri() + "?api=" + API)
|
||
|
|
page.wait_for_load_state("networkidle")
|
||
|
|
page.click("button:has-text('uid 登录')")
|
||
|
|
page.wait_for_selector(".toast-item:has-text('登录成功')", timeout=15000)
|
||
|
|
|
||
|
|
# 建组
|
||
|
|
page.click("button:has-text('+ 新建分组')")
|
||
|
|
page.wait_for_selector("#fgName")
|
||
|
|
page.fill("#fgName", P_NAME); page.fill("#fgCode", P_CODE)
|
||
|
|
page.click(".dfoot button.ok")
|
||
|
|
page.wait_for_selector(".toast-item:has-text('分组保存成功')", timeout=15000)
|
||
|
|
page.wait_for_timeout(400)
|
||
|
|
|
||
|
|
page.click("nav.tabs button[data-tab='item']")
|
||
|
|
page.wait_for_selector("#iGroup option", state="attached", timeout=15000)
|
||
|
|
p_id = opt_val(page, "#iGroup", re.escape(P_NAME))
|
||
|
|
page.select_option("#iGroup", p_id)
|
||
|
|
page.wait_for_timeout(400)
|
||
|
|
|
||
|
|
def create_item(name, value, set_default):
|
||
|
|
page.click("button:has-text('+ 新建字典项')")
|
||
|
|
page.wait_for_selector("#fiName")
|
||
|
|
page.select_option("#fiGroup", p_id)
|
||
|
|
page.wait_for_timeout(300)
|
||
|
|
if set_default: page.check("#fiDefault")
|
||
|
|
page.fill("#fiName", name); page.fill("#fiValue", value)
|
||
|
|
page.evaluate("document.getElementById('toast').innerHTML = ''")
|
||
|
|
page.click(".dfoot button.ok")
|
||
|
|
page.wait_for_selector(".toast-item:has-text('字典项保存成功')", timeout=15000)
|
||
|
|
page.wait_for_timeout(700)
|
||
|
|
|
||
|
|
# 1. 新建 A1 勾选默认(新建态:saveOrUpdate 返回 Void → groupId+键值反查联动 set-default)
|
||
|
|
create_item("e2d-默认A1", "av1", True)
|
||
|
|
rows = page_items(p_id)
|
||
|
|
a1 = next((x for x in rows if x.get("value") == "av1"), {})
|
||
|
|
ok_all &= step("新建勾默认 → 反查联动 set-default", a1.get("isDefault") is True,
|
||
|
|
f"A1.isDefault={a1.get('isDefault')}")
|
||
|
|
|
||
|
|
# 2. 新建 A2 勾默认 → 同组原默认自动取消
|
||
|
|
create_item("e2d-默认A2", "av2", True)
|
||
|
|
rows = page_items(p_id)
|
||
|
|
a1 = next((x for x in rows if x.get("value") == "av1"), {})
|
||
|
|
a2 = next((x for x in rows if x.get("value") == "av2"), {})
|
||
|
|
ok_all &= step("同组新默认生效且原默认自动取消",
|
||
|
|
a2.get("isDefault") is True and a1.get("isDefault") is False,
|
||
|
|
f"A1={a1.get('isDefault')} A2={a2.get('isDefault')}")
|
||
|
|
|
||
|
|
# 3. 编辑当前默认项 A2:checkbox 回显 checked+disabled
|
||
|
|
a2_id = str(a2.get("id"))
|
||
|
|
page.click(f"#iBody button[onclick*=\"dlgItem('{a2_id}')\"]")
|
||
|
|
page.wait_for_selector("#fiDefault")
|
||
|
|
page.wait_for_timeout(400)
|
||
|
|
ck = page.eval_on_selector("#fiDefault", "e => ({c: e.checked, d: e.disabled})")
|
||
|
|
ok_all &= step("编辑默认项勾选框回显 checked+disabled", ck["c"] is True and ck["d"] is True, str(ck))
|
||
|
|
page.click(".dfoot button:has-text('取消')")
|
||
|
|
page.wait_for_timeout(300)
|
||
|
|
|
||
|
|
# 4. 编辑非默认项 A1:可勾 → 勾上保存 → 默认切换
|
||
|
|
a1_id = str(a1.get("id"))
|
||
|
|
page.click(f"#iBody button[onclick*=\"dlgItem('{a1_id}')\"]")
|
||
|
|
page.wait_for_selector("#fiDefault")
|
||
|
|
page.wait_for_timeout(400)
|
||
|
|
ck = page.eval_on_selector("#fiDefault", "e => ({c: e.checked, d: e.disabled})")
|
||
|
|
ok_all &= step("编辑非默认项勾选框可选", ck["c"] is False and ck["d"] is False, str(ck))
|
||
|
|
page.check("#fiDefault")
|
||
|
|
page.evaluate("document.getElementById('toast').innerHTML = ''")
|
||
|
|
page.click(".dfoot button.ok")
|
||
|
|
page.wait_for_selector(".toast-item:has-text('字典项保存成功')", timeout=15000)
|
||
|
|
page.wait_for_timeout(700)
|
||
|
|
rows = page_items(p_id)
|
||
|
|
a1 = next((x for x in rows if x.get("value") == "av1"), {})
|
||
|
|
a2 = next((x for x in rows if x.get("value") == "av2"), {})
|
||
|
|
ok_all &= step("编辑态勾默认保存 → 默认切换(A1是/A2否)",
|
||
|
|
a1.get("isDefault") is True and a2.get("isDefault") is False,
|
||
|
|
f"A1={a1.get('isDefault')} A2={a2.get('isDefault')}")
|
||
|
|
|
||
|
|
# 5. 列表独立「默认」列渲染
|
||
|
|
page.wait_for_selector(f"#iBody tr:has-text('e2d-默认A1') .badge.b5:has-text('默认')", timeout=10000)
|
||
|
|
a2_no = page.locator("#iBody tr:has-text('e2d-默认A2')").locator("td span.mark").filter(has_text="否").count()
|
||
|
|
ok_all &= step("列表独立「默认」列(badge/否)", a2_no >= 1, f"A2行否标记={a2_no}")
|
||
|
|
|
||
|
|
# 清理
|
||
|
|
for iid in (a1_id, a2_id):
|
||
|
|
try: api_post(f"{API}/api/dict/item/delete?id={iid}", {})
|
||
|
|
except Exception: api_post(f"{API}/api/dict/item/force-delete?id={iid}", {})
|
||
|
|
try: api_post(f"{API}/api/dict/group/delete?id={p_id}", {})
|
||
|
|
except Exception as ex: print("组删除失败:", ex)
|
||
|
|
page.screenshot(path=str(ROOT / "shots" / f"probe-default-{TS}.png"))
|
||
|
|
browser.close()
|
||
|
|
|
||
|
|
ok_all &= (len(console_errors) == 0 and len(page_errors) == 0)
|
||
|
|
step("console/page error 零", len(console_errors) == 0 and len(page_errors) == 0,
|
||
|
|
f"console={console_errors[:2]} page={page_errors[:2]}")
|
||
|
|
print("ALL_PASS =", ok_all)
|