tighten whitespace, DRY shell and buttons

This commit is contained in:
Abel Luck 2026-03-31 10:04:33 +02:00
parent 0b3b1b2731
commit a88eba7dd1
9 changed files with 439 additions and 225 deletions

View file

@ -7,7 +7,7 @@ from datetime import UTC, datetime, timedelta
from pathlib import Path
from typing import Any, cast
from repub.components import status_badge, toggle_field
from repub.components import action_button, status_badge, toggle_field
from repub.datastar import RefreshBroker, render_sse_event, render_stream
from repub.jobs import load_dashboard_view
from repub.model import (
@ -61,6 +61,52 @@ def test_toggle_field_active_state_utilities_exist_in_built_css() -> None:
assert ".translate-x-5" in css
def test_action_button_adds_cursor_pointer_for_active_buttons() -> None:
markup = str(action_button(label="Run now"))
assert "cursor-pointer" in markup
assert 'type="button"' in markup
def test_action_button_omits_post_handler_when_disabled() -> None:
markup = str(
action_button(
label="Queued",
disabled=True,
post_path="/actions/jobs/7/run-now",
)
)
assert "cursor-not-allowed" in markup
assert "@post(" not in markup
def test_action_button_supports_submit_variant() -> None:
markup = str(
action_button(
label="Save settings",
tone="dark",
button_type="submit",
)
)
assert 'type="submit"' in markup
assert "bg-slate-950" in markup
assert "cursor-pointer" in markup
def test_action_button_supports_datastar_pointerdown_post() -> None:
markup = str(
action_button(
label="Delete",
tone="danger",
post_path="/actions/jobs/7/delete",
)
)
assert 'data-on:pointerdown="@post('/actions/jobs/7/delete')"' in markup
def test_runs_page_renders_completed_execution_end_time_as_relative_hoverable_time() -> (
None
):
@ -140,6 +186,8 @@ def test_root_get_serves_datastar_shim() -> None:
assert "retryMaxCount: Infinity" in body
assert "data-on:online__window=" in body
assert '<main id="morph"' in body
assert "lg:grid-cols-[14rem_minmax(0,1fr)]" in body
assert "lg:px-5 lg:py-4" in body
assert 'href="/sources"' in body
assert 'href="/runs"' in body
assert 'href="/settings"' in body
@ -264,6 +312,8 @@ def test_render_dashboard_shows_dashboard_information_architecture(
assert 'href="/sources"' in body
assert 'href="/runs"' in body
assert "Create source" in body
assert "lg:grid-cols-[14rem_minmax(0,1fr)]" in body
assert "lg:px-5 lg:py-4" in body
asyncio.run(run())
@ -694,6 +744,8 @@ def test_render_settings_shows_current_max_concurrent_jobs(
assert "/actions/settings" in body
assert 'value="3"' in body
assert "Max concurrent jobs" in body
assert 'type="submit"' in body
assert "cursor-pointer" in body
asyncio.run(run())
@ -1110,6 +1162,23 @@ def test_render_runs_shows_running_upcoming_and_completed_tables(
asyncio.run(run())
def test_render_runs_uses_compact_shell_and_table_classes(
monkeypatch, tmp_path: Path
) -> None:
db_path = tmp_path / "runs-compact.db"
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))
async def run() -> None:
app = create_app()
body = str(await render_runs(app))
assert "lg:grid-cols-[14rem_minmax(0,1fr)]" in body
assert "lg:px-5 lg:py-4" in body
assert "min-w-[64rem]" in body
asyncio.run(run())
def test_render_runs_shows_empty_state_rows(monkeypatch, tmp_path: Path) -> None:
db_path = tmp_path / "runs-empty.db"
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))
@ -1231,6 +1300,87 @@ def test_render_runs_shows_cancel_button_for_running_row_with_queued_follow_up(
asyncio.run(run())
def test_render_runs_keeps_all_action_controls_visible_in_html_after_compaction() -> (
None
):
body = str(
runs_page(
running_executions=(
{
"source": "Running source",
"slug": "running-source",
"job_id": 1,
"execution_id": 11,
"started_at": "2026-03-30 12:00 UTC",
"runtime": "running for 10s",
"status": "Running",
"stats": "1 requests • 1 items • 1 byte",
"worker": "streaming stats from worker",
"log_href": "/job/1/execution/11/logs",
"cancel_label": "Stop",
"cancel_post_path": "/actions/executions/11/cancel",
},
),
queued_executions=(
{
"source": "Queued source",
"slug": "queued-source",
"job_id": 2,
"execution_id": 22,
"queued_at": "2 minutes ago",
"queued_at_iso": "2026-03-30T12:28:00+00:00",
"queue_position": 1,
"status": "Queued",
"status_tone": "idle",
"run_label": "Queued",
"run_disabled": True,
"run_post_path": "/actions/jobs/2/run-now",
"cancel_post_path": "/actions/queued-executions/22/cancel",
},
),
upcoming_jobs=(
{
"source": "Scheduled source",
"slug": "scheduled-source",
"job_id": 3,
"next_run": "in 5 minutes",
"next_run_at": "2026-03-30T12:35:00+00:00",
"schedule": "*/5 * * * *",
"enabled_label": "Enabled",
"enabled_tone": "scheduled",
"run_disabled": False,
"run_reason": "Ready",
"toggle_label": "Disable",
"toggle_post_path": "/actions/jobs/3/toggle-enabled",
"run_post_path": "/actions/jobs/3/run-now",
"delete_post_path": "/actions/jobs/3/delete",
},
),
completed_executions=(
{
"source": "Completed source",
"slug": "completed-source",
"job_id": 4,
"execution_id": 44,
"ended_at": "2 minutes ago",
"ended_at_iso": "2026-03-30T12:28:00+00:00",
"status": "Succeeded",
"status_tone": "done",
"stats": "1 requests • 1 items • 1 byte",
"summary": "Worker exited successfully",
"log_href": "/job/4/execution/44/logs",
},
),
)
)
assert ">Stop<" in body
assert ">Cancel<" in body
assert ">Run now<" in body
assert ">Disable<" in body
assert "/job/4/execution/44/logs" in body
def test_cancel_queued_execution_action_deletes_pending_row_without_touching_running_execution(
monkeypatch, tmp_path: Path
) -> None:
@ -1327,6 +1477,24 @@ def test_toggle_job_enabled_action_removes_queued_execution(
asyncio.run(run())
def test_render_create_source_uses_shared_submit_button(
monkeypatch, tmp_path: Path
) -> None:
db_path = tmp_path / "create-source-shared-submit.db"
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))
async def run() -> None:
app = create_app()
body = str(await render_create_source(app))
assert 'type="submit"' in body
assert "Create source" in body
assert "cursor-pointer" in body
assert "bg-slate-950" in body
asyncio.run(run())
def test_render_execution_logs_uses_app_route(monkeypatch, tmp_path: Path) -> None:
db_path = tmp_path / "logs-render.db"
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))