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.
165 lines
7.5 KiB
165 lines
7.5 KiB
|
1 week ago
|
# -*- coding: utf-8 -*-
|
||
|
|
"""demo 子表功能实测 v2(对齐原型 9 子模块,客户信息按拍板跳过)。
|
||
|
|
|
||
|
|
v1 教训:MANAGE 第一行商机无关联客户,写跟进被必填校验拦下、弹窗未关挡住后续点击。
|
||
|
|
v2:先遍历定位 A2(关联客户=园科集团(seed快照)),再按 Tab 逐个实测:
|
||
|
|
跟进真写 / 勘察真写 / 方案卡 seed 断言+新建弹窗+一客户一卡负路径 / 团队 / 附件 / 操作日志。
|
||
|
|
"""
|
||
|
|
import io
|
||
|
|
import sys
|
||
|
|
|
||
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||
|
|
from playwright.sync_api import sync_playwright
|
||
|
|
|
||
|
|
URL = 'file:///D:/code/crm-backend-matt/.scratch/opportunity-e2e/demo/index.html'
|
||
|
|
BASE = r'D:\code\crm-backend-matt\.scratch\opportunity-e2e'
|
||
|
|
A_UID = '744842565802524672' # 赖永利(A)
|
||
|
|
|
||
|
|
console_msgs, page_errors, failed = [], [], []
|
||
|
|
|
||
|
|
|
||
|
|
def note(k, v):
|
||
|
|
print('%-30s %s' % (k, v))
|
||
|
|
|
||
|
|
|
||
|
|
with sync_playwright() as p:
|
||
|
|
browser = p.chromium.launch(headless=True)
|
||
|
|
page = browser.new_page(viewport={'width': 1440, 'height': 960})
|
||
|
|
page.on('console', lambda m: console_msgs.append((m.type, m.text))
|
||
|
|
if m.type in ('error', 'warning') else None)
|
||
|
|
page.on('pageerror', lambda e: page_errors.append(str(e)))
|
||
|
|
page.on('requestfailed', lambda r: failed.append((r.url, str(r.failure))))
|
||
|
|
page.on('response', lambda r: failed.append((r.url, 'HTTP %d' % r.status))
|
||
|
|
if r.status >= 400 else None)
|
||
|
|
|
||
|
|
page.goto(URL)
|
||
|
|
page.wait_for_load_state('networkidle')
|
||
|
|
page.click('button:has-text("管理(全量)")')
|
||
|
|
page.wait_for_timeout(1500)
|
||
|
|
|
||
|
|
# ---- 遍历定位 A2(支持翻页) ----
|
||
|
|
target = -1
|
||
|
|
for pg in (1, 2):
|
||
|
|
n = page.locator('#listBody tr.row').count()
|
||
|
|
for i in range(n):
|
||
|
|
page.locator('#listBody tr.row').nth(i).click()
|
||
|
|
page.wait_for_selector('#subTabs', timeout=8000)
|
||
|
|
page.wait_for_timeout(400)
|
||
|
|
page.click('#subTabs button:has-text("关联客户")')
|
||
|
|
page.wait_for_timeout(900)
|
||
|
|
if page.locator('#subBox', has_text='园科集团(seed快照)').count() > 0:
|
||
|
|
target = i
|
||
|
|
note('定位 A2', '第 %d 页第 %d 行(客户=园科集团(seed快照))' % (pg, i + 1))
|
||
|
|
break
|
||
|
|
page.click('button[data-tab="list"]')
|
||
|
|
page.wait_for_timeout(700)
|
||
|
|
if target >= 0:
|
||
|
|
break
|
||
|
|
try:
|
||
|
|
page.click('.pager button:has-text("下一页")', timeout=3000)
|
||
|
|
page.wait_for_timeout(1200)
|
||
|
|
except Exception:
|
||
|
|
break
|
||
|
|
if target < 0:
|
||
|
|
note('定位 A2', '未找到(所有页都没有 seed 客户),终止')
|
||
|
|
browser.close()
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
# ---- 1) 跟进 Tab:真实写一条 ----
|
||
|
|
page.click('#subTabs button:has-text("跟进")')
|
||
|
|
page.wait_for_timeout(1000)
|
||
|
|
page.click('button:has-text("+ 写跟进")')
|
||
|
|
page.wait_for_selector('#dlgForm textarea[name="followContent"]', timeout=8000)
|
||
|
|
note('跟进弹窗客户下拉', str(page.locator('#dlgForm select[name="customerId"] option').all_text_contents()))
|
||
|
|
page.fill('#dlgForm textarea[name="followContent"]', 'demo-手工验收-测试跟进')
|
||
|
|
page.click('#dlgForm button.ok')
|
||
|
|
try:
|
||
|
|
page.wait_for_selector('#toast .toast-ok', timeout=6000)
|
||
|
|
note('写跟进', '成功(toast-ok)')
|
||
|
|
except Exception:
|
||
|
|
note('写跟进', '未见成功 toast')
|
||
|
|
page.wait_for_timeout(1200)
|
||
|
|
note('跟进回显', '是' if page.locator('#subBox', has_text='demo-手工验收-测试跟进').count() else '否')
|
||
|
|
page.screenshot(path=BASE + r'\demo-sub-3-follow-added.png')
|
||
|
|
|
||
|
|
# ---- 2) 现场勘察 Tab:seed 断言 + 真实新增 ----
|
||
|
|
page.click('#subTabs button:has-text("现场勘察")')
|
||
|
|
page.wait_for_timeout(1000)
|
||
|
|
note('seed 勘察记录渲染', '是' if page.locator('#subBox', has_text='机房位置').count() else '否')
|
||
|
|
page.click('button:has-text("+ 新增勘察")')
|
||
|
|
page.wait_for_selector('#dlgForm input[name="engineerUserId"]', timeout=8000)
|
||
|
|
page.fill('#dlgForm input[name="engineerUserId"]', A_UID)
|
||
|
|
page.fill('#dlgForm input[name="surveySeq"]', 'survey_seq_02')
|
||
|
|
page.fill('#dlgForm input[name="applyNo"]', 'KC-DEMO-001')
|
||
|
|
page.fill('#dlgForm textarea[name="surveyDesc"]', 'demo-手工验收-勘察测试')
|
||
|
|
page.click('#dlgForm button.ok')
|
||
|
|
try:
|
||
|
|
page.wait_for_selector('text=勘察已记录', timeout=6000)
|
||
|
|
note('新增勘察', '成功(toast)')
|
||
|
|
except Exception:
|
||
|
|
note('新增勘察', '未见成功 toast')
|
||
|
|
page.wait_for_timeout(1200)
|
||
|
|
note('勘察回显', '是' if page.locator('#subBox', has_text='demo-手工验收-勘察测试').count() else '否')
|
||
|
|
page.screenshot(path=BASE + r'\demo-sub-4-survey.png')
|
||
|
|
|
||
|
|
# ---- 3) 方案卡 Tab:seed 卡断言 + 详情弹窗 + 新建弹窗 + 一客户一卡负路径 ----
|
||
|
|
page.click('#subTabs button:has-text("方案卡")')
|
||
|
|
page.wait_for_timeout(1200)
|
||
|
|
note('seed 卡(已提交/88 万)', '是' if page.locator('#subBox', has_text='已提交').count() else '否')
|
||
|
|
page.click('#subBox button:has-text("详情")')
|
||
|
|
page.wait_for_timeout(1000)
|
||
|
|
note('卡详情弹窗字段值', page.locator('#dlgForm .grid .kv').count())
|
||
|
|
page.screenshot(path=BASE + r'\demo-sub-5-card-detail.png')
|
||
|
|
page.click('#dlgForm button:has-text("取消")')
|
||
|
|
page.wait_for_timeout(500)
|
||
|
|
page.click('button:has-text("新建方案卡")')
|
||
|
|
page.wait_for_timeout(1800)
|
||
|
|
if page.locator('#dlgForm', has_text='新建方案卡').count():
|
||
|
|
note('新建卡弹窗·模板显示字段输入框', page.locator('#cardFields input').count())
|
||
|
|
page.fill('#dlgForm input[name="customerId"]', '920000000000000100')
|
||
|
|
page.click('#dlgForm button.ok') # 保存草稿 → 预期被一客户一卡(66009)拦
|
||
|
|
page.wait_for_timeout(1500)
|
||
|
|
still = page.locator('#dlgForm', has_text='新建方案卡').count() > 0
|
||
|
|
err_txt = ''
|
||
|
|
try:
|
||
|
|
err_txt = page.locator('#toast .toast-err').last.inner_text(timeout=1500)
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
note('一客户一卡负路径', '弹窗仍开=被拦(toast: %s)' % err_txt if still else '意外保存成功(异常,需查)')
|
||
|
|
page.click('#dlgForm button:has-text("取消")')
|
||
|
|
else:
|
||
|
|
note('新建卡弹窗', '未出现(无可选模板版本)')
|
||
|
|
page.screenshot(path=BASE + r'\demo-sub-5-card.png')
|
||
|
|
|
||
|
|
# ---- 4) 团队成员 Tab(写接口缺失 D-04/05/06,验证只读渲染) ----
|
||
|
|
page.click('#subTabs button:has-text("团队成员")')
|
||
|
|
page.wait_for_timeout(1200)
|
||
|
|
note('团队成员 .kv 行数', page.locator('#subBox .kv').count())
|
||
|
|
page.screenshot(path=BASE + r'\demo-sub-6-team.png')
|
||
|
|
|
||
|
|
# ---- 5) 附件 Tab(上传需 MinIO,demo 只读渲染) ----
|
||
|
|
page.click('#subTabs button:has-text("附件")')
|
||
|
|
page.wait_for_timeout(1200)
|
||
|
|
note('附件 .kv 行数', page.locator('#subBox .kv').count())
|
||
|
|
page.screenshot(path=BASE + r'\demo-sub-7-attachment.png')
|
||
|
|
|
||
|
|
# ---- 6) 操作日志 Tab ----
|
||
|
|
page.click('#subTabs button:has-text("操作日志")')
|
||
|
|
page.wait_for_timeout(1500)
|
||
|
|
note('操作日志条数', page.locator('#subBox div[style*="border-bottom"]').count())
|
||
|
|
page.screenshot(path=BASE + r'\demo-sub-8-oplog.png')
|
||
|
|
|
||
|
|
browser.close()
|
||
|
|
|
||
|
|
print()
|
||
|
|
print('== 健康检查 ==')
|
||
|
|
print('console error/warning:', len(console_msgs))
|
||
|
|
for t, m in console_msgs:
|
||
|
|
print(' [%s] %s' % (t, m))
|
||
|
|
print('page errors:', len(page_errors))
|
||
|
|
for e in page_errors:
|
||
|
|
print(' ', e)
|
||
|
|
print('失败请求(>=400/网络):', len(failed))
|
||
|
|
for u, w in failed:
|
||
|
|
print(' ', u, w)
|