feat(product-suite): autosave prompt edits
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user