test/framework/business/three_d_generation_page.py

165 lines
7.3 KiB
Python

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=600):
"""等待右上角结果出现并点击保存"""
logger.info("⏳ 等待生成结果...")
# 右上角模型参数卡片里出现内容或“保存”按钮变得可用
start_time = time.time()
while time.time() - start_time < timeout:
content = self.page.content()
# 检查是否有反映生成完成的标志,或者截图中的“保存”按钮
if "保存" in content:
save_btn = self.page.locator("button:has-text('保存')").nth(0)
if save_btn.is_visible() and save_btn.is_enabled():
logger.info("✨ 生成结果已出现,点击[保存]按钮")
save_btn.click()
return True
time.sleep(5)
logger.error("❌ 等待结果超时")
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