#!/usr/bin/env python3 """ 斗地主 Headless 浏览器测试 V3 使用Socket.IO事件监听获取状态 """ import asyncio import sys import os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from playwright.async_api import async_playwright SERVER_URL = "http://129.211.173.87:8900" async def run_headless_test(): """运行 Headless 浏览器测试""" print("=" * 70) print(" 🎮 斗地主 Headless 浏览器测试 V3") print("=" * 70) async with async_playwright() as p: browser = await p.chromium.launch(headless=True) page = await browser.new_page(viewport={'width': 1280, 'height': 900}) try: # 访问测试客户端 await page.goto(f"{SERVER_URL}/test_client.html") await asyncio.sleep(1) # 连接到服务器并设置事件监听 print("\n📡 连接到游戏服务器...") await page.fill('input[placeholder*="服务器地址"]', SERVER_URL) # 设置全局状态监听 await page.evaluate(""" () => { window.testState = { room: null, game: null, myId: null, myCards: [], events: [], connected: false }; // 等待socket就绪 const waitForSocket = setInterval(() => { if (typeof socket !== 'undefined') { clearInterval(waitForSocket); socket.on('connect', () => { window.testState.connected = true; window.testState.myId = socket.id; }); socket.on('room_created', (data) => { window.testState.room = data.room; window.testState.events.push({type: 'room_created', data}); }); socket.on('room_updated', (data) => { window.testState.room = data.room; window.testState.events.push({type: 'room_updated', data}); }); socket.on('game_started', (data) => { window.testState.game = data.room.game; window.testState.events.push({type: 'game_started', data}); }); socket.on('state_update', (data) => { window.testState.game = data.game_state; if (data.game_state && data.game_state.player_cards) { const myId = window.testState.myId; if (myId && data.game_state.player_cards[myId]) { window.testState.myCards = data.game_state.player_cards[myId]; } } window.testState.events.push({type: 'state_update', data}); }); socket.on('game_ended', (data) => { window.testState.events.push({type: 'game_ended', data}); }); } }, 100); } """) await page.click('button:has-text("连接")') await asyncio.sleep(1.5) # 设置玩家名称并创建房间 await page.fill('input[placeholder*="玩家名称"]', 'Headless玩家') print("🏠 创建房间(含2个AI)...") await page.evaluate(""" () => { socket.emit('create_room', { game_type: 'doudizhu', player_name: 'Headless玩家', ai_config: {ai_count: 2, ai_types: ['rule', 'deepseek']} }); } """) # 等待房间创建 for _ in range(10): await asyncio.sleep(0.5) room = await page.evaluate("() => window.testState.room") if room and room.get('code'): break room = await page.evaluate("() => window.testState.room") if not room: print("❌ 房间创建失败") await browser.close() return room_code = room.get('code', 'UNKNOWN') players = room.get('players', {}) player_list = list(players.values()) if isinstance(players, dict) else players print(f"✅ 房间创建成功: {room_code}") print(f"\n👥 房间玩家 ({len(player_list)}/3):") for p in player_list: ai_tag = "🤖" if p.get('is_ai') else "👤" print(f" {ai_tag} {p.get('name', 'Unknown')} ({p.get('id', 'N/A')[:8]})") my_id = await page.evaluate("() => window.testState.myId") print(f"\n 我的ID: {my_id[:8] if my_id else 'N/A'}") # 准备并开始游戏 print("\n✅ 准备游戏...") await page.click('button:has-text("准备")') await asyncio.sleep(0.5) print("🚀 开始游戏...") await page.click('button:has-text("开始游戏")') print("\n🎲 游戏进行中...") print("-" * 70) turn_count = 0 game_ended = False for i in range(120): # 获取游戏状态 state = await page.evaluate("() => window.testState") game = state.get('game', {}) events = state.get('events', []) # 清空事件 if events: await page.evaluate("() => { window.testState.events = []; }") # 处理事件 for event in events: if event.get('type') == 'state_update': data = event.get('data', {}) action = data.get('action', 'unknown') player_id = data.get('player_id', '') result = data.get('result', {}) game_state = data.get('game_state', {}) doudizhu = game_state.get('doudizhu', {}) players_map = game_state.get('players', {}) player_info = players_map.get(player_id, {}) player_name = player_info.get('name', 'Unknown')[:10] if result.get('success'): turn_count += 1 if action == 'play': card_type = result.get('card_type', {}) remaining = result.get('remaining_cards', 0) print(f"{turn_count:2d}. [{player_name:10}] 出牌: {card_type.get('name', '?'):12} 剩余: {remaining:2d}张") elif action == 'call': if result.get('lord_id'): print(f"{turn_count:2d}. [{player_name:10}] 🎩 叫地主!") else: print(f"{turn_count:2d}. [{player_name:10}] 叫地主") elif action == 'pass': print(f"{turn_count:2d}. [{player_name:10}] 不出") elif event.get('type') == 'game_ended': winner = event.get('data', {}).get('winner', 'N/A') print(f"\n🏆 游戏结束! 获胜者: {winner[:8] if winner else 'N/A'}") game_ended = True if game_ended: break # 检查当前回合并执行动作 current_turn = game.get('current_turn_player_id') or game.get('current_turn') doudizhu = game.get('doudizhu', {}) phase = doudizhu.get('phase', '') if current_turn == my_id and not game_ended: my_cards = state.get('myCards', []) if phase == 'calling': print(f" -> Headless玩家叫地主") await page.evaluate(""" () => socket.emit('game_action', {action: 'call', data: {want: true}}) """) await asyncio.sleep(0.8) elif phase == 'playing': if my_cards: card_id = my_cards[0].get('id') print(f" -> Headless玩家出牌") await page.evaluate(f""" () => socket.emit('game_action', {{action: 'play', data: {{card_ids: ['{card_id}']}}}}) """) else: await page.evaluate(""" () => socket.emit('game_action', {action: 'pass', data: {}}) """) await asyncio.sleep(0.8) # 检查游戏结束 winner = game.get('winner_id') if winner: print(f"\n🏆 游戏结束! 获胜者: {winner[:8]}") game_ended = True break await asyncio.sleep(0.3) if not game_ended: print("\n" + "=" * 70) print(f"⏱️ 测试超时 (120秒)") print(f"📊 共进行了 {turn_count} 步") # 截图 screenshot_path = '/tmp/doudizhu_test_result_v3.png' await page.screenshot(path=screenshot_path, full_page=True) print(f"\n📸 截图已保存: {screenshot_path}") await browser.close() print("\n" + "=" * 70) print("✅ Headless测试完成!") print("=" * 70) except Exception as e: print(f"\n❌ 测试出错: {e}") import traceback traceback.print_exc() await browser.close() raise if __name__ == '__main__': asyncio.run(run_headless_test())