255 lines
11 KiB
Python
255 lines
11 KiB
Python
#!/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())
|