feat: 完成T-501b设置页角色与生成参数

- 在SettingsTab增加标题/图片默认模型角色下拉并按text/image过滤

- 增加并发、重试、分辨率、返回超时展示和jpg质量设置

- 增加Chrome路径、账号数据根目录、图片目录、DB路径和端口配置

- 保存到config.json并校验端口范围,避免写入测试注入路径

- 补充GUI测试并同步任务、api、routes、current-state和progress
This commit is contained in:
chengma
2026-06-29 08:58:39 +08:00
parent bf961e62d6
commit 91683abe7d
7 changed files with 424 additions and 17 deletions
+148
View File
@@ -54,6 +54,7 @@ class GuiTests(TempDirMixin, unittest.TestCase):
"db_path": os.path.join(temp_dir, "cmshopee.db"),
"debug_port_range": [9222, 9260],
"ai_models_path": os.path.join(temp_dir, "ai_models.json"),
"config_path": os.path.join(temp_dir, "config.json"),
}
def test_main_window_has_five_tabs_in_workflow_order(self):
@@ -215,6 +216,153 @@ class GuiTests(TempDirMixin, unittest.TestCase):
self.assert_removed(temp_dir)
def test_settings_tab_saves_role_generation_path_and_port_config(self):
with self.make_temp_dir() as temp_dir:
cfg = self.make_config(temp_dir)
cfg["ai"] = appconfig.default_config()["ai"]
cfg["ai"]["default_text_model"] = "Text A"
cfg["ai"]["default_image_model"] = "Image A"
models_path = cfg["ai_models_path"]
config_path = cfg["config_path"]
appconfig.save_ai_models_config(
{
"models": [
{
"name": "Text A",
"category": "text",
"enabled": True,
"url": "",
"model": "",
"api_key": "",
"api_type": "chat",
"connect_timeout_seconds": 30,
"timeout_seconds": 0,
"extra_body": {},
},
{
"name": "Text B",
"category": "text",
"enabled": True,
"url": "",
"model": "",
"api_key": "",
"api_type": "chat",
"connect_timeout_seconds": 30,
"timeout_seconds": 0,
"extra_body": {},
},
{
"name": "Image A",
"category": "image",
"enabled": True,
"url": "",
"model": "",
"api_key": "",
"api_type": "auto",
"connect_timeout_seconds": 30,
"timeout_seconds": 0,
"extra_body": {},
},
{
"name": "Image B",
"category": "image",
"enabled": True,
"url": "",
"model": "",
"api_key": "",
"api_type": "auto",
"connect_timeout_seconds": 30,
"timeout_seconds": 0,
"extra_body": {},
},
]
},
path=models_path,
)
statuses = []
tab = SettingsTab(
config=cfg,
config_path=config_path,
ai_models_path=models_path,
status_callback=statuses.append,
)
self.addCleanup(tab.close)
text_roles = [
tab.default_text_model_combo.itemData(index)
for index in range(tab.default_text_model_combo.count())
]
image_roles = [
tab.default_image_model_combo.itemData(index)
for index in range(tab.default_image_model_combo.count())
]
self.assertEqual(["Text A", "Text B"], text_roles)
self.assertEqual(["Image A", "Image B"], image_roles)
tab.default_text_model_combo.setCurrentIndex(
tab.default_text_model_combo.findData("Text B")
)
tab.default_image_model_combo.setCurrentIndex(
tab.default_image_model_combo.findData("Image B")
)
tab.title_concurrency_spin.setValue(3)
tab.image_concurrency_spin.setValue(2)
tab.retry_spin.setValue(1)
tab.resolution_combo.setCurrentIndex(tab.resolution_combo.findData("2k"))
self.assertEqual("360 秒", tab.response_timeout_label.text())
tab.jpg_quality_spin.setValue(86)
tab.chrome_path_edit.setText("D:\\Chrome\\chrome.exe")
tab.user_data_root_edit.setText("profiles")
tab.image_dir_edit.setText("pictures")
tab.db_path_edit.setText("data\\cmshopee.db")
tab.default_debug_port_spin.setValue(9300)
tab.debug_port_start_spin.setValue(9300)
tab.debug_port_end_spin.setValue(9350)
tab.cdp_ready_timeout_spin.setValue(45)
tab.save_app_settings()
saved = appconfig.load_config(config_path)
self.assertEqual("Text B", saved["ai"]["default_text_model"])
self.assertEqual("Image B", saved["ai"]["default_image_model"])
self.assertEqual(3, saved["ai"]["title_concurrency"])
self.assertEqual(2, saved["ai"]["image_concurrency"])
self.assertEqual(1, saved["ai"]["retry"])
self.assertEqual("2k", saved["ai"]["resolution"])
self.assertEqual(86, saved["ai"]["jpg_quality"])
self.assertEqual("D:\\Chrome\\chrome.exe", saved["chrome_path"])
self.assertEqual("profiles", saved["user_data_root"])
self.assertEqual("pictures", saved["image_dir"])
self.assertEqual("data\\cmshopee.db", saved["db_path"])
self.assertEqual(9300, saved["default_debug_port"])
self.assertEqual([9300, 9350], saved["debug_port_range"])
self.assertEqual(45, saved["cdp_ready_timeout"])
self.assertNotIn("ai_models_path", saved)
self.assertNotIn("config_path", saved)
self.assertIn("设置已保存", statuses[-1])
self.assert_removed(temp_dir)
def test_settings_tab_rejects_invalid_port_range(self):
with self.make_temp_dir() as temp_dir:
cfg = self.make_config(temp_dir)
tab = SettingsTab(
config=cfg,
config_path=cfg["config_path"],
ai_models_path=cfg["ai_models_path"],
)
self.addCleanup(tab.close)
tab.debug_port_start_spin.setValue(9400)
tab.debug_port_end_spin.setValue(9300)
with mock.patch("app.gui.QMessageBox.warning") as warning:
tab.save_app_settings()
warning.assert_called_once()
self.assertFalse(os.path.exists(cfg["config_path"]))
self.assert_removed(temp_dir)
def test_ai_model_test_worker_calls_appconfig(self):
with self.make_temp_dir() as temp_dir:
models_path = os.path.join(temp_dir, "ai_models.json")