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.
142 lines
6.8 KiB
142 lines
6.8 KiB
# -*- coding: utf-8 -*-
|
|
"""票 12 自测 v2:demo 新建对话框 opp-create-fields 7 类字段 + 看板泳道(展示名取卡 + offset/limit)。
|
|
|
|
v2 修正:oppSource 按 value 精确选(v1 index=1 落在 opp_source_01 线索转入触发关联线索必填);
|
|
客户区块改手输 id+名快照(v1 实测 customer/search oppId=0 被后端 66002 拒绝:商机不存在: 0);
|
|
创建后调 detail API 断言新字段落库回显。
|
|
"""
|
|
import io
|
|
import re
|
|
import sys
|
|
|
|
import requests
|
|
|
|
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'
|
|
SHOT = r'D:\code\crm-backend-matt\.scratch\opportunity-e2e\demo\shot-12-'
|
|
BASE = 'https://ai.itc.vip/crm-api'
|
|
UID_MAIN = '739564171091247104' # 罗伟健:测试环境可用(赖永利 id 在测试环境 40401,待补导)
|
|
|
|
console_msgs, page_errors, failed = [], [], []
|
|
|
|
def sniff(page):
|
|
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) if isinstance(r.failure, str) else (r.failure or {}).get('errorText', '?'))))
|
|
page.on('response', lambda r: failed.append((r.url, 'HTTP %d' % r.status)) if r.status >= 400 else None)
|
|
|
|
with sync_playwright() as p:
|
|
browser = p.chromium.launch(headless=True)
|
|
page = browser.new_page(viewport={'width': 1500, 'height': 950})
|
|
sniff(page)
|
|
page.goto(URL)
|
|
page.wait_for_load_state('networkidle')
|
|
page.wait_for_timeout(2000)
|
|
|
|
# 测试环境模式:boot 不自动登录,uid 直登罗伟健
|
|
page.fill('#uidInput', UID_MAIN)
|
|
page.click('text=uid 登录')
|
|
page.wait_for_load_state('networkidle')
|
|
page.wait_for_timeout(2500)
|
|
|
|
# 1) 泳道渲染断言 + 截图
|
|
lanes = page.locator('.lane')
|
|
lane_n = lanes.count()
|
|
lane_heads = [lanes.nth(i).locator('.laneHead').inner_text().replace('\n', ' ') for i in range(lane_n)]
|
|
cards = page.locator('.laneCard').count()
|
|
more_btns = page.locator('.laneFoot button').count()
|
|
page.screenshot(path=SHOT + '09-lanes.png', full_page=False)
|
|
print('1) 泳道列数=%d 卡片=%d 「更多」按钮=%d' % (lane_n, cards, more_btns))
|
|
for h in lane_heads:
|
|
print(' 列:', h)
|
|
if more_btns:
|
|
page.locator('.laneFoot button').first.click()
|
|
page.wait_for_timeout(1500)
|
|
foot = page.locator('.laneFoot').first.inner_text()
|
|
print(' 点击「更多」后:卡片总数=%d 列脚注=%r' % (page.locator('.laneCard').count(), foot))
|
|
page.screenshot(path=SHOT + '10-lanes-more.png', full_page=False)
|
|
|
|
# 2) 打开新建对话框 + 截图
|
|
page.click('text=+ 新建商机')
|
|
page.wait_for_timeout(800)
|
|
page.screenshot(path=SHOT + '11-create-dialog.png', full_page=False)
|
|
dlg = page.locator('dialog:visible')
|
|
print('2) 新建对话框可见=%s 字段数(name=*)=%d' % (
|
|
dlg.count() > 0, page.locator('dialog [name]').count()))
|
|
|
|
# 三级级联:省 → 市 → 区县(区县码折叠进 cityCode)
|
|
page.select_option('dialog [name="provinceCode"]', index=1)
|
|
page.wait_for_timeout(1200)
|
|
city_opts = page.locator('dialog [name="cityCode"] option').count()
|
|
page.select_option('dialog [name="cityCode"]', index=1)
|
|
page.wait_for_timeout(1200)
|
|
dist_opts = page.locator('dialog [name="districtCode"] option').count()
|
|
if dist_opts > 1:
|
|
page.select_option('dialog [name="districtCode"]', index=1)
|
|
print('3) 三级级联:市选项=%d 区县选项=%d(选中区县码=%s)' % (
|
|
city_opts, dist_opts, page.locator('dialog [name="districtCode"]').input_value() or '空'))
|
|
|
|
# 填表:客户手输 id+名(=1 主要意向路径,验证主表冗余)
|
|
page.fill('dialog [name="opportunityName"]', 'demo-12-新字段验收')
|
|
page.select_option('dialog [name="oppSource"]', 'opp_source_02')
|
|
page.select_option('dialog [name="localityType"]', index=1)
|
|
page.select_option('dialog [name="bidForm"]', index=1)
|
|
page.fill('dialog [name="projectAmount"]', '880000.50')
|
|
page.fill('dialog [name="address"]', '天府大道北段1700号环球中心E1')
|
|
page.fill('dialog [name="customerId"]', '99001')
|
|
page.fill('dialog [name="customerName"]', '智慧园区客户')
|
|
page.select_option('dialog [name="customerRole"]', 'customer_role_01')
|
|
page.select_option('dialog [name="isPrimaryIntended"]', '1')
|
|
page.fill('dialog [name="keyContactName"]', '王工')
|
|
page.fill('dialog [name="keyContactCompany"]', '甲方Y公司')
|
|
page.fill('dialog [name="remark"]', 'demo自测:项目概况字段')
|
|
page.click('dialog button:has-text("创建")')
|
|
page.wait_for_timeout(2500)
|
|
|
|
# 3) 创建结果:toast 或 errbox
|
|
toast_txt = page.locator('.toast-item').last.inner_text() if page.locator('.toast-item').count() else ''
|
|
err_txt = page.locator('#crtErr .errbox').inner_text() if page.locator('#crtErr .errbox').count() else ''
|
|
print('4) 创建结果 toast=%r err=%r' % (toast_txt[:80], err_txt[:200]))
|
|
page.screenshot(path=SHOT + '13-create-result.png', full_page=False)
|
|
page.wait_for_timeout(1500)
|
|
page.screenshot(path=SHOT + '14-detail-after-create.png', full_page=False)
|
|
browser.close()
|
|
|
|
# 4) detail API 断言新字段落库
|
|
m = re.search(r'id=(\d+)', toast_txt)
|
|
if m:
|
|
new_id = m.group(1)
|
|
tok = requests.get(BASE + '/api/auth/debug/token', params={'userId': UID_MAIN}).json()['data']
|
|
d = requests.get(BASE + '/api/opportunity/detail', headers={'Authorization': 'Bearer ' + tok},
|
|
params={'id': new_id}).json()
|
|
dv = d.get('data') or {}
|
|
checks = {
|
|
'projectAmount': dv.get('projectAmount'),
|
|
'address': dv.get('address'),
|
|
'remark(项目概况)': dv.get('remark'),
|
|
'cityCode(区县码)': dv.get('cityCode'),
|
|
'customerId': dv.get('customerId'),
|
|
'primaryCustomerId(=1冗余)': dv.get('primaryCustomerId'),
|
|
'primaryCustomerNameSnapshot': dv.get('primaryCustomerNameSnapshot'),
|
|
'keyContactId(留空)': dv.get('keyContactId'),
|
|
'keyContactNameSnapshot': dv.get('keyContactNameSnapshot'),
|
|
'keyContactCompanySnapshot': dv.get('keyContactCompanySnapshot'),
|
|
}
|
|
print('5) detail 断言(id=%s):' % new_id)
|
|
for k, v in checks.items():
|
|
print(' %-28s = %r' % (k, v))
|
|
else:
|
|
print('5) 创建失败,跳过 detail 断言')
|
|
|
|
print('== console error/warning:', len(console_msgs))
|
|
for t, x in console_msgs[:20]:
|
|
print(' [%s] %s' % (t, x[:300]))
|
|
print('== page errors:', len(page_errors))
|
|
for e in page_errors[:10]:
|
|
print(' ', e[:300])
|
|
print('== failed requests:', len(failed))
|
|
for u, f in failed[:20]:
|
|
print(' ', f, '←', u[:200])
|
|
|