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.
72 lines
4.4 KiB
72 lines
4.4 KiB
|
14 hours ago
|
# -*- coding: utf-8 -*-
|
||
|
|
"""票16 UI 补充2:销售视角回退/冲突解除可见性 + scope 快速连点竞态"""
|
||
|
|
import io, json, pathlib, sys
|
||
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||
|
|
from playwright.sync_api import sync_playwright
|
||
|
|
|
||
|
|
AUD = pathlib.Path(r"E:\code\crm-backend-matt\.scratch\crm-project\audit")
|
||
|
|
SHOTS = AUD / "screenshots"
|
||
|
|
DEMO = pathlib.Path(r"E:\code\crm-backend-matt\.scratch\crm-project\demo\index.html")
|
||
|
|
SALES = "99021003"; BASE = "http://localhost:8080"
|
||
|
|
PID_RB = (AUD / "rbpid.txt").read_text().strip()
|
||
|
|
|
||
|
|
results = []
|
||
|
|
def step(name, ok, note=""):
|
||
|
|
results.append({"step": name, "ok": bool(ok), "note": str(note)[:400]})
|
||
|
|
print(("PASS " if ok else "!! ") + name + ((" | " + str(note)) if note else ""))
|
||
|
|
|
||
|
|
with sync_playwright() as p:
|
||
|
|
browser = p.chromium.launch(headless=True)
|
||
|
|
page = browser.new_context(viewport={"width": 1680, "height": 1000}).new_page()
|
||
|
|
page.set_default_timeout(15000)
|
||
|
|
page.goto(DEMO.as_uri() + "?api=" + BASE)
|
||
|
|
page.evaluate(f"() => fetch('{BASE}/api/auth/debug/token?userId={SALES}').then(r=>r.json()).then(j=>{{const d=j.data; localStorage.setItem('a5_token', typeof d==='string'?d:d.token);}})")
|
||
|
|
page.evaluate("() => localStorage.setItem('a5_me', JSON.stringify({userId:'99021003',userName:'A5销售'}))")
|
||
|
|
page.goto(DEMO.as_uri() + "?api=" + BASE); page.wait_for_timeout(1200)
|
||
|
|
|
||
|
|
# scope 快速连点竞态(销售 manage=本部门有数据 / mine=0)
|
||
|
|
page.click("nav.tabs button[data-tab='list']"); page.wait_for_timeout(300)
|
||
|
|
page.click("#tab-list [data-scope='mine']"); page.wait_for_timeout(2500)
|
||
|
|
t = page.inner_text("#listTotal")
|
||
|
|
step("R1 销售 mine 终态=共 0 条", "共 0 条" in t, t)
|
||
|
|
page.screenshot(path=str(SHOTS / "23-race-mine.png"), full_page=True)
|
||
|
|
for sc in ["manage", "mine", "manage", "mine", "mine"]:
|
||
|
|
page.click(f"#tab-list [data-scope='{sc}']"); page.wait_for_timeout(40)
|
||
|
|
page.wait_for_timeout(2500)
|
||
|
|
t2 = page.inner_text("#listTotal"); body = page.inner_text("#listBox")
|
||
|
|
consistent = ("共 0 条" in t2 and "无数据" in body) or ("共 0 条" not in t2 and "无数据" not in body)
|
||
|
|
step("R2 快速连点后 total 与行内容一致(无旧响应覆盖)", consistent, f"total={t2} body={body[:60]!r}")
|
||
|
|
|
||
|
|
page.click("nav.tabs button[data-tab='board']"); page.wait_for_timeout(1200)
|
||
|
|
ok = page.evaluate(f"() => {{ const cards=[...document.querySelectorAll('#boardBox .bcard')]; return cards.some(c => c.getAttribute('onclick').includes('{PID_RB}')); }}")
|
||
|
|
step("R2b 销售 manage 看板可见他人 stage2 卡", ok)
|
||
|
|
page.evaluate(f"() => openProject('{PID_RB}')"); page.wait_for_timeout(1200)
|
||
|
|
acts = page.inner_text("#detailActions")
|
||
|
|
step("R3 普通销售可见「回退…」按钮(原型: 仅管理员+销售总监可见)", "回退…" in acts, acts[:150])
|
||
|
|
page.screenshot(path=str(SHOTS / "25-sales-sees-rollback.png"), full_page=True)
|
||
|
|
page.click("#detailActions button:has-text('回退…')"); page.wait_for_timeout(900)
|
||
|
|
toast = page.inner_text("#toast")
|
||
|
|
step("R4 销售提交回退被拒 68005", "68005" in toast, toast)
|
||
|
|
page.screenshot(path=str(SHOTS / "26-sales-rollback-68005.png"), full_page=True)
|
||
|
|
|
||
|
|
page.click("nav.tabs button[data-tab='list']"); page.wait_for_timeout(600)
|
||
|
|
page.select_option("#fStage", "0"); page.wait_for_timeout(1200)
|
||
|
|
row0 = page.query_selector("#listBox tr.row")
|
||
|
|
if row0:
|
||
|
|
row0.click(); page.wait_for_timeout(1000)
|
||
|
|
acts = page.inner_text("#detailActions")
|
||
|
|
step("R5 普通销售在他人 stage0 项目可见「冲突解除」(原型: 仅报备专员+管理员)", "冲突解除" in acts, acts[:150])
|
||
|
|
page.screenshot(path=str(SHOTS / "27-sales-sees-filing.png"), full_page=True)
|
||
|
|
page.click("#detailActions button:has-text('冲突解除')"); page.wait_for_selector("#dlgFiling[open]")
|
||
|
|
page.select_option("#filingApproved", "true"); page.click("#dlgFiling .dfoot .ok"); page.wait_for_timeout(1200)
|
||
|
|
toast = page.inner_text("#toast")
|
||
|
|
step("R6 销售提交冲突解除成功(服务端无权限校验)", "报备审核回填成功" in toast, toast)
|
||
|
|
page.screenshot(path=str(SHOTS / "28-sales-filing-approved.png"), full_page=True)
|
||
|
|
else:
|
||
|
|
step("R5 无 stage0 行", False, "")
|
||
|
|
browser.close()
|
||
|
|
|
||
|
|
passed = sum(1 for r in results if r["ok"])
|
||
|
|
print(f"\n==== UI补 {passed}/{len(results)} PASS ====")
|
||
|
|
(AUD / "ui-result2.json").write_text(json.dumps(results, ensure_ascii=False, indent=1), encoding="utf-8")
|