feat: update driver BaseClass
This commit is contained in:
0
app/tests/conftest.py
Normal file
0
app/tests/conftest.py
Normal file
337
app/tests/test_amazon_base.py
Normal file
337
app/tests/test_amazon_base.py
Normal file
@@ -0,0 +1,337 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from amazon import base
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, payload):
|
||||
self.payload = payload
|
||||
|
||||
def json(self):
|
||||
return self.payload
|
||||
|
||||
|
||||
class FakeWait:
|
||||
def __init__(self, *, raise_on_doc_loaded=False):
|
||||
self.doc_loaded_calls = []
|
||||
self.displayed_calls = []
|
||||
self.raise_on_doc_loaded = raise_on_doc_loaded
|
||||
|
||||
def doc_loaded(self, **kwargs):
|
||||
if self.raise_on_doc_loaded:
|
||||
raise RuntimeError("doc load failed")
|
||||
self.doc_loaded_calls.append(kwargs)
|
||||
|
||||
def displayed(self, **kwargs):
|
||||
self.displayed_calls.append(kwargs)
|
||||
return True
|
||||
|
||||
|
||||
class FakeElement:
|
||||
def __init__(self, *, text="", value=None):
|
||||
self.text = text
|
||||
self.value = value
|
||||
self.clicks = 0
|
||||
self.inputs = []
|
||||
self.wait = FakeWait()
|
||||
|
||||
def click(self, *args, **kwargs):
|
||||
self.clicks += 1
|
||||
|
||||
def input(self, value, clear=False):
|
||||
self.inputs.append((value, clear))
|
||||
|
||||
|
||||
class FakeTab:
|
||||
def __init__(self, *, ele_returns=None, eles_returns=None, raise_on_doc_loaded=False):
|
||||
self.ele_returns = {key: list(values) for key, values in (ele_returns or {}).items()}
|
||||
self.eles_returns = {key: list(values) for key, values in (eles_returns or {}).items()}
|
||||
self.wait = FakeWait(raise_on_doc_loaded=raise_on_doc_loaded)
|
||||
self.refreshes = 0
|
||||
|
||||
def ele(self, locator, timeout=None):
|
||||
values = self.ele_returns.get(locator)
|
||||
if not values:
|
||||
return None
|
||||
return values.pop(0)
|
||||
|
||||
def eles(self, locator, timeout=None):
|
||||
values = self.eles_returns.get(locator)
|
||||
if not values:
|
||||
return []
|
||||
return values.pop(0)
|
||||
|
||||
def refresh(self):
|
||||
self.refreshes += 1
|
||||
|
||||
|
||||
def test_ziniao_driver_initializes_browser_state():
|
||||
user_info = {
|
||||
"company": "test-company",
|
||||
"username": "test-user",
|
||||
"password": "test-password",
|
||||
}
|
||||
|
||||
driver = base.ZiniaoDriver(user_info, socket_port=19999)
|
||||
|
||||
assert driver.user_info is user_info
|
||||
assert driver.socket_port == 19999
|
||||
assert driver.client_path is None
|
||||
assert driver.browser is None
|
||||
assert driver.tab is None
|
||||
assert driver.store_id is None
|
||||
|
||||
|
||||
def test_amazon_base_inherits_ziniao_driver():
|
||||
driver = base.AmamzonBase({})
|
||||
|
||||
assert isinstance(driver, base.ZiniaoDriver)
|
||||
|
||||
|
||||
def test_kill_process_uses_ziniao_driver(monkeypatch):
|
||||
calls = []
|
||||
|
||||
class FakeDriver:
|
||||
def __init__(self, user_info):
|
||||
calls.append(("init", user_info))
|
||||
|
||||
def kill_process(self, version):
|
||||
calls.append(("kill_process", version))
|
||||
|
||||
monkeypatch.setattr(base, "ZiniaoDriver", FakeDriver)
|
||||
|
||||
base.kill_process("v6")
|
||||
|
||||
assert calls == [("init", {}), ("kill_process", "v6")]
|
||||
|
||||
|
||||
def test_get_zinaio_exe_returns_none_when_winreg_unavailable(monkeypatch, caplog):
|
||||
driver = base.ZiniaoDriver({})
|
||||
monkeypatch.setattr(base, "winreg", None)
|
||||
|
||||
with caplog.at_level("ERROR", logger=base.logger.name):
|
||||
result = driver.get_zinaio_exe()
|
||||
|
||||
assert result is None
|
||||
assert any("winreg" in message for message in caplog.messages)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("store_info", "expected_key"),
|
||||
[
|
||||
("12345", "browserId"),
|
||||
("oauth-token", "browserOauth"),
|
||||
],
|
||||
)
|
||||
def test_open_store_payload_uses_id_or_oauth(monkeypatch, store_info, expected_key):
|
||||
payloads = []
|
||||
|
||||
def fake_post(url, data, timeout):
|
||||
payloads.append(json.loads(data.decode("utf-8")))
|
||||
return FakeResponse({"statusCode": 0, "debuggingPort": 9222})
|
||||
|
||||
monkeypatch.setattr(base.requests, "post", fake_post)
|
||||
|
||||
result = base.ZiniaoDriver({"company": "c"}).open_store(store_info)
|
||||
|
||||
assert result["debuggingPort"] == 9222
|
||||
assert payloads[0]["action"] == "startBrowser"
|
||||
assert payloads[0][expected_key] == store_info
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("payload", "expected"),
|
||||
[
|
||||
({"statusCode": 0, "browserList": [{"browserName": "shop"}]}, [{"browserName": "shop"}]),
|
||||
({"statusCode": -10003}, None),
|
||||
({"statusCode": 500, "message": "failed"}, None),
|
||||
],
|
||||
)
|
||||
def test_get_browser_list_status_handling(monkeypatch, payload, expected):
|
||||
monkeypatch.setattr(base.requests, "post", lambda *args, **kwargs: FakeResponse(payload))
|
||||
|
||||
assert base.ZiniaoDriver({}).get_browser_list() == expected
|
||||
|
||||
|
||||
def test_close_store_success_uses_current_store(monkeypatch):
|
||||
payloads = []
|
||||
|
||||
def fake_post(url, data, timeout):
|
||||
payloads.append(json.loads(data.decode("utf-8")))
|
||||
return FakeResponse({"statusCode": 0, "ok": True})
|
||||
|
||||
monkeypatch.setattr(base.requests, "post", fake_post)
|
||||
driver = base.ZiniaoDriver({})
|
||||
driver.store_id = "oauth-1"
|
||||
|
||||
result = driver.close_store()
|
||||
|
||||
assert result["ok"] is True
|
||||
assert payloads[0]["action"] == "stopBrowser"
|
||||
assert payloads[0]["browserOauth"] == "oauth-1"
|
||||
|
||||
|
||||
def test_close_store_login_failure_raises(monkeypatch):
|
||||
monkeypatch.setattr(base.requests, "post", lambda *args, **kwargs: FakeResponse({"statusCode": -10003}))
|
||||
|
||||
with pytest.raises(RuntimeError, match="【close_store】登录失败"):
|
||||
base.ZiniaoDriver({}).close_store("oauth-1")
|
||||
|
||||
|
||||
def test_open_shop_returns_message_when_shop_missing(monkeypatch):
|
||||
driver = base.ZiniaoDriver({})
|
||||
monkeypatch.setattr(driver, "start_client", lambda: None)
|
||||
monkeypatch.setattr(driver, "get_browser_list", lambda: [{"browserName": "other"}])
|
||||
|
||||
assert driver.open_shop("target") == "店铺不存在"
|
||||
assert driver.store_id is None
|
||||
|
||||
|
||||
def test_open_shop_opens_matching_store(monkeypatch):
|
||||
opened_pages = []
|
||||
fake_browser = object()
|
||||
driver = base.ZiniaoDriver({})
|
||||
monkeypatch.setattr(driver, "start_client", lambda: None)
|
||||
monkeypatch.setattr(driver, "get_browser_list", lambda: [{"browserName": "target", "browserOauth": "oauth-1"}])
|
||||
monkeypatch.setattr(
|
||||
driver,
|
||||
"open_store",
|
||||
lambda store_id: {
|
||||
"browserOauth": "oauth-2",
|
||||
"debuggingPort": 9222,
|
||||
"ipDetectionPage": "https://ip-check",
|
||||
"launcherPage": "https://launcher",
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(driver, "get_browser", lambda port: fake_browser)
|
||||
monkeypatch.setattr(driver, "open_ip_check", lambda browser, url: True)
|
||||
monkeypatch.setattr(driver, "open_launcher_page", lambda page, browser: opened_pages.append((page, browser)))
|
||||
|
||||
assert driver.open_shop("target") is fake_browser
|
||||
assert driver.store_id == "oauth-2"
|
||||
assert opened_pages == [("https://launcher", fake_browser)]
|
||||
|
||||
|
||||
def test_switching_countries_returns_true_when_already_current():
|
||||
current_country = FakeElement(text="西班牙")
|
||||
driver = base.AmamzonBase({})
|
||||
driver.browser = object()
|
||||
driver.tab = FakeTab(
|
||||
ele_returns={
|
||||
'xpath://div[@class="dropdown-account-switcher-header-label"]/span[last()]': [current_country],
|
||||
}
|
||||
)
|
||||
|
||||
assert driver.SwitchingCountries("西班牙") is True
|
||||
|
||||
|
||||
def test_switching_countries_returns_false_when_target_missing(monkeypatch):
|
||||
monkeypatch.setattr(base.time, "sleep", lambda seconds: None)
|
||||
driver = base.AmamzonBase({})
|
||||
driver.browser = object()
|
||||
driver.tab = FakeTab(
|
||||
ele_returns={
|
||||
'xpath://div[@class="dropdown-account-switcher-header-label"]/span[last()]': [FakeElement(text="英国")],
|
||||
'xpath://div[@class="dropdown-account-switcher-header-label"]': [FakeElement()],
|
||||
'xpath://div[@class="dropdown-account-switcher-list-item"]': [FakeElement()],
|
||||
'xpath://div[@class="dropdown-account-switcher-list-item dropdown-account-switcher-list-item-indented" and @title="西班牙"]': [
|
||||
None
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
assert driver.SwitchingCountries("西班牙") is False
|
||||
|
||||
|
||||
def test_switching_countries_clicks_and_verifies_target(monkeypatch):
|
||||
monkeypatch.setattr(base.time, "sleep", lambda seconds: None)
|
||||
dropdown = FakeElement()
|
||||
first_item = FakeElement()
|
||||
target = FakeElement()
|
||||
driver = base.AmamzonBase({})
|
||||
driver.browser = object()
|
||||
driver.tab = FakeTab(
|
||||
ele_returns={
|
||||
'xpath://div[@class="dropdown-account-switcher-header-label"]/span[last()]': [
|
||||
FakeElement(text="英国"),
|
||||
FakeElement(text="西班牙"),
|
||||
],
|
||||
'xpath://div[@class="dropdown-account-switcher-header-label"]': [dropdown],
|
||||
'xpath://div[@class="dropdown-account-switcher-list-item"]': [first_item],
|
||||
'xpath://div[@class="dropdown-account-switcher-list-item dropdown-account-switcher-list-item-indented" and @title="西班牙"]': [
|
||||
target
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
assert driver.SwitchingCountries("西班牙") is True
|
||||
assert dropdown.clicks == 1
|
||||
assert first_item.clicks == 1
|
||||
assert target.clicks == 1
|
||||
|
||||
|
||||
def test_login_returns_true_when_otp_submit_succeeds(monkeypatch):
|
||||
monkeypatch.setattr(base.time, "sleep", lambda seconds: None)
|
||||
password_input = FakeElement()
|
||||
password_submit = FakeElement()
|
||||
otp_input = FakeElement(value="123456")
|
||||
otp_submit = FakeElement()
|
||||
driver = base.AmamzonBase({})
|
||||
driver.tab = FakeTab(
|
||||
eles_returns={
|
||||
'xpath://input[@type="password"]': [[password_input]],
|
||||
'xpath://input[@id="signInSubmit"]': [[password_submit]],
|
||||
'xpath://span[@id="auth-send-code" and contains(string(.),"发送一次性密码")]': [[]],
|
||||
'xpath://input[@name="otpCode"]': [[otp_input]],
|
||||
'xpath://input[@id="auth-signin-button"]': [[otp_submit]],
|
||||
'xpath://div[@id="auth-error-message-box"]': [[]],
|
||||
}
|
||||
)
|
||||
|
||||
assert driver.login("secret") is True
|
||||
assert password_input.inputs == [("secret", True)]
|
||||
assert password_submit.clicks == 1
|
||||
assert otp_submit.clicks == 1
|
||||
|
||||
|
||||
def test_login_refreshes_on_otp_error(monkeypatch):
|
||||
monkeypatch.setattr(base.time, "sleep", lambda seconds: None)
|
||||
otp_input = FakeElement(value="123456")
|
||||
driver = base.AmamzonBase({})
|
||||
driver.tab = FakeTab(
|
||||
eles_returns={
|
||||
'xpath://input[@type="password"]': [[]],
|
||||
'xpath://input[@id="signInSubmit"]': [[]],
|
||||
'xpath://span[@id="auth-send-code" and contains(string(.),"发送一次性密码")]': [[]],
|
||||
'xpath://input[@name="otpCode"]': [[otp_input]],
|
||||
'xpath://input[@id="auth-signin-button"]': [[FakeElement()]],
|
||||
'xpath://div[@id="auth-error-message-box"]': [[FakeElement(text="验证码错误")]],
|
||||
}
|
||||
)
|
||||
|
||||
assert driver.login("secret") is False
|
||||
assert driver.tab.refreshes == 1
|
||||
|
||||
|
||||
def test_login_returns_false_when_no_otp_input(monkeypatch):
|
||||
monkeypatch.setattr(base.time, "sleep", lambda seconds: None)
|
||||
driver = base.AmamzonBase({})
|
||||
driver.tab = FakeTab(
|
||||
eles_returns={
|
||||
'xpath://input[@type="password"]': [[]],
|
||||
'xpath://input[@id="signInSubmit"]': [[]],
|
||||
'xpath://span[@id="auth-send-code" and contains(string(.),"发送一次性密码")]': [[]],
|
||||
'xpath://input[@name="otpCode"]': [[]],
|
||||
'xpath://input[@id="auth-signin-button"]': [[]],
|
||||
}
|
||||
)
|
||||
|
||||
assert driver.login("secret") is False
|
||||
|
||||
|
||||
def test_login_returns_false_on_exception():
|
||||
driver = base.AmamzonBase({})
|
||||
driver.tab = FakeTab(raise_on_doc_loaded=True)
|
||||
|
||||
assert driver.login("secret") is False
|
||||
Reference in New Issue
Block a user