2026-07-08 18:03:59 +08:00
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
import unittest
|
|
|
|
|
from unittest import mock
|
|
|
|
|
|
|
|
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
|
|
|
|
|
|
|
|
from _helpers import REPO_ROOT
|
|
|
|
|
|
|
|
|
|
if str(REPO_ROOT) not in sys.path:
|
|
|
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
|
|
|
|
|
|
|
|
from app import cdp
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FakeBrowserCDP:
|
|
|
|
|
sent = []
|
|
|
|
|
fail_first_create = False
|
|
|
|
|
|
|
|
|
|
def __init__(self, ws_url):
|
|
|
|
|
self.ws_url = ws_url
|
|
|
|
|
self.closed = False
|
|
|
|
|
|
|
|
|
|
def send(self, method, params=None):
|
|
|
|
|
self.sent.append((method, params or {}))
|
|
|
|
|
if method == "Target.createTarget":
|
|
|
|
|
if self.fail_first_create:
|
|
|
|
|
type(self).fail_first_create = False
|
|
|
|
|
raise RuntimeError("Target.createTarget: Invalid parameters")
|
|
|
|
|
return {"targetId": "target-new"}
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
|
self.closed = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CdpTests(unittest.TestCase):
|
2026-07-14 12:08:12 +08:00
|
|
|
def test_wait_target_closed_returns_when_target_disappears(self):
|
|
|
|
|
targets = [
|
|
|
|
|
[{"id": "target-closing", "type": "page"}],
|
|
|
|
|
[],
|
|
|
|
|
]
|
|
|
|
|
with mock.patch("app.cdp.http_get", side_effect=targets) as http_get, mock.patch(
|
|
|
|
|
"app.cdp.time.sleep"
|
|
|
|
|
) as sleep:
|
|
|
|
|
closed = cdp.wait_target_closed(
|
|
|
|
|
"target-closing",
|
|
|
|
|
host="127.0.0.1:9222",
|
|
|
|
|
timeout=1.0,
|
|
|
|
|
poll_interval=0.1,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertTrue(closed)
|
|
|
|
|
sleep.assert_called_once_with(0.1)
|
|
|
|
|
self.assertLessEqual(http_get.call_args_list[0].kwargs["timeout"], 1.0)
|
|
|
|
|
|
|
|
|
|
def test_wait_target_closed_stops_at_timeout(self):
|
|
|
|
|
with mock.patch(
|
|
|
|
|
"app.cdp.http_get",
|
|
|
|
|
return_value=[{"id": "target-closing", "type": "page"}],
|
|
|
|
|
) as http_get, mock.patch("app.cdp.time.sleep") as sleep:
|
|
|
|
|
closed = cdp.wait_target_closed(
|
|
|
|
|
"target-closing",
|
|
|
|
|
host="127.0.0.1:9222",
|
|
|
|
|
timeout=0,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertFalse(closed)
|
|
|
|
|
sleep.assert_not_called()
|
|
|
|
|
self.assertEqual(0.05, http_get.call_args.kwargs["timeout"])
|
|
|
|
|
|
|
|
|
|
def test_close_tab_and_wait_only_waits_after_close_request_succeeds(self):
|
|
|
|
|
with mock.patch("app.cdp.close_tab", return_value=True) as close_tab, mock.patch(
|
|
|
|
|
"app.cdp.wait_target_closed",
|
|
|
|
|
return_value=True,
|
|
|
|
|
) as wait_target_closed:
|
|
|
|
|
closed = cdp.close_tab_and_wait(
|
|
|
|
|
"target-closing",
|
|
|
|
|
host="127.0.0.1:9222",
|
|
|
|
|
timeout=1.5,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertTrue(closed)
|
|
|
|
|
close_tab.assert_called_once_with("target-closing", host="127.0.0.1:9222")
|
|
|
|
|
wait_target_closed.assert_called_once_with(
|
|
|
|
|
"target-closing",
|
|
|
|
|
host="127.0.0.1:9222",
|
|
|
|
|
timeout=1.5,
|
|
|
|
|
poll_interval=0.1,
|
|
|
|
|
)
|
|
|
|
|
|
2026-07-08 18:03:59 +08:00
|
|
|
def test_create_tab_info_can_request_background_target(self):
|
|
|
|
|
FakeBrowserCDP.sent = []
|
|
|
|
|
FakeBrowserCDP.fail_first_create = False
|
|
|
|
|
responses = [
|
|
|
|
|
{"webSocketDebuggerUrl": "ws-browser"},
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"id": "target-new",
|
|
|
|
|
"type": "page",
|
|
|
|
|
"url": "https://seller.shopee.tw/portal/product/1",
|
|
|
|
|
"webSocketDebuggerUrl": "ws-page",
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
with mock.patch("app.cdp.http_get", side_effect=responses), \
|
|
|
|
|
mock.patch("app.cdp.CDP", FakeBrowserCDP), \
|
|
|
|
|
mock.patch("app.cdp.time.sleep"):
|
|
|
|
|
tab = cdp.create_tab_info(
|
|
|
|
|
"https://seller.shopee.tw/portal/product/1",
|
|
|
|
|
host="127.0.0.1:9222",
|
|
|
|
|
background=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertEqual("ws-page", tab["webSocketDebuggerUrl"])
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
[
|
|
|
|
|
(
|
|
|
|
|
"Target.createTarget",
|
|
|
|
|
{
|
|
|
|
|
"url": "https://seller.shopee.tw/portal/product/1",
|
|
|
|
|
"background": True,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
FakeBrowserCDP.sent,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_create_tab_info_keeps_foreground_default_params(self):
|
|
|
|
|
FakeBrowserCDP.sent = []
|
|
|
|
|
FakeBrowserCDP.fail_first_create = False
|
|
|
|
|
responses = [
|
|
|
|
|
{"webSocketDebuggerUrl": "ws-browser"},
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"id": "target-new",
|
|
|
|
|
"type": "page",
|
|
|
|
|
"url": "https://seller.shopee.tw/portal/product/1",
|
|
|
|
|
"webSocketDebuggerUrl": "ws-page",
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
with mock.patch("app.cdp.http_get", side_effect=responses), \
|
|
|
|
|
mock.patch("app.cdp.CDP", FakeBrowserCDP), \
|
|
|
|
|
mock.patch("app.cdp.time.sleep"):
|
|
|
|
|
cdp.create_tab_info(
|
|
|
|
|
"https://seller.shopee.tw/portal/product/1",
|
|
|
|
|
host="127.0.0.1:9222",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
[
|
|
|
|
|
(
|
|
|
|
|
"Target.createTarget",
|
|
|
|
|
{"url": "https://seller.shopee.tw/portal/product/1"},
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
FakeBrowserCDP.sent,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_create_tab_info_falls_back_when_background_is_rejected(self):
|
|
|
|
|
FakeBrowserCDP.sent = []
|
|
|
|
|
FakeBrowserCDP.fail_first_create = True
|
|
|
|
|
responses = [
|
|
|
|
|
{"webSocketDebuggerUrl": "ws-browser"},
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"id": "target-new",
|
|
|
|
|
"type": "page",
|
|
|
|
|
"url": "https://seller.shopee.tw/portal/product/1",
|
|
|
|
|
"webSocketDebuggerUrl": "ws-page",
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
with mock.patch("app.cdp.http_get", side_effect=responses), \
|
|
|
|
|
mock.patch("app.cdp.CDP", FakeBrowserCDP), \
|
|
|
|
|
mock.patch("app.cdp.time.sleep"):
|
|
|
|
|
cdp.create_tab_info(
|
|
|
|
|
"https://seller.shopee.tw/portal/product/1",
|
|
|
|
|
host="127.0.0.1:9222",
|
|
|
|
|
background=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
[
|
|
|
|
|
(
|
|
|
|
|
"Target.createTarget",
|
|
|
|
|
{
|
|
|
|
|
"url": "https://seller.shopee.tw/portal/product/1",
|
|
|
|
|
"background": True,
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"Target.createTarget",
|
|
|
|
|
{"url": "https://seller.shopee.tw/portal/product/1"},
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
FakeBrowserCDP.sent,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
unittest.main()
|