mirror of
https://git.zavage.net/Zavage-Software/wabot.git
synced 2026-07-21 13:06:08 -06:00
style: apply ruff format across the codebase
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
5ec5ad615a
commit
68978a34e7
@ -31,9 +31,7 @@ from .pacing import HumanPacing
|
|||||||
LOGGER = logging.getLogger("wabot")
|
LOGGER = logging.getLogger("wabot")
|
||||||
|
|
||||||
# attributes that must never be delegated to the page
|
# attributes that must never be delegated to the page
|
||||||
_OWN_ATTRS = frozenset(
|
_OWN_ATTRS = frozenset({"driver", "pacing", "session_name", "good", "page", "_store"})
|
||||||
{"driver", "pacing", "session_name", "good", "page", "_store"}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _refused(*_args, **_kwargs):
|
def _refused(*_args, **_kwargs):
|
||||||
@ -69,9 +67,7 @@ class Browser:
|
|||||||
if name in _OWN_ATTRS or name.startswith("__"):
|
if name in _OWN_ATTRS or name.startswith("__"):
|
||||||
raise AttributeError(name)
|
raise AttributeError(name)
|
||||||
if self.page is None:
|
if self.page is None:
|
||||||
raise AttributeError(
|
raise AttributeError(f"{name!r}: no current page — call set_page() first")
|
||||||
f"{name!r}: no current page — call set_page() first"
|
|
||||||
)
|
|
||||||
if not self.good:
|
if not self.good:
|
||||||
LOGGER.warning("broken state — refusing page action %r (call reset())", name)
|
LOGGER.warning("broken state — refusing page action %r (call reset())", name)
|
||||||
return _refused
|
return _refused
|
||||||
|
|||||||
@ -72,9 +72,7 @@ class Page:
|
|||||||
LOGGER.warning("element not in page map: %s", key)
|
LOGGER.warning("element not in page map: %s", key)
|
||||||
return NullField(name=key)
|
return NullField(name=key)
|
||||||
if not isinstance(locators, (tuple, list)) or len(locators) < 2:
|
if not isinstance(locators, (tuple, list)) or len(locators) < 2:
|
||||||
LOGGER.error(
|
LOGGER.error("malformed element %r: expected (type, accessors), got %r", key, locators)
|
||||||
"malformed element %r: expected (type, accessors), got %r", key, locators
|
|
||||||
)
|
|
||||||
return NullField(name=key)
|
return NullField(name=key)
|
||||||
obj_type, accessors = locators[0], locators[1]
|
obj_type, accessors = locators[0], locators[1]
|
||||||
# built-in types need a (by, value) accessors pair; a custom field class
|
# built-in types need a (by, value) accessors pair; a custom field class
|
||||||
|
|||||||
@ -26,9 +26,7 @@ def pytest_collection_modifyitems(items):
|
|||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def site_url():
|
def site_url():
|
||||||
"""Serve tests/fixtures over HTTP on an ephemeral port."""
|
"""Serve tests/fixtures over HTTP on an ephemeral port."""
|
||||||
handler = functools.partial(
|
handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(FIXTURES))
|
||||||
http.server.SimpleHTTPRequestHandler, directory=str(FIXTURES)
|
|
||||||
)
|
|
||||||
server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), handler)
|
server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), handler)
|
||||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|||||||
@ -17,7 +17,8 @@ def external_chromedriver():
|
|||||||
pytest.skip("chromedriver not on PATH")
|
pytest.skip("chromedriver not on PATH")
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
[binary, "--port=19515"],
|
[binary, "--port=19515"],
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
)
|
)
|
||||||
url = "http://127.0.0.1:19515"
|
url = "http://127.0.0.1:19515"
|
||||||
deadline = time.monotonic() + 15
|
deadline = time.monotonic() + 15
|
||||||
@ -31,8 +32,12 @@ def external_chromedriver():
|
|||||||
|
|
||||||
def test_persistent_session_on_external_host(external_chromedriver, site_url, store):
|
def test_persistent_session_on_external_host(external_chromedriver, site_url, store):
|
||||||
bot = wabot.browser(
|
bot = wabot.browser(
|
||||||
session="ext", browser="chromium", host=external_chromedriver,
|
session="ext",
|
||||||
headless=True, pacing=wabot.NoPacing(), store=store,
|
browser="chromium",
|
||||||
|
host=external_chromedriver,
|
||||||
|
headless=True,
|
||||||
|
pacing=wabot.NoPacing(),
|
||||||
|
store=store,
|
||||||
)
|
)
|
||||||
bot.driver.get(f"{site_url}/welcome.html")
|
bot.driver.get(f"{site_url}/welcome.html")
|
||||||
record = store.get("ext")
|
record = store.get("ext")
|
||||||
@ -50,8 +55,11 @@ def test_persistent_session_on_external_host(external_chromedriver, site_url, st
|
|||||||
|
|
||||||
def test_ephemeral_on_external_host(external_chromedriver, site_url, store):
|
def test_ephemeral_on_external_host(external_chromedriver, site_url, store):
|
||||||
bot = wabot.browser(
|
bot = wabot.browser(
|
||||||
host=external_chromedriver, browser="chromium",
|
host=external_chromedriver,
|
||||||
headless=True, pacing=wabot.NoPacing(), store=store,
|
browser="chromium",
|
||||||
|
headless=True,
|
||||||
|
pacing=wabot.NoPacing(),
|
||||||
|
store=store,
|
||||||
)
|
)
|
||||||
bot.driver.get(f"{site_url}/welcome.html")
|
bot.driver.get(f"{site_url}/welcome.html")
|
||||||
assert store.names() == [] # session=None never persists
|
assert store.names() == [] # session=None never persists
|
||||||
|
|||||||
@ -28,8 +28,11 @@ REATTACH_SCRIPT = textwrap.dedent(
|
|||||||
@pytest.mark.parametrize("browser_name", BROWSERS)
|
@pytest.mark.parametrize("browser_name", BROWSERS)
|
||||||
def test_second_process_picks_up_saved_browser(browser_name, site_url, store, tmp_path):
|
def test_second_process_picks_up_saved_browser(browser_name, site_url, store, tmp_path):
|
||||||
bot = wabot.browser(
|
bot = wabot.browser(
|
||||||
session="flagship", browser=browser_name, headless=True,
|
session="flagship",
|
||||||
pacing=wabot.NoPacing(), store=store,
|
browser=browser_name,
|
||||||
|
headless=True,
|
||||||
|
pacing=wabot.NoPacing(),
|
||||||
|
store=store,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
bot.driver.get(f"{site_url}/login.html")
|
bot.driver.get(f"{site_url}/login.html")
|
||||||
@ -41,7 +44,10 @@ def test_second_process_picks_up_saved_browser(browser_name, site_url, store, tm
|
|||||||
|
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
[sys.executable, "-c", REATTACH_SCRIPT, str(store.path), "flagship"],
|
[sys.executable, "-c", REATTACH_SCRIPT, str(store.path), "flagship"],
|
||||||
capture_output=True, text=True, timeout=120, check=True,
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=120,
|
||||||
|
check=True,
|
||||||
)
|
)
|
||||||
payload = json.loads(result.stdout.strip().splitlines()[-1])
|
payload = json.loads(result.stdout.strip().splitlines()[-1])
|
||||||
assert payload["url"] == f"{site_url}/login.html"
|
assert payload["url"] == f"{site_url}/login.html"
|
||||||
@ -54,8 +60,11 @@ def test_destroyed_session_is_really_gone(site_url, store):
|
|||||||
from wabot.hosts import service_alive
|
from wabot.hosts import service_alive
|
||||||
|
|
||||||
bot = wabot.browser(
|
bot = wabot.browser(
|
||||||
session="doomed", browser="chromium", headless=True,
|
session="doomed",
|
||||||
pacing=wabot.NoPacing(), store=store,
|
browser="chromium",
|
||||||
|
headless=True,
|
||||||
|
pacing=wabot.NoPacing(),
|
||||||
|
store=store,
|
||||||
)
|
)
|
||||||
bot.driver.get(f"{site_url}/login.html")
|
bot.driver.get(f"{site_url}/login.html")
|
||||||
record = store.get("doomed")
|
record = store.get("doomed")
|
||||||
|
|||||||
@ -158,11 +158,17 @@ class TestResourceCleanup:
|
|||||||
|
|
||||||
stopped = []
|
stopped = []
|
||||||
monkeypatch.setattr(wabot, "stop_service", lambda pid: stopped.append(pid))
|
monkeypatch.setattr(wabot, "stop_service", lambda pid: stopped.append(pid))
|
||||||
store.save(wabot.SessionRecord(
|
store.save(
|
||||||
name="s1", executor_url="http://127.0.0.1:7777", session_id="old",
|
wabot.SessionRecord(
|
||||||
browser="chromium", created_at=datetime.now(timezone.utc).isoformat(),
|
name="s1",
|
||||||
service_pid=9999, service_port=7777,
|
executor_url="http://127.0.0.1:7777",
|
||||||
))
|
session_id="old",
|
||||||
|
browser="chromium",
|
||||||
|
created_at=datetime.now(timezone.utc).isoformat(),
|
||||||
|
service_pid=9999,
|
||||||
|
service_port=7777,
|
||||||
|
)
|
||||||
|
)
|
||||||
fake_selenium.service_alive.return_value = True
|
fake_selenium.service_alive.return_value = True
|
||||||
fake_selenium.attach.return_value = None # session dead on a live server
|
fake_selenium.attach.return_value = None # session dead on a live server
|
||||||
wabot.browser(session="s1", store=store)
|
wabot.browser(session="s1", store=store)
|
||||||
@ -176,8 +182,11 @@ class TestDestroyEdges:
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
defaults = dict(
|
defaults = dict(
|
||||||
name="s1", executor_url="http://127.0.0.1:7777", session_id="old",
|
name="s1",
|
||||||
browser="chromium", created_at=datetime.now(timezone.utc).isoformat(),
|
executor_url="http://127.0.0.1:7777",
|
||||||
|
session_id="old",
|
||||||
|
browser="chromium",
|
||||||
|
created_at=datetime.now(timezone.utc).isoformat(),
|
||||||
)
|
)
|
||||||
defaults.update(kw)
|
defaults.update(kw)
|
||||||
store.save(wabot.SessionRecord(**defaults))
|
store.save(wabot.SessionRecord(**defaults))
|
||||||
|
|||||||
@ -97,7 +97,7 @@ class TestPerform:
|
|||||||
bot.set_page(Login)
|
bot.set_page(Login)
|
||||||
bot.perform("explode")
|
bot.perform("explode")
|
||||||
assert bot.perform("do_login") is None # refused
|
assert bot.perform("do_login") is None # refused
|
||||||
assert bot.do_login() is None # __getattr__ path also refused
|
assert bot.do_login() is None # __getattr__ path also refused
|
||||||
bot.reset()
|
bot.reset()
|
||||||
assert bot.perform("do_login") == "logged-in"
|
assert bot.perform("do_login") == "logged-in"
|
||||||
|
|
||||||
|
|||||||
@ -73,9 +73,7 @@ class TestGetProxy:
|
|||||||
browser = make_browser()
|
browser = make_browser()
|
||||||
page = BasePage(browser)
|
page = BasePage(browser)
|
||||||
result = page["rows"]
|
result = page["rows"]
|
||||||
browser.driver.find_elements.assert_called_once_with(
|
browser.driver.find_elements.assert_called_once_with(by="css selector", value="tr.row")
|
||||||
by="css selector", value="tr.row"
|
|
||||||
)
|
|
||||||
assert result is browser.driver.find_elements.return_value
|
assert result is browser.driver.find_elements.return_value
|
||||||
|
|
||||||
def test_unknown_key_returns_falsy_nullfield(self):
|
def test_unknown_key_returns_falsy_nullfield(self):
|
||||||
|
|||||||
@ -16,9 +16,7 @@ class TestReattachingRemote:
|
|||||||
assert isinstance(driver.caps, dict)
|
assert isinstance(driver.caps, dict)
|
||||||
|
|
||||||
def test_works_for_firefox_options_too(self):
|
def test_works_for_firefox_options_too(self):
|
||||||
driver = ReattachingRemote(
|
driver = ReattachingRemote("http://127.0.0.1:1", "sid", options=build_options("firefox"))
|
||||||
"http://127.0.0.1:1", "sid", options=build_options("firefox")
|
|
||||||
)
|
|
||||||
assert driver.session_id == "sid"
|
assert driver.session_id == "sid"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,8 @@ class TestChromiumPath:
|
|||||||
assert Image.open(out).size == (100, 150)
|
assert Image.open(out).size == (100, 150)
|
||||||
# scrolled at least twice beyond the initial position
|
# scrolled at least twice beyond the initial position
|
||||||
scroll_calls = [
|
scroll_calls = [
|
||||||
c for c in driver.execute_script.call_args_list
|
c
|
||||||
|
for c in driver.execute_script.call_args_list
|
||||||
if c.args and str(c.args[0]).startswith("window.scrollTo")
|
if c.args and str(c.args[0]).startswith("window.scrollTo")
|
||||||
]
|
]
|
||||||
assert len(scroll_calls) >= 2
|
assert len(scroll_calls) >= 2
|
||||||
@ -77,17 +78,17 @@ class TestChromiumPath:
|
|||||||
"return window.innerHeight": 60,
|
"return window.innerHeight": 60,
|
||||||
}.get(script) # window.scrollTo(...) -> None
|
}.get(script) # window.scrollTo(...) -> None
|
||||||
driver.get_screenshot_as_png.side_effect = [
|
driver.get_screenshot_as_png.side_effect = [
|
||||||
png_bytes(100, 60, (255, 0, 0)), # tile @ y=0
|
png_bytes(100, 60, (255, 0, 0)), # tile @ y=0
|
||||||
png_bytes(100, 60, (0, 255, 0)), # tile @ y=60
|
png_bytes(100, 60, (0, 255, 0)), # tile @ y=60
|
||||||
png_bytes(100, 60, (0, 0, 255)), # tile @ y=120, clamped to paste_y=90
|
png_bytes(100, 60, (0, 0, 255)), # tile @ y=120, clamped to paste_y=90
|
||||||
]
|
]
|
||||||
out = tmp_path / "shot.png"
|
out = tmp_path / "shot.png"
|
||||||
assert save_full_page(driver, str(out)) is True
|
assert save_full_page(driver, str(out)) is True
|
||||||
img = Image.open(out)
|
img = Image.open(out)
|
||||||
assert img.size == (100, 150)
|
assert img.size == (100, 150)
|
||||||
assert img.getpixel((50, 30)) == (255, 0, 0) # first tile
|
assert img.getpixel((50, 30)) == (255, 0, 0) # first tile
|
||||||
assert img.getpixel((50, 75)) == (0, 255, 0) # second tile (rows 60-89)
|
assert img.getpixel((50, 75)) == (0, 255, 0) # second tile (rows 60-89)
|
||||||
assert img.getpixel((50, 140)) == (0, 0, 255) # third tile, bottom-aligned
|
assert img.getpixel((50, 140)) == (0, 0, 255) # third tile, bottom-aligned
|
||||||
|
|
||||||
def test_wide_page_clamps_last_column(self, tmp_path):
|
def test_wide_page_clamps_last_column(self, tmp_path):
|
||||||
driver = MagicMock(name="driver")
|
driver = MagicMock(name="driver")
|
||||||
@ -99,12 +100,12 @@ class TestChromiumPath:
|
|||||||
"return window.innerHeight": 60,
|
"return window.innerHeight": 60,
|
||||||
}.get(script)
|
}.get(script)
|
||||||
driver.get_screenshot_as_png.side_effect = [
|
driver.get_screenshot_as_png.side_effect = [
|
||||||
png_bytes(100, 60, (255, 0, 0)), # column @ x=0
|
png_bytes(100, 60, (255, 0, 0)), # column @ x=0
|
||||||
png_bytes(100, 60, (0, 0, 255)), # column @ x=100, clamped to paste_x=50
|
png_bytes(100, 60, (0, 0, 255)), # column @ x=100, clamped to paste_x=50
|
||||||
]
|
]
|
||||||
out = tmp_path / "shot.png"
|
out = tmp_path / "shot.png"
|
||||||
assert save_full_page(driver, str(out)) is True
|
assert save_full_page(driver, str(out)) is True
|
||||||
img = Image.open(out)
|
img = Image.open(out)
|
||||||
assert img.size == (150, 60)
|
assert img.size == (150, 60)
|
||||||
assert img.getpixel((25, 30)) == (255, 0, 0) # left column (cols 0-49)
|
assert img.getpixel((25, 30)) == (255, 0, 0) # left column (cols 0-49)
|
||||||
assert img.getpixel((140, 30)) == (0, 0, 255) # right column, clamped (cols 50-149)
|
assert img.getpixel((140, 30)) == (0, 0, 255) # right column, clamped (cols 50-149)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user