test/framework/business/three_d_generation_page.py

187 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import time
import os
import random
import string
from framework.core.base_page import BasePage
from framework.core.logger import get_logger
logger = get_logger("ThreeDGenerationPage")
class ThreeDGenerationPage(BasePage):
"""3D生成页面 POM - 包含生成、等待与保存逻辑"""
MENU_TEXT = "3D生成"
def __init__(self, page):
self.page = page
def navigate_to(self):
"""进入3D生成页面"""
logger.info("正在切换到3D生成页面...")
self.smart_click(self.MENU_TEXT)
try:
self.page.wait_for_selector(".p-button:has-text('保存'), .p-tabmenu", timeout=10000)
except:
pass
time.sleep(2)
def upload_image(self, file_path):
"""通过隐藏 input 上传图片"""
logger.info(f"📤 准备上传 3D 生成素材: {file_path}")
try:
# 直接通过 input 元素设置文件
self.page.set_input_files("input[type='file']", file_path)
except Exception as e:
logger.warning(f"⚠️ 直接注入失败,尝试点击图标触发: {e}")
with self.page.expect_file_chooser() as fc_info:
self.page.click("button.p-button-icon-only:has(.pi-arrow-up)")
fc_info.value.set_files(file_path)
time.sleep(2)
def start_generation(self, retries=3):
"""点击底部生成按钮 (图标按钮) - 增加 3 次重试机制 & 15s 业务异常监控"""
logger.info(f"🚀 准备点击生成按钮 (最多重试 {retries} 次)...")
target_btn = "button.p-button-icon-only:has(.pi-arrow-up)"
for i in range(retries):
try:
# 1. 执行物理点击
self.page.wait_for_selector(target_btn, state="visible", timeout=5000)
self.page.click(target_btn)
logger.info(f"🖱️ 第 {i+1} 次点击动作已发出")
# 2. 持续观测业务状态 (监控 15s)
logger.info(f"👀 正在观测第 {i+1} 次生成任务的业务反馈...")
is_failed = False
for _ in range(15):
# 使用显式文本探测,捕捉 Toast 或 页面文字报错
error_detected = self.page.locator("text=/网络错误|Generation failed|failed/i").first.is_visible()
if error_detected:
logger.warning(f"⚠️ 第 {i+1} 次运行检测到业务异常文字,准备重试")
is_failed = True
break
time.sleep(1)
if not is_failed:
logger.info(f"✅ 第 {i+1} 次任务成功启动且未检出即时报错")
return True
# 业务失败且有次数则间隔后重试
if i < retries - 1:
time.sleep(2)
continue
except Exception as e:
# 捕捉定位或点击失败
logger.warning(f"⚠️ 第 {i+1} 次点击尝试失败 (物理层): {e}")
if i < retries - 1:
time.sleep(2)
continue
# 3. 最终失败处理
error_msg = f"❌ 3D 生成任务在重试 {retries} 次后仍无法启动,程序强行退出"
logger.error(error_msg)
raise Exception(error_msg)
def wait_for_result_and_save(self, timeout=900):
"""等待右上角[保存]按钮变为可点击状态后,立刻点击"""
logger.info(f"⏳ 等待 3D 生成结果 (最长 {timeout}s)...")
start_time = time.time()
last_log = start_time
while time.time() - start_time < timeout:
elapsed = int(time.time() - start_time)
# 每 30s 打印一次进度,方便追踪
if time.time() - last_log >= 30:
logger.info(f" ⏱️ 已等待 {elapsed}s继续监听保存按鈕...")
last_log = time.time()
try:
save_btns = self.page.locator("button:has-text('\u4fdd\u5b58')")
count = save_btns.count()
for i in range(count):
btn = save_btns.nth(i)
try:
# 跳过弹窗内的按鈕(弹窗内的“确定”不是目标)
in_dialog = btn.evaluate("el => !!el.closest('.p-dialog')")
if in_dialog:
continue
if btn.is_visible() and btn.is_enabled():
logger.info(f"✨ 第 {elapsed}s 时检测到[保存]按鈕就绪,立刻点击")
btn.click()
return True
except Exception:
continue
except Exception:
pass # 按鈕还未出现,继续等待
time.sleep(3)
logger.error(f"❌ 等待结果超时 ({timeout}s),保存按鈕始终未就绪")
return False
def handle_save_dialog(self):
"""处理保存模型弹窗及其路径选择流程"""
# 1. 生成随机资产名称
random_name = "".join(random.choices(string.ascii_letters + string.digits, k=8))
asset_name = f"Auto3D_{random_name}"
logger.info(f"📝 输入资产名称: {asset_name}")
# 输入资产名称
name_input = self.page.locator("input[placeholder='请输入资产名称']")
name_input.fill(asset_name)
time.sleep(1)
# 2. 点击保存路径触发弹窗
logger.info("📂 点击保存路径选择器...")
path_input = self.page.locator("input[placeholder='请选择要保存的文件夹']")
path_input.click()
time.sleep(2)
# 3. 路径选择弹窗内部操作
logger.info(f"📂 正在开启路径选择弹窗,进行多列导航...")
# 修正核心:弹窗标题实为“选择文件” (根据截图中的渲染结果)
file_picker_dialog = self.page.locator(".p-dialog:has-text('选择文件')")
# 定位左侧 3d生成(勿删) 根目录项
folder_item = file_picker_dialog.locator("span:has-text('3d生成(勿删)')").first
folder_item.click()
time.sleep(2) # 留出中间列刷新的渲染窗口
# 定位刚才生成的随机文件夹名称
logger.info(f"🎯 正在定位生成的文件夹项: {asset_name}")
try:
# 延长等待时间 (20s) 提高环境波动兼容性
target_item = file_picker_dialog.locator(f"//span[contains(text(), '{asset_name}')]").last
target_item.wait_for(state="attached", timeout=20000)
target_item.scroll_into_view_if_needed()
target_item.click()
logger.info(f"✅ 文件夹已在 [选择文件] 弹窗中成功选中")
except Exception as e:
logger.warning(f"⚠️ 在 20s 内未能自动选中项 {asset_name},可能是刷新延迟: {e}")
# 4. 点击路径选择弹窗的确认按钮
logger.info("⏳ 点击路径选择器[确定]按钮...")
# 修正:同步标题匹配为“选择文件”
path_dialog_confirm = file_picker_dialog.locator("button:has-text('确定')").last
path_dialog_confirm.click()
logger.info("✅ 路径跳转弹窗已确认并关闭")
time.sleep(2) # 留出 UI 彻底销毁的时间
# 5. 校验路径是否回填
filled_val = path_input.get_attribute("value")
logger.info(f"🔍 检查保存路径回填状态: {filled_val}")
# 6. 点击保存模型弹窗最终确认按钮 (精准定位保存弹窗确定)
logger.info("💾 点击保存模型弹窗最终确认按钮")
save_dialog_confirm = self.page.locator(".p-dialog:has-text('保存模型')").locator("button:has-text('确定')").last
save_dialog_confirm.wait_for(state="visible", timeout=5000)
save_dialog_confirm.click()
logger.info("🎉 3D 生成场景全流程保存确认成功")
time.sleep(2)
return asset_name