import os import sys import unittest os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") sys.path.insert(0, os.path.dirname(__file__)) from _helpers import TempDirMixin from app import accounts, appconfig, image_studio, image_studio_images from app import gui if gui.QT_IMPORT_ERROR is not None: raise unittest.SkipTest("PySide6 未安装") from PySide6.QtGui import QImage from PySide6.QtWidgets import QApplication, QLabel, QPushButton from app.gui.tabs.product_suite import ProductSuiteTab, SuiteResultCard class ProductSuiteGuiTests(TempDirMixin, unittest.TestCase): @classmethod def setUpClass(cls): cls.app = QApplication.instance() or QApplication([]) def tearDown(self): for widget in QApplication.topLevelWidgets(): widget.close() widget.deleteLater() self.app.processEvents() def _config(self, temp_dir): return { "chrome_path": "chrome.exe", "user_data_root": os.path.join(temp_dir, "chrome_user_data_dir"), "image_dir": os.path.join(temp_dir, "images"), "db_path": os.path.join(temp_dir, "cmshopee.db"), "debug_port_range": [9222, 9260], "config_path": os.path.join(temp_dir, "config.json"), "cmhub_config_path": os.path.join(temp_dir, "cmhub.json"), } def _write_image(self, path): image = QImage(40, 30, QImage.Format_RGB32) image.fill(0xFF336699) self.assertTrue(image.save(path)) def test_tab_builds_suite_controls_without_old_detail_workspace(self): with self.make_temp_dir() as temp_dir: config = self._config(temp_dir) accounts.create_account("主店", "alias-a", debug_port=9222, config=config) tab = ProductSuiteTab(config=config, db_path=config["db_path"]) self.addCleanup(tab.close) tab.resize(1180, 760) tab.show() self.app.processEvents() self.assertEqual("productSuiteTab", tab.objectName()) self.assertEqual(1, tab.task_tabs.count()) self.assertEqual("套图任务 1", tab.task_tabs.tabText(0)) self.assertEqual("alias-a", tab.account_combo.currentData()) self.assertEqual("Shopee", tab.platform_combo.currentData()) self.assertEqual("中国台湾", tab.country_combo.currentData()) self.assertEqual("繁体中文", tab.language_combo.currentData()) self.assertEqual("1:1", tab.ratio_combo.currentData()) self.assertEqual("Shopee", tab.platform_combo.currentText()) self.assertEqual("中国台湾", tab.country_combo.currentText()) self.assertEqual("繁体中文", tab.language_combo.currentText()) self.assertEqual("1:1", tab.ratio_combo.currentText()) self.assertEqual("平台", tab.platform_label.text()) self.assertEqual("站点", tab.country_label.text()) self.assertEqual("语言", tab.language_label.text()) self.assertEqual("比例", tab.ratio_label.text()) for column, combo in enumerate( ( tab.platform_combo, tab.country_combo, tab.language_combo, tab.ratio_combo, ) ): self.assertIs(combo, tab.settings_grid.itemAtPosition(1, column).widget()) self.assertEqual( 1, len( { tab.platform_combo.width(), tab.country_combo.width(), tab.language_combo.width(), tab.ratio_combo.width(), } ), ) self.assertLessEqual(tab.task_tabs.maximumHeight(), 36) self.assertEqual(120, tab.account_combo.minimumWidth()) self.assertEqual(160, tab.account_combo.maximumWidth()) self.assertGreaterEqual(tab.item_id_edit.minimumWidth(), 120) self.assertLessEqual(tab.item_id_edit.maximumWidth(), 140) self.assertLess( tab.context_bar_layout.indexOf(tab.history_button), tab.context_bar_layout.indexOf(tab.account_combo), ) self.assertLess( tab.context_bar_layout.indexOf(tab.open_folder_button), tab.context_bar_layout.indexOf(tab.account_combo), ) self.assertLess( tab.context_bar_layout.indexOf(tab.add_images_button), tab.context_bar_layout.indexOf(tab.account_combo), ) self.assertEqual(-1, tab.results_toolbar_layout.indexOf(tab.history_button)) self.assertEqual(-1, tab.results_toolbar_layout.indexOf(tab.open_folder_button)) self.assertEqual("打开结果文件夹", tab.open_folder_button.text()) self.assertEqual("合计 5 张", tab.category_total_label.text()) self.assertEqual("生成套图(5)", tab.generate_button.text()) visible_text = " ".join( [widget.text() for widget in tab.findChildren(QLabel)] + [widget.text() for widget in tab.findChildren(QPushButton)] ) self.assertNotIn("详情图", visible_text) self.assertNotIn("AI工场", visible_text) self.assertIn("白底图", visible_text) self.assertIn("场景图", visible_text) self.assertIn("卖点图", visible_text) tab.add_custom_category() self.assertFalse(tab.custom_category_edit.isHidden()) tab.custom_category_edit.setText("尺寸图") tab._commit_custom_category() self.assertTrue(tab.custom_category_edit.isHidden()) self.assertIn("尺寸图", tab._displayed_state.settings["categories"]) self.assertEqual("尺寸图", tab._displayed_state.active_category) self.assert_removed(temp_dir) def test_recent_dropdown_settings_restore_after_restart_and_project_wins(self): with self.make_temp_dir() as temp_dir: config_path = os.path.join(temp_dir, "config.json") config = appconfig.save_config(self._config(temp_dir), path=config_path) account = accounts.create_account( "主店", "alias-a", debug_port=9222, config=config, ) project = image_studio.create_or_get_project( account, item_id="51100639510", path=config["db_path"], ) project_settings = image_studio.project_suite_settings(project) project_settings["ratio"] = "4:3" image_studio.update_project_suite_settings( project.id, project_settings, path=config["db_path"], ) first = ProductSuiteTab( config=config, config_path=config_path, db_path=config["db_path"], ) first.ratio_combo.setCurrentIndex(first.ratio_combo.findData("16:9")) self.assertEqual( "16:9", appconfig.product_suite_last_settings( appconfig.load_config(config_path) )["ratio"], ) first.close() first.deleteLater() self.app.processEvents() reloaded = appconfig.load_config(config_path) second = ProductSuiteTab( config=reloaded, config_path=config_path, db_path=reloaded["db_path"], ) self.addCleanup(second.close) self.assertEqual("16:9", second.ratio_combo.currentData()) state = second._displayed_state state.account_alias = "alias-a" state.item_id = "51100639510" second._bind_project(state, load_existing=True) self.assertEqual("4:3", second.ratio_combo.currentData()) self.assertEqual("4:3", state.settings["ratio"]) self.assert_removed(temp_dir) def test_task_tabs_keep_independent_prompt_and_settings(self): with self.make_temp_dir() as temp_dir: config = self._config(temp_dir) accounts.create_account("主店", "alias-a", debug_port=9222, config=config) tab = ProductSuiteTab(config=config, db_path=config["db_path"]) self.addCleanup(tab.close) tab.prompt_edit.setPlainText("任务一卖点") first_state = tab._displayed_state tab.ratio_combo.setCurrentIndex(tab.ratio_combo.findData("3:4")) second_state = tab.add_task(inherit=True) self.assertEqual(2, tab.task_tabs.count()) self.assertEqual("任务一卖点", second_state.prompt) self.assertEqual("3:4", second_state.settings["ratio"]) tab.prompt_edit.setPlainText("任务二卖点") tab.ratio_combo.setCurrentIndex(tab.ratio_combo.findData("16:9")) tab.task_tabs.setCurrentIndex(0) self.assertIs(first_state, tab._displayed_state) self.assertEqual("任务一卖点", tab.prompt_edit.toPlainText()) self.assertEqual("3:4", tab.ratio_combo.currentData()) self.assert_removed(temp_dir) def test_project_settings_and_result_history_use_existing_backend(self): with self.make_temp_dir() as temp_dir: config = self._config(temp_dir) account = accounts.create_account("主店", "alias-a", debug_port=9222, config=config) project = image_studio.create_or_get_project( account, item_id="51100639510", path=config["db_path"], ) source_path = os.path.join(temp_dir, "source.png") self._write_image(source_path) source = image_studio_images.import_original_files( project.id, [source_path], path=config["db_path"], config=config, )["assets"][0] job = image_studio.create_job( project.id, source_asset_id=source.id, job_type="场景图", prompt="场景卖点", path=config["db_path"], ) image_studio.update_job_status( job.id, "failed", error="上游超时 https://example.invalid/private", path=config["db_path"], ) tab = ProductSuiteTab(config=config, db_path=config["db_path"]) self.addCleanup(tab.close) state = tab._displayed_state state.account_alias = "alias-a" state.item_id = "51100639510" state.project_id = project.id state.current_job_ids = [job.id] tab._load_state(state) self.assertEqual([source.id], tab.original_list.asset_ids()) self.assertEqual("共 1 张 · 成功 0 张", tab.result_summary_label.text()) cards = tab.findChildren(SuiteResultCard) self.assertEqual(1, len(cards)) self.assertNotIn( "https://", " ".join(label.text() for label in cards[0].findChildren(QLabel)), ) state.settings["ratio"] = "4:3" state.prompt = "持久化卖点" tab._persist_state(state) stored = image_studio.get_project(project.id, path=config["db_path"]) self.assertEqual("持久化卖点", stored.draft_prompt) self.assertEqual("4:3", image_studio.project_suite_settings(stored)["ratio"]) self.assert_removed(temp_dir) if __name__ == "__main__": unittest.main()