Commit 57cf814d authored by wenwen.tang's avatar wenwen.tang 😕

test(ui): ui_verify1-7 加 RENTOS_BASE 闸门并适配账期折叠,新增 ui_verify11

parent 9326844a
......@@ -4,9 +4,9 @@
====================================
台账(房租车位费)→ 改单元格 → 增行 → 删行 → 搜索 → 电费手工录入 → 还原现场。
运行:
python tools/ui_verify.py
RENTOS_BASE=http://127.0.0.1:8901 python tools/ui_verify.py
运行(该脚本会写数据,默认目标 8899 是【真实台账】,必须走沙箱):
python tools/ui_verify_runner.py tools/ui_verify.py
RENTOS_ALLOW_REAL=1 python tools/ui_verify.py # 确需直连真实服务才这样用
覆盖的改造点:
* 只有一张台账「房租车位费」(旧的两张已退休)
......@@ -21,7 +21,18 @@ warnings.filterwarnings("ignore")
from playwright.sync_api import sync_playwright
BASE = os.environ.get("RENTOS_BASE", "http://127.0.0.1:8899")
BASE = os.environ.get("RENTOS_BASE", "")
if not BASE:
# 该脚本会写数据;未注入 RENTOS_BASE 时默认目标 8899 是【真实台账】,
# 直连会污染权威数据(2026-10-13 曾误写入 17 个测试批次)。
# 正常路径:tools/ui_verify_runner.py(沙箱 + 注入 RENTOS_BASE)。
if os.environ.get("RENTOS_ALLOW_REAL") == "1":
BASE = "http://127.0.0.1:8899"
else:
_me = os.path.basename(__file__)
sys.exit("已阻止:%s 未设置 RENTOS_BASE(默认 8899 是【真实台账】)。\n"
" 沙箱运行: python tools/ui_verify_runner.py tools/%s\n"
" 确需打真实服务:RENTOS_ALLOW_REAL=1 python tools/%s" % (_me, _me, _me))
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "shots")
os.makedirs(OUT, exist_ok=True)
......
This diff is collapsed.
......@@ -2,19 +2,31 @@
"""
界面验证:两张账单发送计划 / 免租期 / 台账锁定与二次确认 / 撤销 / 无「序号」列
运行:
python tools/ui_verify2.py
RENTOS_BASE=http://127.0.0.1:8901 python tools/ui_verify2.py
运行(该脚本会写数据,默认目标 8899 是【真实台账】,必须走沙箱):
python tools/ui_verify_runner.py tools/ui_verify2.py
RENTOS_ALLOW_REAL=1 python tools/ui_verify2.py # 确需直连真实服务才这样用
"""
import json
import os
import re
import sys
import warnings
warnings.filterwarnings("ignore")
from playwright.sync_api import sync_playwright # noqa: E402
BASE = os.environ.get("RENTOS_BASE", "http://127.0.0.1:8899")
BASE = os.environ.get("RENTOS_BASE", "")
if not BASE:
# 该脚本会写数据;未注入 RENTOS_BASE 时默认目标 8899 是【真实台账】,
# 直连会污染权威数据(2026-10-13 曾误写入 17 个测试批次)。
# 正常路径:tools/ui_verify_runner.py(沙箱 + 注入 RENTOS_BASE)。
if os.environ.get("RENTOS_ALLOW_REAL") == "1":
BASE = "http://127.0.0.1:8899"
else:
_me = os.path.basename(__file__)
sys.exit("已阻止:%s 未设置 RENTOS_BASE(默认 8899 是【真实台账】)。\n"
" 沙箱运行: python tools/ui_verify_runner.py tools/%s\n"
" 确需打真实服务:RENTOS_ALLOW_REAL=1 python tools/%s" % (_me, _me, _me))
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "shots2")
os.makedirs(OUT, exist_ok=True)
......@@ -49,13 +61,14 @@ def main():
page.wait_for_url("**/admin.html")
page.wait_for_timeout(1200)
# ============================================ 1. 设置页:两张账单
print("\n== 1. 设置页:两张账单发送计划 ==")
# ============================================ 1. 设置页:只剩房租车位费
print("\n== 1. 设置页:只配置「房租车位费」(电费已删) ==")
page.goto(f"{BASE}/admin.html#/settings")
page.wait_for_selector("#planRows tr[data-plan]", timeout=20000)
page.wait_for_timeout(800)
rows = page.eval_on_selector_all("#planRows tr[data-plan]", "els=>els.map(e=>e.dataset.plan)")
ck("两张账单计划各一行", rows == ["elec", "rent_parking"], str(rows))
ck("电费通知单已从定时发送设置里删掉,只剩房租车位费",
rows == ["rent_parking"], str(rows))
ck("退休的旧账单键已消失",
"rent_summary_367" not in rows and "rent_contract_8f11f" not in rows, str(rows))
for k in rows:
......@@ -71,24 +84,30 @@ def main():
ck(f"{k} 四个控件齐全", got["en"] and got["adv"] and got["tm"] and got["rf"],
json.dumps(got, ensure_ascii=False))
names = page.eval_on_selector_all("#planRows .bill-name", "e=>e.map(x=>x.innerText)")
ck("账单名称 = 电费 / 房租车位费",
names == ["电费收费通知书", "房租车位费收费通知书"], str(names))
ck("账单名称只剩「房租车位费收费通知书」",
names == ["房租车位费收费通知书"], str(names))
ck("页面不再出现「三张账单」",
"三张账单" not in page.eval_on_selector("#page-settings", "e=>e.innerText"))
ck("设置页不再重复展示「出租方 · 汇款公司」",
"汇款公司" not in page.eval_on_selector("#page-settings", "e=>e.innerText"))
page.screenshot(path=os.path.join(OUT, "S1-plans.png"), full_page=True)
print("\n== 2. 免租期卡片 ==")
rf = page.eval_on_selector("#rentfreeBox", "e=>e.innerText")
ck("免租期卡片列出两种免租类型", "首N月免租" in rf and "每年8-9月免租" in rf)
ck("免租期列出 3 段(315室 / 302室 / 303室)", "共 3 段" in rf, rf.split("\n")[0][:80])
# 段数跟着台账走(不写死):801/802 登记「每年8-9月免租」后会按年展开
ck("免租期卡片给出「共 N 段」汇总",
re.search(r"共 \d+ 段", rf) is not None, rf.split("\n")[0][:80])
ck("免租期卡片列出真实台账里的免租房间",
all(u in rf for u in ("315室", "302室", "303室")), rf[:120])
ck("免租期表格有 302室", "302室" in rf and "2026-08-16" in rf)
ck("免租期来源标注为房租车位费台账", "房租车位费" in rf or "台账" in rf)
page.screenshot(path=os.path.join(OUT, "S2-rentfree.png"), full_page=True)
# 记下初始计划,最后原样还原(脚本不该改变现场配置)
adv = '#planRows tr[data-plan="elec"] input[data-f="advance_days"]'
tm = '#planRows tr[data-plan="elec"] input[data-f="send_time"]'
en = '#planRows tr[data-plan="elec"] input[data-f="enabled"]'
adv = '#planRows tr[data-plan="rent_parking"] input[data-f="advance_days"]'
tm = '#planRows tr[data-plan="rent_parking"] input[data-f="send_time"]'
en = '#planRows tr[data-plan="rent_parking"] input[data-f="enabled"]'
init = {"master": page.input_value("#st-enabled"), "adv": page.input_value(adv),
"tm": page.input_value(tm), "en": page.is_checked(en)}
......@@ -128,10 +147,10 @@ def main():
ck("确认后提前天数=7 已落盘", page.input_value(adv) == "7", page.input_value(adv))
ck("确认后发送时点=07:30 已落盘", page.input_value(tm) == "07:30", page.input_value(tm))
st_txt = page.eval_on_selector(
'#planRows tr[data-plan="elec"] .plan-state', "e=>e.innerText")
ck("状态列给出运行状态(排期说明 / 暂无通知书 / 未启用)",
("缴费期限" in st_txt) or ("还没有可发送的通知书" in st_txt) or ("未启用" in st_txt),
st_txt.replace("\n", " | ")[:110])
'#planRows tr[data-plan="rent_parking"] .plan-state', "e=>e.innerText")
ck("状态列给出运行状态(排期说明 / 未启用 / 无待发)",
("推送日" in st_txt) or ("待出单" in st_txt) or ("未启用" in st_txt)
or ("无待发" in st_txt), st_txt.replace("\n", " | ")[:110])
page.screenshot(path=os.path.join(OUT, "S4-saved.png"), full_page=True)
print("\n== 4b. 关闭总开关时不再排期 ==")
......@@ -143,10 +162,10 @@ def main():
page.reload(wait_until="networkidle")
page.wait_for_selector("#planRows tr[data-plan]", timeout=20000)
page.wait_for_timeout(600)
ck("总开关关闭 → 两张账单状态均为 off",
ck("总开关关闭 → 计划状态为「总开关关闭」",
page.eval_on_selector_all("#planRows tr[data-plan] .plan-state .l1",
"e=>e.map(x=>x.innerText)")
== ["总开关关闭", "总开关关闭"],
== ["总开关关闭"],
str(page.eval_on_selector_all("#planRows tr[data-plan] .plan-state .l1",
"e=>e.map(x=>x.innerText)")))
......
......@@ -10,9 +10,9 @@
5. 「房租车位费」台账:8F/11F 付款周期 = 3 个月
6. 全程页面无 JS 报错
运行:
python tools/ui_verify3.py # 默认 http://127.0.0.1:8899
RENTOS_BASE=http://127.0.0.1:8901 python tools/ui_verify3.py
运行(该脚本会写数据,默认目标 8899 是【真实台账】,必须走沙箱):
python tools/ui_verify_runner.py tools/ui_verify3.py
RENTOS_ALLOW_REAL=1 python tools/ui_verify3.py # 确需直连真实服务才这样用
"""
import base64
import io
......@@ -24,7 +24,18 @@ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
from playwright.sync_api import sync_playwright # noqa: E402
BASE = os.environ.get("RENTOS_BASE", "http://127.0.0.1:8899")
BASE = os.environ.get("RENTOS_BASE", "")
if not BASE:
# 该脚本会写数据;未注入 RENTOS_BASE 时默认目标 8899 是【真实台账】,
# 直连会污染权威数据(2026-10-13 曾误写入 17 个测试批次)。
# 正常路径:tools/ui_verify_runner.py(沙箱 + 注入 RENTOS_BASE)。
if os.environ.get("RENTOS_ALLOW_REAL") == "1":
BASE = "http://127.0.0.1:8899"
else:
_me = os.path.basename(__file__)
sys.exit("已阻止:%s 未设置 RENTOS_BASE(默认 8899 是【真实台账】)。\n"
" 沙箱运行: python tools/ui_verify_runner.py tools/%s\n"
" 确需打真实服务:RENTOS_ALLOW_REAL=1 python tools/%s" % (_me, _me, _me))
USER, PWD = "admin", "1989@rent05"
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "shots3")
PNG_B64 = ("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGA"
......
......@@ -6,14 +6,14 @@
1. 导航出现「公司管理」,深链 #/companies 可直达,共 5 项
2. 租客 / 往来单位名册:加载、类型标签、搜索过滤、录入、行内编辑改名、删除
3. 出租方 · 汇款公司:加载、引用单元数、录入、改名(提示会同步台账)、删除
4. 设置页不再有旧的「汇款公司」编辑卡片(只剩指引)
4. 设置页不再有旧的「汇款公司」编辑卡片,也不再重复展示(名单只在「公司管理」维护)
5. 选择场景接入:电费录入「单位名称 / 出租方」有候选并在填房间号时自动带出
6. 台账「现租客」列编辑时挂上名册候选
7. 全程页面无 JS 报错,且测试用完即清理(不留脏数据)
运行:
python tools/ui_verify4.py # 默认 8899
RENTOS_BASE=http://127.0.0.1:8901 python tools/ui_verify4.py
运行(该脚本会写数据,默认目标 8899 是【真实台账】,必须走沙箱):
python tools/ui_verify_runner.py tools/ui_verify4.py
RENTOS_ALLOW_REAL=1 python tools/ui_verify4.py # 确需直连真实服务才这样用
"""
import io
import os
......@@ -24,7 +24,18 @@ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
from playwright.sync_api import sync_playwright # noqa: E402
BASE = os.environ.get("RENTOS_BASE", "http://127.0.0.1:8899")
BASE = os.environ.get("RENTOS_BASE", "")
if not BASE:
# 该脚本会写数据;未注入 RENTOS_BASE 时默认目标 8899 是【真实台账】,
# 直连会污染权威数据(2026-10-13 曾误写入 17 个测试批次)。
# 正常路径:tools/ui_verify_runner.py(沙箱 + 注入 RENTOS_BASE)。
if os.environ.get("RENTOS_ALLOW_REAL") == "1":
BASE = "http://127.0.0.1:8899"
else:
_me = os.path.basename(__file__)
sys.exit("已阻止:%s 未设置 RENTOS_BASE(默认 8899 是【真实台账】)。\n"
" 沙箱运行: python tools/ui_verify_runner.py tools/%s\n"
" 确需打真实服务:RENTOS_ALLOW_REAL=1 python tools/%s" % (_me, _me, _me))
USER, PWD = "admin", "1989@rent05"
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "shots4")
......@@ -278,7 +289,8 @@ def main():
ck("旧汇款公司编辑区已移除",
page.eval_on_selector_all("#ownersBox, #btn-save-owners, #btn-reset-owners", "e => e.length") == 0)
txt = page.inner_text("#page-settings")
ck("留有指向公司管理的指引", "公司管理" in txt)
ck("设置页不再重复展示「出租方 · 汇款公司」(已迁走,不留冗余指引)",
"汇款公司" not in txt, txt[:80].replace("\n", " "))
page.screenshot(path=os.path.join(OUT, "C4-settings.png"), full_page=True)
# ============================================================ 5
......
......@@ -23,9 +23,9 @@
* 结束时逐格核对台账「账单起始日」与跑前一致,不一致就报 FAIL;
* 本次生成的产物文件会被清理(批次 JSON 由删除步骤清掉)。
运行:
python tools/ui_verify5.py # 默认 8899
RENTOS_BASE=http://127.0.0.1:8901 python tools/ui_verify5.py
运行(该脚本会写数据,默认目标 8899 是【真实台账】,必须走沙箱):
python tools/ui_verify_runner.py tools/ui_verify5.py
RENTOS_ALLOW_REAL=1 python tools/ui_verify5.py # 确需直连真实服务才这样用
"""
import io
import os
......@@ -37,7 +37,18 @@ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from playwright.sync_api import sync_playwright # noqa: E402
BASE = os.environ.get("RENTOS_BASE", "http://127.0.0.1:8899")
BASE = os.environ.get("RENTOS_BASE", "")
if not BASE:
# 该脚本会写数据;未注入 RENTOS_BASE 时默认目标 8899 是【真实台账】,
# 直连会污染权威数据(2026-10-13 曾误写入 17 个测试批次)。
# 正常路径:tools/ui_verify_runner.py(沙箱 + 注入 RENTOS_BASE)。
if os.environ.get("RENTOS_ALLOW_REAL") == "1":
BASE = "http://127.0.0.1:8899"
else:
_me = os.path.basename(__file__)
sys.exit("已阻止:%s 未设置 RENTOS_BASE(默认 8899 是【真实台账】)。\n"
" 沙箱运行: python tools/ui_verify_runner.py tools/%s\n"
" 确需打真实服务:RENTOS_ALLOW_REAL=1 python tools/%s" % (_me, _me, _me))
OUT = os.environ.get("RENTOS_SHOTS", os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "output", "ui_shots"))
USER, PWD = "admin", "1989@rent05"
......@@ -275,14 +286,21 @@ def main():
# ============================================ 7. 批次台账 / 类别隔离
print("\n7. 批次台账 · 类别隔离")
kinds = p.eval_on_selector_all("#rpBatchList tbody tr td:first-child",
# 批次行默认按账期折叠;只展开仍折叠的分组(hash 导航不刷新页面,状态会延续)
js("""() => { document.querySelectorAll('#rpBatchList tbody.pgrp.collapsed tr.grp')
.forEach(tr => tr.click()); }""")
p.wait_for_timeout(500)
kinds = p.eval_on_selector_all("#rpBatchList tbody tr.prow td:first-child",
"e=>e.map(x=>x.innerText.trim())")
ck("房租批次表的「账单」列标出「房租车位费」",
kinds and all(k == "房租车位费" for k in kinds), str(kinds[:3]))
p.goto(BASE + "/admin.html#/elec")
p.wait_for_selector("#eGrid", timeout=15000)
p.wait_for_timeout(1800)
elec_ids = p.eval_on_selector_all("#batchList tbody tr td:nth-child(2)",
js("""() => { document.querySelectorAll('#batchList tbody.pgrp.collapsed tr.grp')
.forEach(tr => tr.click()); }""")
p.wait_for_timeout(400)
elec_ids = p.eval_on_selector_all("#batchList tbody tr.prow td:nth-child(2)",
"e=>e.map(x=>x.innerText.trim())")
ck("电费页的批次列表不含刚生成的房租批次",
(created_batch or "x") not in elec_ids, str(elec_ids))
......@@ -292,6 +310,9 @@ def main():
p.goto(BASE + "/admin.html#/table/rent_parking")
p.wait_for_selector("#rpWrap table", timeout=20000)
p.wait_for_timeout(1200)
js("""() => { document.querySelectorAll('#rpBatchList tbody.pgrp.collapsed tr.grp')
.forEach(tr => tr.click()); }""")
p.wait_for_timeout(400)
if created_batch:
p.click('#rpBatchList button[data-open="%s"]' % created_batch)
p.wait_for_timeout(1500)
......
......@@ -15,7 +15,7 @@
* 只出 1 行单,跑完**必定删除该批次并回滚账期**(try/finally 兜底);
* 结束时核对台账 AC/AJ 与跑前一致。
运行:python tools/ui_verify6.py (需先起服务)
运行:python tools/ui_verify_runner.py tools/ui_verify6.py (该脚本会写数据,沙箱隔离)
"""
import io
import os
......@@ -25,7 +25,18 @@ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from playwright.sync_api import sync_playwright # noqa: E402
BASE = os.environ.get("RENTOS_BASE", "http://127.0.0.1:8899")
BASE = os.environ.get("RENTOS_BASE", "")
if not BASE:
# 该脚本会写数据;未注入 RENTOS_BASE 时默认目标 8899 是【真实台账】,
# 直连会污染权威数据(2026-10-13 曾误写入 17 个测试批次)。
# 正常路径:tools/ui_verify_runner.py(沙箱 + 注入 RENTOS_BASE)。
if os.environ.get("RENTOS_ALLOW_REAL") == "1":
BASE = "http://127.0.0.1:8899"
else:
_me = os.path.basename(__file__)
sys.exit("已阻止:%s 未设置 RENTOS_BASE(默认 8899 是【真实台账】)。\n"
" 沙箱运行: python tools/ui_verify_runner.py tools/%s\n"
" 确需打真实服务:RENTOS_ALLOW_REAL=1 python tools/%s" % (_me, _me, _me))
OUT = os.environ.get("RENTOS_SHOTS", os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "output", "ui_shots"))
USER, PWD = "admin", "1989@rent05"
......
......@@ -247,6 +247,10 @@ def main():
_st, _bl = api_call("GET", "/api/batches?kind=elec")
_elec = (_bl.get("batches") or [])
if _elec:
# 批次行默认按账期折叠;只展开仍折叠的分组
js("""() => { document.querySelectorAll('#batchList tbody.pgrp.collapsed tr.grp')
.forEach(tr => tr.click()); }""")
p.wait_for_timeout(500)
ck("guest 看得到电费批次的「删除」按钮",
p.eval_on_selector_all("#batchList tbody tr button[data-del]",
"es=>es.length>0 && es.every(b=>b.offsetParent!==null)"))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment