""" 一物一码会员营销系统 - 业务流程测试 测试范围: 1. 管理后台(Admin):登录、营销码生成、礼品管理、积分规则配置、订单管理 2. C端应用(H5):登录/注册、扫码积分、积分商城、积分兑换、订单查看 """ from playwright.sync_api import sync_playwright, Page, expect import time import json from datetime import datetime class BusinessFlowTester: def __init__(self, admin_url: str, h5_url: str): self.admin_url = admin_url self.h5_url = h5_url self.test_results = [] self.marketing_codes = [] self.gift_id = None def log_test(self, test_name: str, status: str, message: str = ""): """记录测试结果""" result = { "test": test_name, "status": status, "message": message, "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") } self.test_results.append(result) status_emoji = "✅" if status == "PASS" else "❌" print(f"{status_emoji} [{test_name}] {status}: {message}") def test_admin_login(self, page: Page): """测试管理后台登录""" try: print("\n=== 测试:管理后台登录 ===") page.goto(self.admin_url) page.wait_for_load_state('networkidle') # 检查是否在登录页 page.wait_for_selector('input[type="text"]', timeout=5000) # 输入登录信息(Mock登录) page.fill('input[type="text"]', 'admin') page.fill('input[type="password"]', 'admin123') # 点击登录按钮 page.click('button[type="submit"]') # 等待跳转到仪表盘 page.wait_for_url('**/dashboard', timeout=10000) # 验证仪表盘加载成功 page.wait_for_selector('text=仪表盘', timeout=5000) self.log_test("管理后台登录", "PASS", "成功登录并跳转到仪表盘") return True except Exception as e: self.log_test("管理后台登录", "FAIL", str(e)) return False def test_admin_dashboard(self, page: Page): """测试管理后台仪表盘""" try: print("\n=== 测试:管理后台仪表盘 ===") # 检查统计卡片 page.wait_for_selector('text=总礼品数', timeout=5000) page.wait_for_selector('text=待处理订单', timeout=5000) # 截图保存 page.screenshot(path='test_screenshots/admin_dashboard.png', full_page=True) self.log_test("管理后台仪表盘", "PASS", "仪表盘数据展示正常") return True except Exception as e: self.log_test("管理后台仪表盘", "FAIL", str(e)) return False def test_generate_marketing_codes(self, page: Page): """测试营销码生成""" try: print("\n=== 测试:营销码生成 ===") # 导航到营销码管理页面 page.goto(f"{self.admin_url}/marketing-codes") page.wait_for_load_state('networkidle') # 填写生成表单 batch_no = f"BATCH-{int(time.time())}" page.fill('input[name="batchNo"]', batch_no) page.fill('input[name="productId"]', 'PROD-001') page.fill('input[name="productName"]', '测试产品A') page.fill('input[name="quantity"]', '10') # 提交生成 page.click('button:has-text("生成营销码")') # 等待生成结果 page.wait_for_selector('text=生成成功', timeout=10000) # 获取生成的营销码 time.sleep(2) code_elements = page.locator('table tbody tr td:first-child').all() self.marketing_codes = [el.inner_text() for el in code_elements[:5]] print(f"生成的营销码示例: {self.marketing_codes[:3]}") # 截图 page.screenshot(path='test_screenshots/marketing_codes.png', full_page=True) self.log_test("营销码生成", "PASS", f"成功生成营销码,批次号: {batch_no}") return True except Exception as e: self.log_test("营销码生成", "FAIL", str(e)) return False def test_create_points_rule(self, page: Page): """测试创建积分规则""" try: print("\n=== 测试:创建积分规则 ===") # 导航到积分规则页面 page.goto(f"{self.admin_url}/points-rules") page.wait_for_load_state('networkidle') # 填写规则表单 page.fill('input[name="ruleName"]', '测试产品积分规则') # 选择规则类型(产品规则) page.click('select[name="ruleType"]') page.click('option[value="1"]') # Product = 1 # 填写积分值 page.fill('input[name="pointsValue"]', '100') # 填写产品ID page.fill('input[name="productId"]', 'PROD-001') # 提交创建 page.click('button:has-text("创建规则")') # 等待成功提示 page.wait_for_selector('text=创建成功', timeout=10000) # 截图 page.screenshot(path='test_screenshots/points_rule.png', full_page=True) self.log_test("创建积分规则", "PASS", "成功创建产品积分规则") return True except Exception as e: self.log_test("创建积分规则", "FAIL", str(e)) return False def test_create_gift(self, page: Page): """测试创建礼品""" try: print("\n=== 测试:创建礼品 ===") # 导航到礼品列表 page.goto(f"{self.admin_url}/gifts") page.wait_for_load_state('networkidle') # 点击创建按钮 page.click('button:has-text("创建礼品")') page.wait_for_url('**/gifts/create') # 填写礼品信息 page.fill('input[name="name"]', '测试礼品-电子产品') # 选择礼品类型(实物) page.click('select[name="giftType"]') page.click('option[value="1"]') # Physical = 1 page.fill('textarea[name="description"]', '这是一个测试用的电子产品礼品') page.fill('input[name="imageUrl"]', 'https://via.placeholder.com/400') page.fill('input[name="requiredPoints"]', '500') page.fill('input[name="totalStock"]', '100') page.fill('input[name="redemptionLimit"]', '2') # 提交创建 page.click('button:has-text("保存")') # 等待成功并跳转回列表 page.wait_for_url('**/gifts', timeout=10000) page.wait_for_selector('text=创建成功', timeout=5000) # 截图 page.screenshot(path='test_screenshots/gift_created.png', full_page=True) self.log_test("创建礼品", "PASS", "成功创建礼品") return True except Exception as e: self.log_test("创建礼品", "FAIL", str(e)) return False def test_gift_shelf_management(self, page: Page): """测试礼品上下架""" try: print("\n=== 测试:礼品上下架 ===") # 确保在礼品列表页面 page.goto(f"{self.admin_url}/gifts") page.wait_for_load_state('networkidle') # 找到第一个礼品的上架开关 switch = page.locator('button[role="switch"]').first # 点击上架 switch.click() page.wait_for_selector('text=上架成功', timeout=5000) # 等待并再次点击下架 time.sleep(1) switch.click() page.wait_for_selector('text=下架成功', timeout=5000) self.log_test("礼品上下架", "PASS", "礼品上下架功能正常") return True except Exception as e: self.log_test("礼品上下架", "FAIL", str(e)) return False def test_h5_register(self, page: Page): """测试C端会员注册""" try: print("\n=== 测试:C端会员注册 ===") page.goto(f"{self.h5_url}/register") page.wait_for_load_state('networkidle') # 填写注册信息 phone = f"138{int(time.time()) % 100000000}" page.fill('input[name="phone"]', phone) page.fill('input[name="password"]', '123456') page.fill('input[name="nickname"]', '测试用户') # 提交注册 page.click('button:has-text("注册")') # 等待注册成功并跳转 page.wait_for_url('**/home', timeout=10000) # 截图 page.screenshot(path='test_screenshots/h5_register.png', full_page=True) self.log_test("C端会员注册", "PASS", f"成功注册会员,手机号: {phone}") return True except Exception as e: self.log_test("C端会员注册", "FAIL", str(e)) return False def test_h5_scan_code(self, page: Page): """测试C端扫码积分""" try: print("\n=== 测试:C端扫码积分 ===") if not self.marketing_codes: self.log_test("C端扫码积分", "SKIP", "没有可用的营销码") return False # 导航到扫码页面 page.goto(f"{self.h5_url}/scan") page.wait_for_load_state('networkidle') # 模拟扫码(输入营销码) marketing_code = self.marketing_codes[0] page.fill('input[name="marketingCode"]', marketing_code) page.click('button:has-text("确认")') # 等待积分获取结果 page.wait_for_selector('text=获得积分', timeout=10000) # 截图 page.screenshot(path='test_screenshots/h5_scan_code.png', full_page=True) self.log_test("C端扫码积分", "PASS", f"成功扫码获得积分,营销码: {marketing_code}") return True except Exception as e: self.log_test("C端扫码积分", "FAIL", str(e)) return False def test_h5_mall(self, page: Page): """测试C端积分商城""" try: print("\n=== 测试:C端积分商城 ===") # 导航到商城页面 page.goto(f"{self.h5_url}/mall") page.wait_for_load_state('networkidle') # 检查商品列表 page.wait_for_selector('text=积分商城', timeout=5000) # 查找第一个商品 first_gift = page.locator('.gift-card').first first_gift.click() # 等待商品详情页 page.wait_for_url('**/mall/gift/**') # 截图 page.screenshot(path='test_screenshots/h5_mall.png', full_page=True) self.log_test("C端积分商城", "PASS", "积分商城展示正常") return True except Exception as e: self.log_test("C端积分商城", "FAIL", str(e)) return False def test_h5_redeem_gift(self, page: Page): """测试C端积分兑换""" try: print("\n=== 测试:C端积分兑换 ===") # 假设已经在商品详情页 # 点击兑换按钮 page.click('button:has-text("立即兑换")') # 填写收货地址(如果是实物礼品) page.wait_for_selector('input[name="consignee"]', timeout=5000) page.fill('input[name="consignee"]', '张三') page.fill('input[name="phone"]', '13800138000') page.fill('input[name="address"]', '北京市朝阳区xxx街道xxx号') # 确认兑换 page.click('button:has-text("确认兑换")') # 等待兑换成功 page.wait_for_selector('text=兑换成功', timeout=10000) # 截图 page.screenshot(path='test_screenshots/h5_redeem.png', full_page=True) self.log_test("C端积分兑换", "PASS", "成功兑换礼品") return True except Exception as e: self.log_test("C端积分兑换", "FAIL", str(e)) return False def test_h5_orders(self, page: Page): """测试C端订单查询""" try: print("\n=== 测试:C端订单查询 ===") # 导航到订单页面 page.goto(f"{self.h5_url}/orders") page.wait_for_load_state('networkidle') # 检查订单列表 page.wait_for_selector('text=我的订单', timeout=5000) # 点击第一个订单查看详情 first_order = page.locator('.order-card').first if first_order.count() > 0: first_order.click() page.wait_for_url('**/orders/**') # 截图 page.screenshot(path='test_screenshots/h5_orders.png', full_page=True) self.log_test("C端订单查询", "PASS", "订单列表展示正常") return True except Exception as e: self.log_test("C端订单查询", "FAIL", str(e)) return False def test_h5_points_detail(self, page: Page): """测试C端积分明细""" try: print("\n=== 测试:C端积分明细 ===") # 导航到积分明细页面 page.goto(f"{self.h5_url}/points") page.wait_for_load_state('networkidle') # 检查积分明细列表 page.wait_for_selector('text=积分明细', timeout=5000) # 截图 page.screenshot(path='test_screenshots/h5_points.png', full_page=True) self.log_test("C端积分明细", "PASS", "积分明细展示正常") return True except Exception as e: self.log_test("C端积分明细", "FAIL", str(e)) return False def test_admin_order_management(self, page: Page): """测试管理后台订单管理""" try: print("\n=== 测试:管理后台订单管理 ===") # 导航到订单列表 page.goto(f"{self.admin_url}/orders") page.wait_for_load_state('networkidle') # 检查订单列表 page.wait_for_selector('text=兑换订单', timeout=5000) # 筛选待处理订单 page.click('select[name="status"]') page.click('option[value="1"]') # Pending = 1 # 等待筛选结果 time.sleep(2) # 查看第一个订单详情 first_order = page.locator('table tbody tr').first if first_order.count() > 0: detail_button = first_order.locator('button:has-text("详情")') detail_button.click() page.wait_for_url('**/orders/**') # 截图订单详情 page.screenshot(path='test_screenshots/admin_order_detail.png', full_page=True) self.log_test("管理后台订单管理", "PASS", "订单管理功能正常") return True except Exception as e: self.log_test("管理后台订单管理", "FAIL", str(e)) return False def generate_report(self): """生成测试报告""" print("\n" + "="*60) print("测试报告") print("="*60) total = len(self.test_results) passed = sum(1 for r in self.test_results if r['status'] == 'PASS') failed = sum(1 for r in self.test_results if r['status'] == 'FAIL') skipped = sum(1 for r in self.test_results if r['status'] == 'SKIP') print(f"\n总测试数: {total}") print(f"通过: {passed} ✅") print(f"失败: {failed} ❌") print(f"跳过: {skipped} ⏭️") print(f"成功率: {(passed/total*100):.2f}%") print("\n详细结果:") print("-"*60) for result in self.test_results: status_emoji = "✅" if result['status'] == 'PASS' else "❌" if result['status'] == 'FAIL' else "⏭️" print(f"{status_emoji} {result['test']}: {result['message']}") # 保存JSON报告 with open('test_report.json', 'w', encoding='utf-8') as f: json.dump(self.test_results, f, ensure_ascii=False, indent=2) print("\n测试报告已保存到: test_report.json") print("截图已保存到: test_screenshots/ 目录") def run_tests(): """运行所有测试""" admin_url = "http://localhost:3000" h5_url = "http://localhost:5173" print("一物一码会员营销系统 - 业务流程测试") print(f"管理后台地址: {admin_url}") print(f"C端应用地址: {h5_url}") print("-"*60) tester = BusinessFlowTester(admin_url, h5_url) with sync_playwright() as p: # 启动浏览器(headless模式) browser = p.chromium.launch(headless=True) # 测试管理后台 print("\n【开始测试管理后台】") admin_page = browser.new_page() if tester.test_admin_login(admin_page): tester.test_admin_dashboard(admin_page) tester.test_generate_marketing_codes(admin_page) tester.test_create_points_rule(admin_page) tester.test_create_gift(admin_page) tester.test_gift_shelf_management(admin_page) tester.test_admin_order_management(admin_page) admin_page.close() # 测试C端应用 print("\n【开始测试C端应用】") h5_page = browser.new_page() h5_page.set_viewport_size({"width": 375, "height": 667}) # 模拟移动设备 if tester.test_h5_register(h5_page): tester.test_h5_scan_code(h5_page) tester.test_h5_mall(h5_page) # tester.test_h5_redeem_gift(h5_page) # 需要有足够积分 tester.test_h5_orders(h5_page) tester.test_h5_points_detail(h5_page) h5_page.close() browser.close() # 生成测试报告 tester.generate_report() if __name__ == "__main__": # 创建截图目录 import os os.makedirs('test_screenshots', exist_ok=True) run_tests()