Files
wordarr/tests/test_ui_dialogs.py
T

127 lines
4.4 KiB
Python

"""Browser checks for the dialogs.
pytest alone cannot catch CSS mistakes: a "#quick-dialog { display: flex }"
overrides the browser's display:none for a closed <dialog> and leaves it stuck
on screen. Skipped when playwright or its browsers are missing.
"""
import socket
import subprocess
import sys
import time
from pathlib import Path
import pytest
sync_api = pytest.importorskip("playwright.sync_api", reason="playwright not installed")
def _free_port() -> int:
with socket.socket() as s:
s.bind(("127.0.0.1", 0))
return s.getsockname()[1]
@pytest.fixture(scope="module")
def server(tmp_path_factory):
tmp = tmp_path_factory.mktemp("ui")
downloads = tmp / "downloads"
(downloads / "Eine Folge").mkdir(parents=True)
for i in (1, 2):
(downloads / "Eine Folge" / f"{i:02d}.mp3").write_bytes(b"")
# one book in three formats, the case the format column exists for
for ext in (".epub", ".azw3", ".pdf"):
(downloads / f"Der Lehrer{ext}").write_bytes(b"x" * 2048)
port = _free_port()
proc = subprocess.Popen(
[sys.executable, "-m", "uvicorn", "wordarr.main:app", "--port", str(port)],
cwd=Path(__file__).resolve().parent.parent,
env={"PATH": "/usr/bin:/bin", "WORDARR_CONFIG_DIR": str(tmp / "config"),
"WORDARR_DOWNLOAD_DIR": str(downloads)},
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
url = f"http://127.0.0.1:{port}"
for _ in range(100):
if proc.poll() is not None:
pytest.skip("uvicorn did not start")
try:
with socket.create_connection(("127.0.0.1", port), timeout=0.2):
break
except OSError:
time.sleep(0.1)
else:
proc.kill()
pytest.skip("server did not come up")
yield url
proc.kill()
@pytest.fixture
def page(server):
with sync_api.sync_playwright() as pw:
try:
browser = pw.chromium.launch()
except Exception as exc: # browsers not downloaded
pytest.skip(f"no chromium: {exc}")
page = browser.new_page(viewport={"width": 1200, "height": 900})
errors = []
page.on("pageerror", lambda e: errors.append(str(e)))
page.goto(server, wait_until="networkidle")
yield page
assert not errors, errors
browser.close()
def test_dialogs_start_closed(page):
for dialog in ("#quick-dialog", "#series-dialog", "#manual-dialog"):
assert not page.locator(dialog).is_visible(), dialog
# the import dialog lives inside the import section, so it can only be seen
# once that tab is open - a reload would not have hidden a broken one
page.click("nav button[data-view=import]")
assert not page.locator("#quick-dialog").is_visible()
def test_hidden_elements_really_are_hidden(page):
"""A CSS display rule can override the hidden attribute."""
page.click("nav button[data-view=import]")
for sel in ("#import-progress", "#import-btn", "#import-table", "#missing-badge"):
assert not page.locator(sel).is_visible(), sel
def test_import_dialog_opens_and_closes(page):
page.click("nav button[data-view=import]")
page.click("#scan-btn")
page.wait_for_selector("[data-quick]")
page.click("[data-quick='0']")
page.wait_for_selector("#quick-dialog[open]")
assert page.locator("#quick-dialog").is_visible()
# everything reachable, nothing cut off at the bottom
for sel in ("#quick-m-submit", "#quick-close"):
box = page.locator(sel).bounding_box()
assert box["y"] + box["height"] <= 900, sel
page.click("#quick-close")
page.wait_for_timeout(150)
assert not page.locator("#quick-dialog").is_visible()
page.click("[data-quick='0']")
page.wait_for_selector("#quick-dialog[open]")
page.keyboard.press("Escape")
page.wait_for_timeout(150)
assert not page.locator("#quick-dialog").is_visible()
def test_format_tags_and_the_format_filter(page):
page.click("nav button[data-view=import]")
page.click("#scan-btn")
page.wait_for_selector("[data-quick]")
tags = page.locator("#import-table .tag.fmt").all_text_contents()
assert "azw3 · 2,0 KB" in tags
assert "mp3" in " ".join(tags) # the audiobook folder is labelled too
page.select_option("#import-filter-format", "epub")
rows = page.locator("#import-table tbody tr")
assert rows.count() == 1
assert "epub" in rows.first.locator(".tag.fmt").text_content()