fix: capture invalid product toast and close failed tabs
This commit is contained in:
@@ -39,10 +39,23 @@ class FakeCDP:
|
||||
|
||||
|
||||
class FakeProductCDP:
|
||||
def __init__(self, ws):
|
||||
def __init__(self, ws, ready=True, toasts=None):
|
||||
self.ws = ws
|
||||
self.ready = ready
|
||||
self.toasts = list(toasts or [])
|
||||
self.closed = False
|
||||
self.sent = []
|
||||
self.toast_observer_installed = False
|
||||
|
||||
def val(self, expr):
|
||||
if expr == editor.JS_READY:
|
||||
return self.ready
|
||||
if expr == editor.JS_INSTALL_TOAST_OBSERVER:
|
||||
self.toast_observer_installed = True
|
||||
return True
|
||||
if expr == editor.JS_PAGE_TOASTS:
|
||||
return json.dumps(self.toasts)
|
||||
return None
|
||||
|
||||
def send(self, method, params=None):
|
||||
self.sent.append((method, params or {}))
|
||||
@@ -439,6 +452,89 @@ class EditorLoginTests(unittest.TestCase):
|
||||
self.assertEqual("127.0.0.1:9223", cdp.cdp_host)
|
||||
create_tab_info.assert_called_once()
|
||||
|
||||
def test_open_product_installs_toast_observer(self):
|
||||
fake = FakeProductCDP("ws-new", ready=True)
|
||||
with mock.patch("app.editor.find_product_tab", return_value=None), mock.patch(
|
||||
"app.editor.create_tab_info",
|
||||
return_value={"id": "target-new", "webSocketDebuggerUrl": "ws-new"},
|
||||
), mock.patch("app.editor.CDP", return_value=fake), mock.patch(
|
||||
"app.editor._ensure_page_domains"
|
||||
), mock.patch("app.editor.time.sleep"):
|
||||
cdp = editor.open_product({"debug_port": 9223}, "51100639511")
|
||||
|
||||
self.assertIs(fake, cdp)
|
||||
self.assertTrue(fake.toast_observer_installed)
|
||||
self.assertIn(
|
||||
("Page.addScriptToEvaluateOnNewDocument", {"source": editor.JS_TOAST_OBSERVER_SOURCE}),
|
||||
fake.sent,
|
||||
)
|
||||
|
||||
def test_wait_ready_raises_product_unavailable_from_hidden_toast(self):
|
||||
fake = FakeProductCDP(
|
||||
"ws-new",
|
||||
ready=False,
|
||||
toasts=[
|
||||
{
|
||||
"text": "please input correct product id",
|
||||
"html": "<div class='eds-toast__content'>please input correct product id</div>",
|
||||
"url": "https://seller.shopee.tw/portal/product/invalid",
|
||||
"visible": False,
|
||||
"created_at": "2026-07-02T00:00:00Z",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
with self.assertRaises(editor.EditorError) as ctx:
|
||||
editor._wait_ready(fake, timeout=0)
|
||||
|
||||
self.assertIn("商品失效", str(ctx.exception))
|
||||
self.assertIn("please input correct product id", str(ctx.exception))
|
||||
toasts = editor.read_page_toasts(fake)
|
||||
self.assertEqual("please input correct product id", toasts[0]["text"])
|
||||
self.assertFalse(toasts[0]["visible"])
|
||||
|
||||
def test_open_product_closes_auto_created_tab_when_unavailable(self):
|
||||
fake = FakeProductCDP(
|
||||
"ws-new",
|
||||
ready=False,
|
||||
toasts=[{"text": "please input correct product id", "visible": False}],
|
||||
)
|
||||
with mock.patch("app.editor.find_product_tab", return_value=None), mock.patch(
|
||||
"app.editor.create_tab_info",
|
||||
return_value={"id": "target-new", "webSocketDebuggerUrl": "ws-new"},
|
||||
), mock.patch("app.editor.CDP", return_value=fake), mock.patch(
|
||||
"app.editor._ensure_page_domains"
|
||||
), mock.patch("app.editor.close_tab", return_value=True) as close_tab:
|
||||
with self.assertRaises(editor.EditorError) as ctx:
|
||||
editor.open_product({"debug_port": 9223}, "bad-item")
|
||||
|
||||
self.assertIn("商品失效", str(ctx.exception))
|
||||
self.assertTrue(fake.closed)
|
||||
close_tab.assert_called_once_with("target-new", host="127.0.0.1:9223")
|
||||
|
||||
def test_open_product_keeps_reused_tab_when_unavailable(self):
|
||||
fake = FakeProductCDP(
|
||||
"ws-existing",
|
||||
ready=False,
|
||||
toasts=[{"text": "please input correct product id", "visible": False}],
|
||||
)
|
||||
with mock.patch(
|
||||
"app.editor.find_product_tab",
|
||||
return_value={"id": "target-existing", "webSocketDebuggerUrl": "ws-existing"},
|
||||
), mock.patch("app.editor.create_tab_info") as create_tab_info, mock.patch(
|
||||
"app.editor.CDP",
|
||||
return_value=fake,
|
||||
), mock.patch("app.editor._ensure_page_domains"), mock.patch(
|
||||
"app.editor.close_tab",
|
||||
return_value=True,
|
||||
) as close_tab:
|
||||
with self.assertRaises(editor.EditorError):
|
||||
editor.open_product({"debug_port": 9223}, "bad-item")
|
||||
|
||||
self.assertTrue(fake.closed)
|
||||
close_tab.assert_not_called()
|
||||
create_tab_info.assert_not_called()
|
||||
|
||||
def test_collect_closes_only_auto_created_product_tab(self):
|
||||
cdp = FakeProductCDP("ws-new")
|
||||
cdp.target_id = "target-new"
|
||||
|
||||
@@ -3,6 +3,7 @@ import unittest
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
from types import SimpleNamespace
|
||||
from unittest import mock
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
@@ -82,6 +83,34 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
self.assertIsNotNone(value)
|
||||
self.assertEqual(color, value.name())
|
||||
|
||||
def test_collect_table_shows_product_unavailable_only_for_explicit_error(self):
|
||||
account = SimpleNamespace(alias="papa", account_name="papa 店铺")
|
||||
invalid_task = SimpleNamespace(
|
||||
alias="papa",
|
||||
account_name="papa 店铺",
|
||||
item_id="25120403046",
|
||||
stage="imported",
|
||||
status="failed",
|
||||
last_error="商品失效:please input correct product id",
|
||||
)
|
||||
generic_task = SimpleNamespace(
|
||||
alias="papa",
|
||||
account_name="papa 店铺",
|
||||
item_id="26887160467",
|
||||
stage="imported",
|
||||
status="failed",
|
||||
last_error="等待 Shopee 商品编辑器就绪超时",
|
||||
)
|
||||
model = gui.TaskTableModel()
|
||||
model.set_tasks([invalid_task, generic_task], [account])
|
||||
|
||||
self.assertEqual("商品失效", model.data(model.index(0, 3), gui.Qt.DisplayRole))
|
||||
self.assertEqual("失败", model.data(model.index(1, 3), gui.Qt.DisplayRole))
|
||||
self.assertIn(
|
||||
"please input correct product id",
|
||||
model.data(model.index(0, 3), gui.Qt.ToolTipRole),
|
||||
)
|
||||
|
||||
def test_main_window_has_five_tabs_in_workflow_order(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
window = MainWindow(config=self.make_config(temp_dir))
|
||||
|
||||
Reference in New Issue
Block a user