feat(product-suite): autosave prompt edits

This commit is contained in:
chengma
2026-07-16 16:39:40 +08:00
parent a9acfee460
commit f5892bb310
4 changed files with 433 additions and 10 deletions
+268
View File
@@ -20,6 +20,7 @@ from PySide6.QtTest import QTest
from PySide6.QtWidgets import QApplication, QLabel, QListWidgetItem, QPushButton
from app.gui.tabs.product_suite import (
AutoHeightPlainTextEdit,
ORIGINAL_CHECK_STATE_ROLE,
ProductOriginalDelegate,
ProductOriginalList,
@@ -555,6 +556,273 @@ class ProductSuiteGuiTests(TempDirMixin, unittest.TestCase):
self.assert_removed(temp_dir)
def test_prompt_autosaves_after_debounce_and_ignores_unchanged_text(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",
draft_prompt="原卖点",
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 = project.item_id
state.project_id = project.id
state.project_binding_state = project.binding_state
state.prompt = project.draft_prompt
state.last_saved_prompt = project.draft_prompt
tab._load_state(state)
original_update = image_studio.update_project_prompt
with mock.patch(
"app.gui.tabs.product_suite.image_studio.update_project_prompt",
wraps=original_update,
) as update_prompt:
tab.prompt_edit.setPlainText("第一版卖点")
tab.prompt_edit.setPlainText("最终卖点")
QTest.qWait(650)
self.app.processEvents()
stored = image_studio.get_project(project.id, path=config["db_path"])
self.assertEqual("最终卖点", stored.draft_prompt)
self.assertEqual(1, update_prompt.call_count)
QTest.qWait(600)
self.app.processEvents()
self.assertEqual(1, update_prompt.call_count)
self.assert_removed(temp_dir)
def test_prompt_switch_and_close_flush_to_the_correct_projects(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,
)
first_project = image_studio.create_or_get_project(
account,
item_id="51100639510",
path=config["db_path"],
)
second_project = image_studio.create_or_get_project(
account,
item_id="51100639511",
path=config["db_path"],
)
tab = ProductSuiteTab(config=config, db_path=config["db_path"])
first_state = tab._displayed_state
first_state.account_alias = "alias-a"
first_state.item_id = first_project.item_id
first_state.project_id = first_project.id
first_state.project_binding_state = first_project.binding_state
tab._load_state(first_state)
tab.prompt_edit.setPlainText("商品一卖点")
second_state = tab.add_task(inherit=False)
second_state.account_alias = "alias-a"
second_state.item_id = second_project.item_id
second_state.project_id = second_project.id
second_state.project_binding_state = second_project.binding_state
tab._load_state(second_state)
tab.prompt_edit.setPlainText("商品二卖点")
tab.task_tabs.setCurrentIndex(0)
self.app.processEvents()
self.assertEqual(
"商品一卖点",
image_studio.get_project(
first_project.id,
path=config["db_path"],
).draft_prompt,
)
self.assertEqual(
"商品二卖点",
image_studio.get_project(
second_project.id,
path=config["db_path"],
).draft_prompt,
)
tab.prompt_edit.setPlainText("商品一关闭任务前卖点")
tab.close_task(0)
self.app.processEvents()
self.assertEqual(
"商品一关闭任务前卖点",
image_studio.get_project(
first_project.id,
path=config["db_path"],
).draft_prompt,
)
self.assertIs(second_state, tab._displayed_state)
tab.prompt_edit.setPlainText("商品二关闭程序前卖点")
tab.close()
self.app.processEvents()
self.assertEqual(
"商品二关闭程序前卖点",
image_studio.get_project(
second_project.id,
path=config["db_path"],
).draft_prompt,
)
self.assert_removed(temp_dir)
def test_prompt_without_project_stays_in_memory_until_draft_creation(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)
state = tab._displayed_state
tab.prompt_edit.setPlainText("尚未建立项目的卖点")
QTest.qWait(650)
self.app.processEvents()
self.assertEqual([], image_studio.list_projects(path=config["db_path"]))
draft = tab._create_draft_project(state)
self.assertIsNotNone(draft)
stored = image_studio.get_project(draft.id, path=config["db_path"])
self.assertEqual("尚未建立项目的卖点", stored.draft_prompt)
self.assertEqual("尚未建立项目的卖点", state.last_saved_prompt)
self.assert_removed(temp_dir)
def test_prompt_autosave_failure_keeps_memory_text_and_reports_chinese_error(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"],
)
status = mock.Mock()
tab = ProductSuiteTab(
config=config,
db_path=config["db_path"],
status_callback=status,
)
self.addCleanup(tab.close)
state = tab._displayed_state
state.account_alias = "alias-a"
state.item_id = project.item_id
state.project_id = project.id
state.project_binding_state = project.binding_state
tab._load_state(state)
with mock.patch(
"app.gui.tabs.product_suite.image_studio.update_project_prompt",
side_effect=OSError("disk unavailable"),
):
tab.prompt_edit.setPlainText("保存失败仍保留")
QTest.qWait(650)
self.app.processEvents()
self.assertEqual("保存失败仍保留", state.prompt)
self.assertEqual("", state.last_saved_prompt)
self.assertTrue(
any(
"商品卖点自动保存失败" in str(call.args[0])
for call in status.call_args_list
)
)
self.assertTrue(
any(call.kwargs.get("level") == "danger" for call in status.call_args_list)
)
self.assert_removed(temp_dir)
def test_ai_write_result_saves_prompt_without_an_extra_user_action(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",
draft_prompt="原卖点",
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 = project.item_id
state.project_id = project.id
state.project_binding_state = project.binding_state
state.prompt = project.draft_prompt
state.last_saved_prompt = project.draft_prompt
state.ai_prompt_snapshot = project.draft_prompt
tab._load_state(state)
tab._on_ai_write_finished(
state,
{"ok": True, "cancelled": False, "text": "AI生成的新卖点"},
)
stored = image_studio.get_project(project.id, path=config["db_path"])
self.assertEqual("AI生成的新卖点", stored.draft_prompt)
self.assertEqual("AI生成的新卖点", state.last_saved_prompt)
self.assertEqual("AI生成的新卖点", tab.prompt_edit.toPlainText())
self.assert_removed(temp_dir)
def test_prompt_edit_expands_shrinks_and_reflows_without_internal_scrollbars(self):
edit = AutoHeightPlainTextEdit()
self.addCleanup(edit.close)
edit.resize(420, 96)
edit.show()
self.app.processEvents()
self.assertEqual(Qt.ScrollBarAlwaysOff, edit.horizontalScrollBarPolicy())
self.assertEqual(Qt.ScrollBarAlwaysOff, edit.verticalScrollBarPolicy())
self.assertGreaterEqual(edit.height(), 96)
minimum_height = edit.height()
edit.setPlainText("\n".join("第%d行商品卖点" % index for index in range(1, 13)))
QTest.qWait(50)
self.app.processEvents()
expanded_height = edit.height()
self.assertGreater(expanded_height, minimum_height)
edit.clear()
QTest.qWait(50)
self.app.processEvents()
self.assertEqual(minimum_height, edit.height())
edit.setPlainText("这是一段用于测试窗口变窄后自动换行的商品卖点内容。" * 16)
edit.setFixedWidth(420)
QTest.qWait(50)
self.app.processEvents()
wide_height = edit.height()
edit.setFixedWidth(180)
QTest.qWait(50)
self.app.processEvents()
self.assertGreater(edit.height(), wide_height)
def test_project_settings_and_result_history_use_existing_backend(self):
with self.make_temp_dir() as temp_dir:
config = self._config(temp_dir)