mirror of
https://git.zavage.net/Zavage-Software/wabot.git
synced 2026-07-21 13:06:08 -06:00
149 lines
4.9 KiB
Python
149 lines
4.9 KiB
Python
from types import SimpleNamespace
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
from selenium.common.exceptions import WebDriverException
|
|
|
|
from wabot._browser import Browser
|
|
from wabot.pacing import HumanPacing, NoPacing
|
|
from wabot.page import Page
|
|
|
|
|
|
class Login(Page):
|
|
elements = {"username": ("el", ("id", "username"))}
|
|
|
|
def do_login(self):
|
|
return "logged-in"
|
|
|
|
def explode(self):
|
|
raise WebDriverException("browser gone")
|
|
|
|
|
|
class Unverifiable(Page):
|
|
def verify(self):
|
|
return False
|
|
|
|
|
|
@pytest.fixture
|
|
def bot():
|
|
return Browser(MagicMock(name="driver"), pacing=NoPacing())
|
|
|
|
|
|
class TestConstruction:
|
|
def test_good_flag_starts_true(self, bot):
|
|
assert bot.good is True
|
|
|
|
def test_default_pacing_is_human(self):
|
|
assert isinstance(Browser(MagicMock()).pacing, HumanPacing)
|
|
|
|
|
|
class TestSetPage:
|
|
def test_sets_and_returns_true(self, bot):
|
|
assert bot.set_page(Login) is True
|
|
assert isinstance(bot.page, Login)
|
|
|
|
def test_failed_verify_refuses_switch(self, bot):
|
|
bot.set_page(Login)
|
|
assert bot.set_page(Unverifiable) is False
|
|
assert isinstance(bot.page, Login) # unchanged
|
|
|
|
|
|
class TestDelegation:
|
|
def test_getattr_delegates_to_page(self, bot):
|
|
bot.set_page(Login)
|
|
assert bot.do_login() == "logged-in"
|
|
|
|
def test_getitem_delegates_to_page(self, bot):
|
|
bot.set_page(Login)
|
|
el = bot["username"]
|
|
assert el.name == "username"
|
|
|
|
def test_getattr_without_page_raises(self, bot):
|
|
with pytest.raises(AttributeError):
|
|
_ = bot.do_login
|
|
|
|
def test_direct_delegation_does_not_flip_good(self, bot):
|
|
# only perform() traps+flips; direct bot.x() is the raw path
|
|
bot.set_page(Login)
|
|
with pytest.raises(WebDriverException):
|
|
bot.explode()
|
|
assert bot.good is True
|
|
|
|
def test_getitem_without_page_raises_friendly(self, bot):
|
|
with pytest.raises(RuntimeError, match="no current page"):
|
|
bot["username"]
|
|
|
|
def test_repr_shows_page_and_good(self, bot):
|
|
assert "page=None" in repr(bot) and "good=True" in repr(bot)
|
|
bot.set_page(Login)
|
|
assert "page=Login" in repr(bot)
|
|
|
|
|
|
class TestPerform:
|
|
def test_returns_method_result(self, bot):
|
|
bot.set_page(Login)
|
|
assert bot.perform("do_login") == "logged-in"
|
|
|
|
def test_missing_method_returns_none(self, bot):
|
|
bot.set_page(Login)
|
|
assert bot.perform("nope") is None
|
|
|
|
def test_webdriver_exception_flips_good(self, bot):
|
|
bot.set_page(Login)
|
|
assert bot.perform("explode") is None
|
|
assert bot.good is False
|
|
|
|
def test_broken_state_refuses_actions_until_reset(self, bot):
|
|
bot.set_page(Login)
|
|
bot.perform("explode")
|
|
assert bot.perform("do_login") is None # refused
|
|
assert bot.do_login() is None # __getattr__ path also refused
|
|
bot.reset()
|
|
assert bot.perform("do_login") == "logged-in"
|
|
|
|
def test_broken_state_attribute_read_returns_noop_callable(self, bot):
|
|
# documents the footgun: while broken, an attribute read yields the
|
|
# no-op refusal callable, not the underlying value
|
|
bot.set_page(Login)
|
|
bot.perform("explode") # flips good=False
|
|
assert callable(bot.anything)
|
|
|
|
|
|
class TestQuit:
|
|
def test_quit_quits_driver_and_removes_session(self):
|
|
driver, store = MagicMock(), MagicMock()
|
|
store.get.return_value = SimpleNamespace(service_pid=None) # external: nothing to stop
|
|
bot = Browser(driver, session_name="s1", store=store)
|
|
bot.quit()
|
|
driver.quit.assert_called_once_with()
|
|
store.remove.assert_called_once_with("s1")
|
|
|
|
def test_quit_without_session_only_quits(self):
|
|
driver = MagicMock()
|
|
Browser(driver).quit()
|
|
driver.quit.assert_called_once_with()
|
|
|
|
def test_quit_stops_managed_service(self, monkeypatch):
|
|
import wabot._browser as browser_mod
|
|
|
|
stopped = []
|
|
monkeypatch.setattr(browser_mod, "stop_service", lambda pid: stopped.append(pid))
|
|
driver, store = MagicMock(), MagicMock()
|
|
store.get.return_value = SimpleNamespace(service_pid=4321) # managed session
|
|
bot = Browser(driver, session_name="s1", store=store)
|
|
bot.quit()
|
|
assert stopped == [4321]
|
|
store.remove.assert_called_once_with("s1")
|
|
|
|
def test_quit_removes_record_even_if_driver_quit_raises(self, monkeypatch):
|
|
import wabot._browser as browser_mod
|
|
|
|
monkeypatch.setattr(browser_mod, "stop_service", lambda pid: None)
|
|
driver, store = MagicMock(), MagicMock()
|
|
driver.quit.side_effect = WebDriverException("boom")
|
|
store.get.return_value = SimpleNamespace(service_pid=None)
|
|
bot = Browser(driver, session_name="s1", store=store)
|
|
with pytest.raises(WebDriverException):
|
|
bot.quit()
|
|
store.remove.assert_called_once_with("s1") # finally block still ran
|