| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- """
- Comprehensive E2E: all CRM pages, all functions.
- """
- import os
- from playwright.sync_api import sync_playwright
- BASE_URL = "http://localhost:80"
- SCREENSHOT_DIR = "/tmp/crm-e2e-full"
- os.makedirs(SCREENSHOT_DIR, exist_ok=True)
- results = {"pass": [], "fail": []}
- def record(name, ok, detail=""):
- status = "✅" if ok else "❌"
- msg = f" {status} {name}"
- if detail:
- msg += f" — {detail}"
- print(msg)
- if ok:
- results["pass"].append(name)
- else:
- results["fail"].append((name, detail))
- def ss(page, name):
- try:
- page.screenshot(path=os.path.join(SCREENSHOT_DIR, f"{name}.png"))
- except Exception:
- pass
- def force_close_dialogs(page):
- for _ in range(3):
- count = page.locator(".el-dialog:visible, .el-drawer:visible").count()
- if count == 0:
- break
- page.keyboard.press("Escape")
- page.wait_for_timeout(500)
- page.wait_for_timeout(300)
- def nav_to(page, menu_text):
- items = page.locator(f'.el-menu-item:has-text("{menu_text}")').all()
- for item in items:
- if item.is_visible():
- item.click()
- page.wait_for_timeout(2000)
- return True
- return False
- def has_table(page):
- return page.locator(".el-table").count() > 0
- def row_count(page):
- try:
- return page.locator(".el-table__body-wrapper .el-table__row").count()
- except Exception:
- return 0
- def has_error(page):
- return page.locator(".el-message--error").count() > 0
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=True)
- page = browser.new_page(viewport={"width": 1440, "height": 900})
- print("\n[1. LOGIN]")
- page.goto(f"{BASE_URL}/login", wait_until="networkidle", timeout=20000)
- page.locator('input[placeholder="用户名"]').fill("admin")
- page.locator('input[placeholder="密码"]').fill("admin123")
- page.locator('button:has-text("登")').click()
- page.wait_for_url("**/index**", timeout=15000)
- page.wait_for_timeout(2000)
- record("Login", "/index" in page.url)
- ss(page, "01-home")
- print("\n[2. LIST PAGES]")
- for name in ["线索管理", "客户管理", "公海池", "商机管理", "报价单管理", "合同列表", "产品列表", "产品分类"]:
- nav_to(page, name)
- ok = has_table(page) and not has_error(page)
- record(f"{name}", ok, f"rows={row_count(page)}")
- ss(page, f"list-{name}")
- print("\n[3. SEARCH]")
- nav_to(page, "线索管理")
- page.wait_for_timeout(1000)
- filter_btn = page.locator('button:has-text("高级筛选")').first
- if filter_btn.is_visible():
- filter_btn.click()
- page.wait_for_timeout(1000)
- visible = page.locator(".el-drawer:visible, .el-dialog:visible").count() > 0
- record("Clue advanced filter", visible)
- ss(page, "03-filter")
- force_close_dialogs(page)
- else:
- inputs = [i for i in page.locator(".el-form input").all() if i.is_visible()]
- record("Clue search inputs", len(inputs) > 0, f"visible={len(inputs)}")
- print("\n[4. PAGINATION]")
- nav_to(page, "合同列表")
- page.wait_for_timeout(1500)
- pager = page.locator(".el-pagination .el-pager li")
- if pager.count() > 1:
- pager.nth(1).click()
- page.wait_for_timeout(1500)
- record("Contract page 2", has_table(page), f"rows={row_count(page)}")
- else:
- record("Contract pagination", True, "only 1 page")
- ss(page, "04-pagination")
- print("\n[5. ADD DIALOGS]")
- for menu_name, btn_text in [("线索管理","新增"), ("客户管理","新增客户"), ("商机管理","新增"), ("报价单管理","新增"), ("合同列表","新增"), ("产品列表","新增")]:
- nav_to(page, menu_name)
- page.wait_for_timeout(1000)
- btn = page.locator(f'button:has-text("{btn_text}")').first
- if btn.is_visible():
- btn.click()
- page.wait_for_timeout(1500)
- dialog_vis = page.locator(".el-dialog:visible, .el-drawer:visible").count() > 0
- record(f"{menu_name} add", dialog_vis)
- ss(page, f"05-add-{menu_name}")
- force_close_dialogs(page)
- else:
- record(f"{menu_name} add", False, f"'{btn_text}' not found")
- print("\n[6. DETAIL VIEWS]")
- for menu_name in ["客户管理", "线索管理", "商机管理"]:
- nav_to(page, menu_name)
- page.wait_for_timeout(1500)
- btns = page.locator('.el-table__row .el-button:has-text("详情")').all()
- if btns:
- for btn in btns[:1]:
- if btn.is_visible():
- btn.click()
- page.wait_for_timeout(2000)
- has_content = page.locator(".el-descriptions, .el-tabs, .detail-info, .app-container").count() > 0
- record(f"{menu_name} detail", has_content, f"url={page.url[-40:]}")
- ss(page, f"06-detail-{menu_name}")
- page.go_back(wait_until="networkidle", timeout=8000)
- page.wait_for_timeout(1500)
- break
- else:
- record(f"{menu_name} detail", False, "no detail button")
- print("\n[7. STATISTICS]")
- for name in ["线索统计", "客户统计", "公海池统计", "产品统计"]:
- found = nav_to(page, name)
- if found:
- page.wait_for_timeout(1500)
- has_content = page.locator(".app-container, .el-card, canvas").count() > 0
- record(name, has_content or has_table(page))
- ss(page, f"07-{name}")
- else:
- record(name, False, "menu not found")
- print("\n[8. KANBAN]")
- nav_to(page, "商机管理")
- page.wait_for_timeout(1000)
- kanban = page.locator('.el-tabs__item:has-text("看板"), button:has-text("看板")').first
- if kanban.is_visible():
- kanban.click()
- page.wait_for_timeout(2000)
- record("Opportunity kanban", has_table(page) or page.locator(".kanban, .board").count() > 0)
- ss(page, "08-kanban")
- else:
- record("Opportunity kanban", False, "tab not found")
- print("\n[9. CONTRACT SUB-PAGES]")
- for name in ["合同模板", "合同审批记录", "合同变更记录"]:
- found = nav_to(page, name)
- page.wait_for_timeout(1500)
- record(name, has_table(page) or page.locator(".app-container").count() > 0)
- ss(page, f"09-{name}")
- print("\n[10. PAYMENT SUB-PAGES]")
- for name in ["回款计划", "催收管理"]:
- found = nav_to(page, name)
- page.wait_for_timeout(1500)
- record(name, has_table(page) or page.locator(".app-container").count() > 0)
- ss(page, f"10-{name}")
- print("\n[11. INVOICE & VISIT]")
- nav_to(page, "发票管理") or page.goto(f"{BASE_URL}/invoice", wait_until="networkidle", timeout=10000)
- page.wait_for_timeout(1500)
- record("发票管理", has_table(page) or page.locator(".app-container").count() > 0)
- ss(page, "11-invoice")
- nav_to(page, "回访管理") or page.goto(f"{BASE_URL}/visit", wait_until="networkidle", timeout=10000)
- page.wait_for_timeout(1500)
- record("回访管理", has_table(page) or page.locator(".app-container").count() > 0)
- ss(page, "11-visit")
- print("\n[12. SUPPLIER]")
- found = nav_to(page, "供应商") or nav_to(page, "供应商管理")
- page.wait_for_timeout(1500)
- record("供应商管理", has_table(page) or page.locator(".app-container").count() > 0)
- ss(page, "12-supplier")
- print("\n[13. EDIT & DELETE]")
- nav_to(page, "产品分类")
- page.wait_for_timeout(1500)
- edit_btns = [b for b in page.locator('.el-table__row .el-button:has-text("修改")').all() if b.is_visible()]
- record("Edit button visible", len(edit_btns) > 0, f"count={len(edit_btns)}")
- if edit_btns:
- edit_btns[0].click()
- page.wait_for_timeout(1000)
- record("Edit dialog opens", page.locator(".el-dialog:visible, .el-drawer:visible").count() > 0)
- force_close_dialogs(page)
- del_btns = [b for b in page.locator('.el-table__row .el-button:has-text("删除")').all() if b.is_visible()]
- record("Delete button visible", len(del_btns) > 0, f"count={len(del_btns)}")
- print("\n[14. TABLE SORT]")
- nav_to(page, "合同列表")
- page.wait_for_timeout(1500)
- sort_cols = page.locator(".el-table__header th.is-sortable").all()
- if sort_cols:
- sort_cols[0].click()
- page.wait_for_timeout(1500)
- record("Contract sort", has_table(page))
- else:
- record("Contract sort", False, "no sortable cols")
- print("\n[15. TOOLBAR BUTTONS]")
- nav_to(page, "客户管理")
- page.wait_for_timeout(1500)
- for btn_text in ["导出", "新增客户"]:
- found = page.locator(f'button:has-text("{btn_text}")').first
- record(f"Button '{btn_text}'", found.is_visible() if found.count() > 0 else False)
- print(f"\n{'=' * 70}")
- total = len(results["pass"]) + len(results["fail"])
- print(f"E2E RESULTS: {len(results['pass'])}/{total} PASSED")
- if results["fail"]:
- print(f"\n❌ FAILURES ({len(results['fail'])}):")
- for name, reason in results["fail"]:
- print(f" - {name}: {reason}")
- else:
- print("🎉 ALL E2E TESTS PASSED!")
- print(f"{'=' * 70}")
- browser.close()
|