Ctrl K
ComunidadGuías…

tests/e2e/test_requests_monitor_e2e.py

Rama dev · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""How much of a show a request takes, driven through the real screens (issues #100 and #154).

Two surfaces, both round-tripped through the API rather than asserted on the DOM alone: the whole
point of the control is the value that reaches a run, so a select that renders and saves nothing
would pass a render-only check.

The Arr profile/folder dropdowns fetch from Sonarr itself, which no fake answers here — so this also
covers the case the layout was restructured for: the monitor choice is Sonarr's own enum, not
something fetched, and must stay usable when that fetch fails.
"""

from __future__ import annotations

import re

import pytest
from playwright.sync_api import Page, expect

from tests.e2e.conftest import ShortlistApp

pytestmark = pytest.mark.e2e

LOAD = 15_000


def _open_row_requests(page: Page) -> None:
    """The default per-person row's editor, at its Requests section (every section is always rendered)."""
    page.goto("/rows")
    expect(page.get_by_role("heading", name="Rows", exact=True)).to_be_visible(timeout=LOAD)
    page.get_by_role("button", name=re.compile(r"^More actions for ")).first.click()
    page.get_by_role("menuitem", name="Edit", exact=True).click()
    expect(page.get_by_role("heading", name="✨ library name Picked for You", exact=True)).to_be_visible(timeout=LOAD)
    expect(page.locator("section#requests")).to_be_visible()


def _connect_sonarr(app: ShortlistApp) -> None:
    """Requests on, with Sonarr's address and key saved — what makes the card render its controls."""
    app.api(
        "PUT",
        "/api/settings",
        json={
            "values": {
                "requests.enabled": True,
                "requests.sonarr.url": "http://sonarr.invalid",
                "requests.sonarr.apikey": "fake-key",
            }
        },
    )


def _seasons(page: Page):
    """The visible season count — the stepper's `<output>`."""
    return page.get_by_label("Number of seasons", exact=True)


def test_the_global_amount_of_a_show_saves_and_survives_a_reload(page: Page, app: ShortlistApp):
    _connect_sonarr(app)
    page.goto("/settings#requests")

    every = page.get_by_label("Every season", exact=True)
    expect(every).to_be_checked(timeout=LOAD)  # the default, and what every add did before this existed

    every.uncheck()
    expect(page.get_by_role("button", name="First seasons", exact=True)).to_have_attribute("aria-pressed", "true")
    expect(page.get_by_text(re.compile("Season 1 only"))).to_be_visible()
    page.get_by_role("button", name="More seasons", exact=True).click()
    expect(_seasons(page)).to_have_text("2")
    expect(page.get_by_text(re.compile("Seasons 1 to 2"))).to_be_visible()

    # Autosave has no button; the stored value is the real wait.
    # The Requests tab keeps its own "Saved" readout beside the form (it has no save bar).
    expect(page.get_by_role("status").get_by_text("Saved", exact=True)).to_be_visible(timeout=LOAD)
    app.wait_for_setting("requests.sonarr.monitor", "firstSeason")
    app.wait_for_setting("requests.season_count", 2)

    page.reload()
    expect(page.get_by_role("button", name="First seasons", exact=True)).to_have_attribute(
        "aria-pressed", "true", timeout=LOAD
    )
    expect(_seasons(page)).to_have_text("2")
    expect(page.get_by_label("Every season", exact=True)).not_to_be_checked()


def test_overseerr_offers_only_what_it_can_send(page: Page, app: ShortlistApp):
    """Overseerr takes a season list and nothing else, so Sonarr's pilot/unmonitored modes are not offered."""
    app.api("PUT", "/api/settings", json={"values": {"requests.enabled": True, "requests.target": "overseerr"}})
    page.goto("/settings#requests")

    expect(page.get_by_label("Every season", exact=True)).to_be_visible(timeout=LOAD)
    expect(page.get_by_role("button", name="First seasons", exact=True)).to_be_visible()
    expect(page.get_by_role("button", name="Last seasons", exact=True)).to_be_visible()
    expect(page.get_by_role("button", name="Pilot episode", exact=True)).to_have_count(0)
    expect(page.get_by_role("button", name="Add unmonitored", exact=True)).to_have_count(0)
    expect(page.get_by_text(re.compile("Monitor New Seasons"))).to_be_visible()


def test_a_row_can_take_less_of_a_show_than_the_global(page: Page, app: ShortlistApp):
    _connect_sonarr(app)
    _open_row_requests(page)

    override = page.get_by_role("button", name="Override How much of a show this row grabs", exact=True)
    expect(override).to_be_visible(timeout=LOAD)
    # While inheriting, the row names the global in the control's own words rather than a value it lacks.
    expect(page.get_by_text(re.compile("Every season"))).to_be_visible()

    override.click()
    section = page.locator("section#requests")
    section.get_by_role("button", name="Last seasons", exact=True).click()
    section.get_by_role("button", name="More seasons", exact=True).click()
    section.get_by_role("button", name="More seasons", exact=True).click()
    expect(_seasons(page)).to_have_text("3")

    page.get_by_role("button", name="Save changes").click()
    app.wait_for_row_field("picked", "req_sonarr_monitor", "lastSeason")
    app.wait_for_row_field("picked", "req_season_count", 3)

    _open_row_requests(page)
    expect(page.get_by_role("button", name="Last seasons", exact=True)).to_have_attribute(
        "aria-pressed", "true", timeout=LOAD
    )
    expect(_seasons(page)).to_have_text("3")


def test_a_row_can_take_only_the_pilot_on_the_sonarr_route(page: Page, app: ShortlistApp):
    _connect_sonarr(app)
    _open_row_requests(page)

    page.get_by_role("button", name="Override How much of a show this row grabs", exact=True).click(timeout=LOAD)
    page.get_by_role("button", name="Pilot episode", exact=True).click()

    page.get_by_role("button", name="Save changes").click()
    app.wait_for_row_field("picked", "req_sonarr_monitor", "pilot")


def test_clearing_the_row_override_puts_it_back_on_the_global(page: Page, app: ShortlistApp):
    """NULL is the inherit signal, so "use the global again" must store null — not the mode that was
    on screen when the box was ticked, which would silently pin the row to today's global."""
    _connect_sonarr(app)
    # Resolve the id rather than assuming the seeded row is 1: `app.api` doesn't raise on a non-2xx,
    # so a 404 here would surface three lines later as a baffling Playwright timeout on a checkbox.
    picked = next(c for c in app.api("GET", "/api/collections").json() if c["slug"] == "picked")
    resp = app.api(
        "PATCH",
        f"/api/collections/{picked['id']}",
        json={"name": picked["name"], "req_sonarr_monitor": "lastSeason"},
    )
    assert resp.status_code == 200, resp.text

    _open_row_requests(page)

    expect(page.get_by_role("button", name="Override How much of a show this row grabs", exact=True)).to_have_count(0)
    page.get_by_role("button", name="Reset How much of a show this row grabs", exact=True).click(timeout=LOAD)

    page.get_by_role("button", name="Save changes").click()
    app.wait_for_row_field("picked", "req_sonarr_monitor", None)
    app.wait_for_row_field("picked", "req_season_count", None)